xref: /linux/tools/testing/selftests/x86/int_signal.c (revision 156fa7417fac89fd9dcf3a4ee88785ff90ab6411)
1*96443a53SMatthew Schwartz // SPDX-License-Identifier: GPL-2.0-only
2*96443a53SMatthew Schwartz /* Check the signal context for INT instructions with IDT and FRED entry. */
3*96443a53SMatthew Schwartz #define _GNU_SOURCE
4*96443a53SMatthew Schwartz 
5*96443a53SMatthew Schwartz #include <cpuid.h>
6*96443a53SMatthew Schwartz #include <errno.h>
7*96443a53SMatthew Schwartz #include <stdbool.h>
8*96443a53SMatthew Schwartz #include <stddef.h>
9*96443a53SMatthew Schwartz #include <stdint.h>
10*96443a53SMatthew Schwartz #include <sys/ptrace.h>
11*96443a53SMatthew Schwartz #include <sys/user.h>
12*96443a53SMatthew Schwartz #include <sys/wait.h>
13*96443a53SMatthew Schwartz #include <unistd.h>
14*96443a53SMatthew Schwartz #include <ucontext.h>
15*96443a53SMatthew Schwartz 
16*96443a53SMatthew Schwartz #include "helpers.h"
17*96443a53SMatthew Schwartz 
18*96443a53SMatthew Schwartz #ifdef __x86_64__
19*96443a53SMatthew Schwartz #define REG_IP REG_RIP
20*96443a53SMatthew Schwartz #define USER_IP rip
21*96443a53SMatthew Schwartz #define STACK_PTR "%rsp"
22*96443a53SMatthew Schwartz #else
23*96443a53SMatthew Schwartz #define REG_IP REG_EIP
24*96443a53SMatthew Schwartz #define USER_IP eip
25*96443a53SMatthew Schwartz #define STACK_PTR "%esp"
26*96443a53SMatthew Schwartz #endif
27*96443a53SMatthew Schwartz 
28*96443a53SMatthew Schwartz /*
29*96443a53SMatthew Schwartz  * Each instruction has normal and single-step entry points. Resume at the
30*96443a53SMatthew Schwartz  * NOP after handling its signal, then expect a trace trap after that NOP
31*96443a53SMatthew Schwartz  * when TF is set. Explicit labels avoid assuming the kernel's saved IP.
32*96443a53SMatthew Schwartz  */
33*96443a53SMatthew Schwartz #define PROBE(name, insn) \
34*96443a53SMatthew Schwartz 	extern void name(void); \
35*96443a53SMatthew Schwartz 	extern void name##_tf(void); \
36*96443a53SMatthew Schwartz 	extern const char name##_end[], name##_step[]; \
37*96443a53SMatthew Schwartz 	asm(".pushsection .text\n" \
38*96443a53SMatthew Schwartz 	    ".globl " #name "_tf\n" \
39*96443a53SMatthew Schwartz 	    ".type " #name "_tf, @function\n" \
40*96443a53SMatthew Schwartz 	    #name "_tf:\n" \
41*96443a53SMatthew Schwartz 	    "pushf\n" \
42*96443a53SMatthew Schwartz 	    "orl $0x100, (" STACK_PTR ")\n" \
43*96443a53SMatthew Schwartz 	    "popf\n" \
44*96443a53SMatthew Schwartz 	    ".globl " #name "\n" \
45*96443a53SMatthew Schwartz 	    ".type " #name ", @function\n" \
46*96443a53SMatthew Schwartz 	    #name ":\n" insn "\n" \
47*96443a53SMatthew Schwartz 	    ".globl " #name "_end\n" \
48*96443a53SMatthew Schwartz 	    #name "_end:\nnop\n" \
49*96443a53SMatthew Schwartz 	    ".globl " #name "_step\n" \
50*96443a53SMatthew Schwartz 	    #name "_step:\nret\n" \
51*96443a53SMatthew Schwartz 	    ".size " #name ", .-" #name "\n" \
52*96443a53SMatthew Schwartz 	    ".size " #name "_tf, .-" #name "_tf\n" \
53*96443a53SMatthew Schwartz 	    ".popsection\n")
54*96443a53SMatthew Schwartz 
55*96443a53SMatthew Schwartz PROBE(int1, ".byte 0xcd, 0x01");
56*96443a53SMatthew Schwartz PROBE(int29, ".byte 0xcd, 0x29");
57*96443a53SMatthew Schwartz PROBE(int2c, ".byte 0xcd, 0x2c");
58*96443a53SMatthew Schwartz PROBE(int2d, ".byte 0xcd, 0x2d");
59*96443a53SMatthew Schwartz PROBE(prefixed_int2d, ".byte 0x66, 0xcd, 0x2d");
60*96443a53SMatthew Schwartz PROBE(long_int2d, ".fill 13, 1, 0x2e\n.byte 0xcd, 0x2d");
61*96443a53SMatthew Schwartz PROBE(int81, ".byte 0xcd, 0x81");
62*96443a53SMatthew Schwartz PROBE(intff, ".byte 0xcd, 0xff");
63*96443a53SMatthew Schwartz PROBE(short_int3, ".byte 0xcc");
64*96443a53SMatthew Schwartz PROBE(long_int3, ".byte 0xcd, 0x03");
65*96443a53SMatthew Schwartz PROBE(int4, ".byte 0xcd, 0x04");
66*96443a53SMatthew Schwartz PROBE(ud2, ".byte 0x0f, 0x0b");
67*96443a53SMatthew Schwartz PROBE(hlt, ".byte 0xf4");
68*96443a53SMatthew Schwartz 
69*96443a53SMatthew Schwartz struct test {
70*96443a53SMatthew Schwartz 	const char *name;
71*96443a53SMatthew Schwartz 	void (*run)(void);
72*96443a53SMatthew Schwartz 	void (*run_tf)(void);
73*96443a53SMatthew Schwartz 	const char *end, *step;
74*96443a53SMatthew Schwartz 	int signo, trap, error, ip_offset, flags, code;
75*96443a53SMatthew Schwartz };
76*96443a53SMatthew Schwartz 
77*96443a53SMatthew Schwartz #define TEST(name, sig, trap, error, offset, flags, code) \
78*96443a53SMatthew Schwartz 	{ #name, name, name##_tf, name##_end, name##_step, \
79*96443a53SMatthew Schwartz 	  sig, trap, error, offset, flags, code }
80*96443a53SMatthew Schwartz 
81*96443a53SMatthew Schwartz #define GP(name, error) \
82*96443a53SMatthew Schwartz 	TEST(name, SIGSEGV, 13, error, 0, X86_EFLAGS_RF, SI_KERNEL)
83*96443a53SMatthew Schwartz 
84*96443a53SMatthew Schwartz static const struct test tests[] = {
85*96443a53SMatthew Schwartz 	GP(int1, 0x00a),
86*96443a53SMatthew Schwartz 	GP(int29, 0x14a),
87*96443a53SMatthew Schwartz 	GP(int2c, 0x162),
88*96443a53SMatthew Schwartz 	GP(int2d, 0x16a),
89*96443a53SMatthew Schwartz 	GP(prefixed_int2d, 0x16a),
90*96443a53SMatthew Schwartz 	GP(long_int2d, 0x16a),
91*96443a53SMatthew Schwartz 	GP(int81, 0x40a),
92*96443a53SMatthew Schwartz 	GP(intff, 0x7fa),
93*96443a53SMatthew Schwartz 	GP(hlt, 0),
94*96443a53SMatthew Schwartz 	TEST(short_int3, SIGTRAP, 3, 0, 1, 0, SI_KERNEL),
95*96443a53SMatthew Schwartz 	TEST(long_int3, SIGTRAP, 3, 0, 2, 0, SI_KERNEL),
96*96443a53SMatthew Schwartz 	TEST(int4, SIGSEGV, 4, 0, 2, 0, SI_KERNEL),
97*96443a53SMatthew Schwartz 	TEST(ud2, SIGILL, 6, 0, 0, X86_EFLAGS_RF, ILL_ILLOPN),
98*96443a53SMatthew Schwartz };
99*96443a53SMatthew Schwartz 
100*96443a53SMatthew Schwartz static const struct test *active;
101*96443a53SMatthew Schwartz static volatile sig_atomic_t seen, signo, trap, error, ip_offset, flags;
102*96443a53SMatthew Schwartz static volatile sig_atomic_t code, addr_ok, single_step, stepped, step_ok;
103*96443a53SMatthew Schwartz 
handler(int sig,siginfo_t * info,void * context)104*96443a53SMatthew Schwartz static void handler(int sig, siginfo_t *info, void *context)
105*96443a53SMatthew Schwartz {
106*96443a53SMatthew Schwartz 	ucontext_t *uc = context;
107*96443a53SMatthew Schwartz 	uintptr_t ip = uc->uc_mcontext.gregs[REG_IP];
108*96443a53SMatthew Schwartz 	uintptr_t start = (uintptr_t)active->run;
109*96443a53SMatthew Schwartz 	uintptr_t end = (uintptr_t)active->end;
110*96443a53SMatthew Schwartz 
111*96443a53SMatthew Schwartz 	if (seen && single_step && sig == SIGTRAP) {
112*96443a53SMatthew Schwartz 		if (stepped++) {
113*96443a53SMatthew Schwartz 			ksft_print_msg("%s: second trace trap at %#lx\n",
114*96443a53SMatthew Schwartz 				       active->name, (unsigned long)ip);
115*96443a53SMatthew Schwartz 			_exit(KSFT_FAIL);
116*96443a53SMatthew Schwartz 		}
117*96443a53SMatthew Schwartz 		step_ok = ip == (uintptr_t)active->step &&
118*96443a53SMatthew Schwartz 			uc->uc_mcontext.gregs[REG_TRAPNO] == 1 &&
119*96443a53SMatthew Schwartz 			info->si_code == TRAP_TRACE;
120*96443a53SMatthew Schwartz 		uc->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF;
121*96443a53SMatthew Schwartz 		return;
122*96443a53SMatthew Schwartz 	}
123*96443a53SMatthew Schwartz 
124*96443a53SMatthew Schwartz 	if (seen || ip < start || ip > end) {
125*96443a53SMatthew Schwartz 		ksft_print_msg("%s: unexpected signal %d at %#lx\n",
126*96443a53SMatthew Schwartz 			       active->name, sig, (unsigned long)ip);
127*96443a53SMatthew Schwartz 		_exit(KSFT_FAIL);
128*96443a53SMatthew Schwartz 	}
129*96443a53SMatthew Schwartz 
130*96443a53SMatthew Schwartz 	signo = sig;
131*96443a53SMatthew Schwartz 	trap = uc->uc_mcontext.gregs[REG_TRAPNO];
132*96443a53SMatthew Schwartz 	error = uc->uc_mcontext.gregs[REG_ERR];
133*96443a53SMatthew Schwartz 	ip_offset = ip - start;
134*96443a53SMatthew Schwartz 	flags = uc->uc_mcontext.gregs[REG_EFL] & (X86_EFLAGS_RF | X86_EFLAGS_TF);
135*96443a53SMatthew Schwartz 	code = info->si_code;
136*96443a53SMatthew Schwartz 	/* force_sig() reports no address, force_sig_fault() reports the IP. */
137*96443a53SMatthew Schwartz 	addr_ok = info->si_addr == (code == SI_KERNEL ? NULL : (void *)ip);
138*96443a53SMatthew Schwartz 	seen = 1;
139*96443a53SMatthew Schwartz 	uc->uc_mcontext.gregs[REG_IP] = end;
140*96443a53SMatthew Schwartz }
141*96443a53SMatthew Schwartz 
wait_for_child(pid_t child,int * status)142*96443a53SMatthew Schwartz static void wait_for_child(pid_t child, int *status)
143*96443a53SMatthew Schwartz {
144*96443a53SMatthew Schwartz 	pid_t ret;
145*96443a53SMatthew Schwartz 
146*96443a53SMatthew Schwartz 	do {
147*96443a53SMatthew Schwartz 		ret = waitpid(child, status, 0);
148*96443a53SMatthew Schwartz 	} while (ret < 0 && errno == EINTR);
149*96443a53SMatthew Schwartz 	if (ret != child)
150*96443a53SMatthew Schwartz 		ksft_exit_fail_perror("waitpid");
151*96443a53SMatthew Schwartz }
152*96443a53SMatthew Schwartz 
153*96443a53SMatthew Schwartz /* Resume the tracee and check where the next stop lands. */
resume_to(pid_t child,int * status,int request,int sig,const void * ip,const char * what)154*96443a53SMatthew Schwartz static bool resume_to(pid_t child, int *status, int request, int sig,
155*96443a53SMatthew Schwartz 		      const void *ip, const char *what)
156*96443a53SMatthew Schwartz {
157*96443a53SMatthew Schwartz 	struct user_regs_struct regs;
158*96443a53SMatthew Schwartz 
159*96443a53SMatthew Schwartz 	if (ptrace(request, child, 0, 0))
160*96443a53SMatthew Schwartz 		return false;
161*96443a53SMatthew Schwartz 	wait_for_child(child, status);
162*96443a53SMatthew Schwartz 	if (!WIFSTOPPED(*status)) {
163*96443a53SMatthew Schwartz 		ksft_print_msg("%s: tracee did not stop\n", what);
164*96443a53SMatthew Schwartz 		return false;
165*96443a53SMatthew Schwartz 	}
166*96443a53SMatthew Schwartz 	if (WSTOPSIG(*status) != sig) {
167*96443a53SMatthew Schwartz 		ksft_print_msg("%s: stopped with signal %d, expected %d\n",
168*96443a53SMatthew Schwartz 			       what, WSTOPSIG(*status), sig);
169*96443a53SMatthew Schwartz 		return false;
170*96443a53SMatthew Schwartz 	}
171*96443a53SMatthew Schwartz 	if (ptrace(PTRACE_GETREGS, child, 0, &regs))
172*96443a53SMatthew Schwartz 		return false;
173*96443a53SMatthew Schwartz 	if ((unsigned long)regs.USER_IP != (unsigned long)ip) {
174*96443a53SMatthew Schwartz 		ksft_print_msg("%s: stopped at %#lx, expected %#lx\n", what,
175*96443a53SMatthew Schwartz 			       (unsigned long)regs.USER_IP, (unsigned long)ip);
176*96443a53SMatthew Schwartz 		return false;
177*96443a53SMatthew Schwartz 	}
178*96443a53SMatthew Schwartz 	return true;
179*96443a53SMatthew Schwartz }
180*96443a53SMatthew Schwartz 
set_ip(pid_t child,const void * ip,bool tf)181*96443a53SMatthew Schwartz static bool set_ip(pid_t child, const void *ip, bool tf)
182*96443a53SMatthew Schwartz {
183*96443a53SMatthew Schwartz 	struct user_regs_struct regs;
184*96443a53SMatthew Schwartz 
185*96443a53SMatthew Schwartz 	if (ptrace(PTRACE_GETREGS, child, 0, &regs))
186*96443a53SMatthew Schwartz 		return false;
187*96443a53SMatthew Schwartz 	regs.USER_IP = (unsigned long)ip;
188*96443a53SMatthew Schwartz 	if (tf)
189*96443a53SMatthew Schwartz 		regs.eflags |= X86_EFLAGS_TF;
190*96443a53SMatthew Schwartz 	return !ptrace(PTRACE_SETREGS, child, 0, &regs);
191*96443a53SMatthew Schwartz }
192*96443a53SMatthew Schwartz 
193*96443a53SMatthew Schwartz /*
194*96443a53SMatthew Schwartz  * Exercise the tracer paths that resume through the fault frame rather than
195*96443a53SMatthew Schwartz  * sigreturn. A stale FRED software event flag on that frame traps before the
196*96443a53SMatthew Schwartz  * NOP executes instead of after it.
197*96443a53SMatthew Schwartz  */
test_ptrace(void)198*96443a53SMatthew Schwartz static void test_ptrace(void)
199*96443a53SMatthew Schwartz {
200*96443a53SMatthew Schwartz 	bool into = false, step = false, cont = false;
201*96443a53SMatthew Schwartz 	pid_t child;
202*96443a53SMatthew Schwartz 	int status;
203*96443a53SMatthew Schwartz 
204*96443a53SMatthew Schwartz 	child = fork();
205*96443a53SMatthew Schwartz 	if (child < 0)
206*96443a53SMatthew Schwartz 		ksft_exit_fail_perror("fork");
207*96443a53SMatthew Schwartz 	if (!child) {
208*96443a53SMatthew Schwartz 		if (ptrace(PTRACE_TRACEME, 0, 0, 0))
209*96443a53SMatthew Schwartz 			_exit(KSFT_FAIL);
210*96443a53SMatthew Schwartz 		/* Start from a breakpoint frame, not the syscall frame of raise(). */
211*96443a53SMatthew Schwartz 		asm volatile("int3");
212*96443a53SMatthew Schwartz 		_exit(KSFT_FAIL);
213*96443a53SMatthew Schwartz 	}
214*96443a53SMatthew Schwartz 
215*96443a53SMatthew Schwartz 	wait_for_child(child, &status);
216*96443a53SMatthew Schwartz 	if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP)
217*96443a53SMatthew Schwartz 		goto out;
218*96443a53SMatthew Schwartz 	if (ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_EXITKILL))
219*96443a53SMatthew Schwartz 		goto out;
220*96443a53SMatthew Schwartz 
221*96443a53SMatthew Schwartz 	/* Single-step into the INT. The fault must report the INT's address. */
222*96443a53SMatthew Schwartz 	if (!set_ip(child, int2d, false))
223*96443a53SMatthew Schwartz 		goto out;
224*96443a53SMatthew Schwartz 	into = resume_to(child, &status, PTRACE_SINGLESTEP, SIGSEGV, int2d,
225*96443a53SMatthew Schwartz 			 "single-step into INT");
226*96443a53SMatthew Schwartz 	if (!into)
227*96443a53SMatthew Schwartz 		goto out;
228*96443a53SMatthew Schwartz 
229*96443a53SMatthew Schwartz 	/* Suppress SIGSEGV and single-step the NOP. */
230*96443a53SMatthew Schwartz 	if (!set_ip(child, int2d_end, false))
231*96443a53SMatthew Schwartz 		goto out;
232*96443a53SMatthew Schwartz 	step = resume_to(child, &status, PTRACE_SINGLESTEP, SIGTRAP, int2d_step,
233*96443a53SMatthew Schwartz 			 "single-step after INT");
234*96443a53SMatthew Schwartz 	if (!step)
235*96443a53SMatthew Schwartz 		goto out;
236*96443a53SMatthew Schwartz 
237*96443a53SMatthew Schwartz 	/* Fault again, then suppress SIGSEGV and continue with TF set. */
238*96443a53SMatthew Schwartz 	if (!set_ip(child, int2d, false))
239*96443a53SMatthew Schwartz 		goto out;
240*96443a53SMatthew Schwartz 	if (!resume_to(child, &status, PTRACE_CONT, SIGSEGV, int2d,
241*96443a53SMatthew Schwartz 		       "continue to INT"))
242*96443a53SMatthew Schwartz 		goto out;
243*96443a53SMatthew Schwartz 	if (!set_ip(child, int2d_end, true))
244*96443a53SMatthew Schwartz 		goto out;
245*96443a53SMatthew Schwartz 	cont = resume_to(child, &status, PTRACE_CONT, SIGTRAP, int2d_step,
246*96443a53SMatthew Schwartz 			 "continue with TF after INT");
247*96443a53SMatthew Schwartz out:
248*96443a53SMatthew Schwartz 	if (WIFSTOPPED(status)) {
249*96443a53SMatthew Schwartz 		kill(child, SIGKILL);
250*96443a53SMatthew Schwartz 		wait_for_child(child, &status);
251*96443a53SMatthew Schwartz 	}
252*96443a53SMatthew Schwartz 	ksft_test_result(into, "ptrace single-step into INT faults at the INT\n");
253*96443a53SMatthew Schwartz 	ksft_test_result(step, "ptrace single-step after suppressing SIGSEGV\n");
254*96443a53SMatthew Schwartz 	ksft_test_result(cont, "ptrace continue with TF after suppressing SIGSEGV\n");
255*96443a53SMatthew Schwartz }
256*96443a53SMatthew Schwartz 
cpu_has_fred(void)257*96443a53SMatthew Schwartz static bool cpu_has_fred(void)
258*96443a53SMatthew Schwartz {
259*96443a53SMatthew Schwartz 	unsigned int eax, ebx, ecx, edx;
260*96443a53SMatthew Schwartz 
261*96443a53SMatthew Schwartz 	if (__get_cpuid_max(0, NULL) < 7)
262*96443a53SMatthew Schwartz 		return false;
263*96443a53SMatthew Schwartz 	__cpuid_count(7, 1, eax, ebx, ecx, edx);
264*96443a53SMatthew Schwartz 	return eax & (1 << 17);
265*96443a53SMatthew Schwartz }
266*96443a53SMatthew Schwartz 
main(void)267*96443a53SMatthew Schwartz int main(void)
268*96443a53SMatthew Schwartz {
269*96443a53SMatthew Schwartz 	unsigned int i, tf;
270*96443a53SMatthew Schwartz 	int expected_flags, ok;
271*96443a53SMatthew Schwartz 
272*96443a53SMatthew Schwartz 	ksft_print_header();
273*96443a53SMatthew Schwartz 	ksft_set_plan(2 * ARRAY_SIZE(tests) + 3);
274*96443a53SMatthew Schwartz 	ksft_print_msg("CPU %s FRED\n", cpu_has_fred() ? "supports" : "lacks");
275*96443a53SMatthew Schwartz 	sethandler(SIGSEGV, handler, 0);
276*96443a53SMatthew Schwartz 	sethandler(SIGTRAP, handler, 0);
277*96443a53SMatthew Schwartz 	sethandler(SIGILL, handler, 0);
278*96443a53SMatthew Schwartz 
279*96443a53SMatthew Schwartz 	for (tf = 0; tf < 2; tf++) {
280*96443a53SMatthew Schwartz 		for (i = 0; i < ARRAY_SIZE(tests); i++) {
281*96443a53SMatthew Schwartz 			active = &tests[i];
282*96443a53SMatthew Schwartz 			single_step = tf;
283*96443a53SMatthew Schwartz 			seen = signo = trap = error = ip_offset = flags = 0;
284*96443a53SMatthew Schwartz 			code = addr_ok = stepped = step_ok = 0;
285*96443a53SMatthew Schwartz 			expected_flags = active->flags | (tf ? X86_EFLAGS_TF : 0);
286*96443a53SMatthew Schwartz 			if (tf)
287*96443a53SMatthew Schwartz 				active->run_tf();
288*96443a53SMatthew Schwartz 			else
289*96443a53SMatthew Schwartz 				active->run();
290*96443a53SMatthew Schwartz 
291*96443a53SMatthew Schwartz 			ok = seen && signo == active->signo && trap == active->trap &&
292*96443a53SMatthew Schwartz 				error == active->error && ip_offset == active->ip_offset &&
293*96443a53SMatthew Schwartz 				flags == expected_flags && code == active->code && addr_ok &&
294*96443a53SMatthew Schwartz 				(!tf || (stepped && step_ok));
295*96443a53SMatthew Schwartz 			ksft_test_result(ok, "%s%s\n", active->name, tf ? " with TF" : "");
296*96443a53SMatthew Schwartz 			if (!ok) {
297*96443a53SMatthew Schwartz 				ksft_print_msg("got signal=%d trap=%d error=%#x ip=%d\n",
298*96443a53SMatthew Schwartz 					       signo, trap, error, ip_offset);
299*96443a53SMatthew Schwartz 				ksft_print_msg("got flags=%#x code=%d addr_ok=%d step_ok=%d\n",
300*96443a53SMatthew Schwartz 					       flags, code, addr_ok, step_ok);
301*96443a53SMatthew Schwartz 				ksft_print_msg("expected signal=%d trap=%d error=%#x ip=%d\n",
302*96443a53SMatthew Schwartz 					       active->signo, active->trap, active->error,
303*96443a53SMatthew Schwartz 					       active->ip_offset);
304*96443a53SMatthew Schwartz 				ksft_print_msg("expected flags=%#x code=%d\n",
305*96443a53SMatthew Schwartz 					       expected_flags, active->code);
306*96443a53SMatthew Schwartz 			}
307*96443a53SMatthew Schwartz 		}
308*96443a53SMatthew Schwartz 	}
309*96443a53SMatthew Schwartz 	test_ptrace();
310*96443a53SMatthew Schwartz 	ksft_finished();
311*96443a53SMatthew Schwartz }
312