xref: /linux/tools/testing/selftests/x86/sigtrap_loop.c (revision 9afe652958c3ee88f24df1e4a97f298afce89407)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2025 Intel Corporation
4  */
5 #define _GNU_SOURCE
6 
7 #include <err.h>
8 #include <signal.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/ucontext.h>
13 
14 #ifdef __x86_64__
15 # define REG_IP REG_RIP
16 #else
17 # define REG_IP REG_EIP
18 #endif
19 
sethandler(int sig,void (* handler)(int,siginfo_t *,void *),int flags)20 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), int flags)
21 {
22 	struct sigaction sa;
23 
24 	memset(&sa, 0, sizeof(sa));
25 	sa.sa_sigaction = handler;
26 	sa.sa_flags = SA_SIGINFO | flags;
27 	sigemptyset(&sa.sa_mask);
28 
29 	if (sigaction(sig, &sa, 0))
30 		err(1, "sigaction");
31 
32 	return;
33 }
34 
sigtrap(int sig,siginfo_t * info,void * ctx_void)35 static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
36 {
37 	ucontext_t *ctx = (ucontext_t *)ctx_void;
38 	static unsigned int loop_count_on_same_ip;
39 	static unsigned long last_trap_ip;
40 
41 	if (last_trap_ip == ctx->uc_mcontext.gregs[REG_IP]) {
42 		printf("\tTrapped at %016lx\n", last_trap_ip);
43 
44 		/*
45 		 * If the same IP is hit more than 10 times in a row, it is
46 		 * _considered_ an infinite loop.
47 		 */
48 		if (++loop_count_on_same_ip > 10) {
49 			printf("[FAIL]\tDetected SIGTRAP infinite loop\n");
50 			exit(1);
51 		}
52 
53 		return;
54 	}
55 
56 	loop_count_on_same_ip = 0;
57 	last_trap_ip = ctx->uc_mcontext.gregs[REG_IP];
58 	printf("\tTrapped at %016lx\n", last_trap_ip);
59 }
60 
main(int argc,char * argv[])61 int main(int argc, char *argv[])
62 {
63 	sethandler(SIGTRAP, sigtrap, 0);
64 
65 	/*
66 	 * Set the Trap Flag (TF) to single-step the test code, therefore to
67 	 * trigger a SIGTRAP signal after each instruction until the TF is
68 	 * cleared.
69 	 *
70 	 * Because the arithmetic flags are not significant here, the TF is
71 	 * set by pushing 0x302 onto the stack and then popping it into the
72 	 * flags register.
73 	 *
74 	 * Four instructions in the following asm code are executed with the
75 	 * TF set, thus the SIGTRAP handler is expected to run four times.
76 	 */
77 	printf("[RUN]\tSIGTRAP infinite loop detection\n");
78 	asm volatile(
79 #ifdef __x86_64__
80 		/*
81 		 * Avoid clobbering the redzone
82 		 *
83 		 * Equivalent to "sub $128, %rsp", however -128 can be encoded
84 		 * in a single byte immediate while 128 uses 4 bytes.
85 		 */
86 		"add $-128, %rsp\n\t"
87 #endif
88 		"push $0x302\n\t"
89 		"popf\n\t"
90 		"nop\n\t"
91 		"nop\n\t"
92 		"push $0x202\n\t"
93 		"popf\n\t"
94 #ifdef __x86_64__
95 		"sub $-128, %rsp\n\t"
96 #endif
97 	);
98 
99 	printf("[OK]\tNo SIGTRAP infinite loop detected\n");
100 	return 0;
101 }
102