xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/x86/mov_ss_trap.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0 */
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * mov_ss_trap.c: Exercise the bizarre side effects of a watchpoint on MOV SS
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * This does MOV SS from a watchpointed address followed by various
6*4882a593Smuzhiyun  * types of kernel entries.  A MOV SS that hits a watchpoint will queue
7*4882a593Smuzhiyun  * up a #DB trap but will not actually deliver that trap.  The trap
8*4882a593Smuzhiyun  * will be delivered after the next instruction instead.  The CPU's logic
9*4882a593Smuzhiyun  * seems to be:
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  *  - Any fault: drop the pending #DB trap.
12*4882a593Smuzhiyun  *  - INT $N, INT3, INTO, SYSCALL, SYSENTER: enter the kernel and then
13*4882a593Smuzhiyun  *    deliver #DB.
14*4882a593Smuzhiyun  *  - ICEBP: enter the kernel but do not deliver the watchpoint trap
15*4882a593Smuzhiyun  *  - breakpoint: only one #DB is delivered (phew!)
16*4882a593Smuzhiyun  *
17*4882a593Smuzhiyun  * There are plenty of ways for a kernel to handle this incorrectly.  This
18*4882a593Smuzhiyun  * test tries to exercise all the cases.
19*4882a593Smuzhiyun  *
20*4882a593Smuzhiyun  * This should mostly cover CVE-2018-1087 and CVE-2018-8897.
21*4882a593Smuzhiyun  */
22*4882a593Smuzhiyun #define _GNU_SOURCE
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #include <stdlib.h>
25*4882a593Smuzhiyun #include <sys/ptrace.h>
26*4882a593Smuzhiyun #include <sys/types.h>
27*4882a593Smuzhiyun #include <sys/wait.h>
28*4882a593Smuzhiyun #include <sys/user.h>
29*4882a593Smuzhiyun #include <sys/syscall.h>
30*4882a593Smuzhiyun #include <unistd.h>
31*4882a593Smuzhiyun #include <errno.h>
32*4882a593Smuzhiyun #include <stddef.h>
33*4882a593Smuzhiyun #include <stdio.h>
34*4882a593Smuzhiyun #include <err.h>
35*4882a593Smuzhiyun #include <string.h>
36*4882a593Smuzhiyun #include <setjmp.h>
37*4882a593Smuzhiyun #include <sys/prctl.h>
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #define X86_EFLAGS_RF (1UL << 16)
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun #if __x86_64__
42*4882a593Smuzhiyun # define REG_IP REG_RIP
43*4882a593Smuzhiyun #else
44*4882a593Smuzhiyun # define REG_IP REG_EIP
45*4882a593Smuzhiyun #endif
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun unsigned short ss;
48*4882a593Smuzhiyun extern unsigned char breakpoint_insn[];
49*4882a593Smuzhiyun sigjmp_buf jmpbuf;
50*4882a593Smuzhiyun static unsigned char altstack_data[SIGSTKSZ];
51*4882a593Smuzhiyun 
enable_watchpoint(void)52*4882a593Smuzhiyun static void enable_watchpoint(void)
53*4882a593Smuzhiyun {
54*4882a593Smuzhiyun 	pid_t parent = getpid();
55*4882a593Smuzhiyun 	int status;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	pid_t child = fork();
58*4882a593Smuzhiyun 	if (child < 0)
59*4882a593Smuzhiyun 		err(1, "fork");
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	if (child) {
62*4882a593Smuzhiyun 		if (waitpid(child, &status, 0) != child)
63*4882a593Smuzhiyun 			err(1, "waitpid for child");
64*4882a593Smuzhiyun 	} else {
65*4882a593Smuzhiyun 		unsigned long dr0, dr1, dr7;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 		dr0 = (unsigned long)&ss;
68*4882a593Smuzhiyun 		dr1 = (unsigned long)breakpoint_insn;
69*4882a593Smuzhiyun 		dr7 = ((1UL << 1) |	/* G0 */
70*4882a593Smuzhiyun 		       (3UL << 16) |	/* RW0 = read or write */
71*4882a593Smuzhiyun 		       (1UL << 18) |	/* LEN0 = 2 bytes */
72*4882a593Smuzhiyun 		       (1UL << 3));	/* G1, RW1 = insn */
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 		if (ptrace(PTRACE_ATTACH, parent, NULL, NULL) != 0)
75*4882a593Smuzhiyun 			err(1, "PTRACE_ATTACH");
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 		if (waitpid(parent, &status, 0) != parent)
78*4882a593Smuzhiyun 			err(1, "waitpid for child");
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 		if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[0]), dr0) != 0)
81*4882a593Smuzhiyun 			err(1, "PTRACE_POKEUSER DR0");
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 		if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[1]), dr1) != 0)
84*4882a593Smuzhiyun 			err(1, "PTRACE_POKEUSER DR1");
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 		if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[7]), dr7) != 0)
87*4882a593Smuzhiyun 			err(1, "PTRACE_POKEUSER DR7");
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 		printf("\tDR0 = %lx, DR1 = %lx, DR7 = %lx\n", dr0, dr1, dr7);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 		if (ptrace(PTRACE_DETACH, parent, NULL, NULL) != 0)
92*4882a593Smuzhiyun 			err(1, "PTRACE_DETACH");
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 		exit(0);
95*4882a593Smuzhiyun 	}
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun 
sethandler(int sig,void (* handler)(int,siginfo_t *,void *),int flags)98*4882a593Smuzhiyun static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
99*4882a593Smuzhiyun 		       int flags)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	struct sigaction sa;
102*4882a593Smuzhiyun 	memset(&sa, 0, sizeof(sa));
103*4882a593Smuzhiyun 	sa.sa_sigaction = handler;
104*4882a593Smuzhiyun 	sa.sa_flags = SA_SIGINFO | flags;
105*4882a593Smuzhiyun 	sigemptyset(&sa.sa_mask);
106*4882a593Smuzhiyun 	if (sigaction(sig, &sa, 0))
107*4882a593Smuzhiyun 		err(1, "sigaction");
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun static char const * const signames[] = {
111*4882a593Smuzhiyun 	[SIGSEGV] = "SIGSEGV",
112*4882a593Smuzhiyun 	[SIGBUS] = "SIBGUS",
113*4882a593Smuzhiyun 	[SIGTRAP] = "SIGTRAP",
114*4882a593Smuzhiyun 	[SIGILL] = "SIGILL",
115*4882a593Smuzhiyun };
116*4882a593Smuzhiyun 
sigtrap(int sig,siginfo_t * si,void * ctx_void)117*4882a593Smuzhiyun static void sigtrap(int sig, siginfo_t *si, void *ctx_void)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun 	ucontext_t *ctx = ctx_void;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	printf("\tGot SIGTRAP with RIP=%lx, EFLAGS.RF=%d\n",
122*4882a593Smuzhiyun 	       (unsigned long)ctx->uc_mcontext.gregs[REG_IP],
123*4882a593Smuzhiyun 	       !!(ctx->uc_mcontext.gregs[REG_EFL] & X86_EFLAGS_RF));
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun 
handle_and_return(int sig,siginfo_t * si,void * ctx_void)126*4882a593Smuzhiyun static void handle_and_return(int sig, siginfo_t *si, void *ctx_void)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun 	ucontext_t *ctx = ctx_void;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	printf("\tGot %s with RIP=%lx\n", signames[sig],
131*4882a593Smuzhiyun 	       (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun 
handle_and_longjmp(int sig,siginfo_t * si,void * ctx_void)134*4882a593Smuzhiyun static void handle_and_longjmp(int sig, siginfo_t *si, void *ctx_void)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun 	ucontext_t *ctx = ctx_void;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	printf("\tGot %s with RIP=%lx\n", signames[sig],
139*4882a593Smuzhiyun 	       (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	siglongjmp(jmpbuf, 1);
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun 
main()144*4882a593Smuzhiyun int main()
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	unsigned long nr;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	asm volatile ("mov %%ss, %[ss]" : [ss] "=m" (ss));
149*4882a593Smuzhiyun 	printf("\tSS = 0x%hx, &SS = 0x%p\n", ss, &ss);
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0) == 0)
152*4882a593Smuzhiyun 		printf("\tPR_SET_PTRACER_ANY succeeded\n");
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	printf("\tSet up a watchpoint\n");
155*4882a593Smuzhiyun 	sethandler(SIGTRAP, sigtrap, 0);
156*4882a593Smuzhiyun 	enable_watchpoint();
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	printf("[RUN]\tRead from watched memory (should get SIGTRAP)\n");
159*4882a593Smuzhiyun 	asm volatile ("mov %[ss], %[tmp]" : [tmp] "=r" (nr) : [ss] "m" (ss));
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	printf("[RUN]\tMOV SS; INT3\n");
162*4882a593Smuzhiyun 	asm volatile ("mov %[ss], %%ss; int3" :: [ss] "m" (ss));
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	printf("[RUN]\tMOV SS; INT 3\n");
165*4882a593Smuzhiyun 	asm volatile ("mov %[ss], %%ss; .byte 0xcd, 0x3" :: [ss] "m" (ss));
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	printf("[RUN]\tMOV SS; CS CS INT3\n");
168*4882a593Smuzhiyun 	asm volatile ("mov %[ss], %%ss; .byte 0x2e, 0x2e; int3" :: [ss] "m" (ss));
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	printf("[RUN]\tMOV SS; CSx14 INT3\n");
171*4882a593Smuzhiyun 	asm volatile ("mov %[ss], %%ss; .fill 14,1,0x2e; int3" :: [ss] "m" (ss));
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	printf("[RUN]\tMOV SS; INT 4\n");
174*4882a593Smuzhiyun 	sethandler(SIGSEGV, handle_and_return, SA_RESETHAND);
175*4882a593Smuzhiyun 	asm volatile ("mov %[ss], %%ss; int $4" :: [ss] "m" (ss));
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun #ifdef __i386__
178*4882a593Smuzhiyun 	printf("[RUN]\tMOV SS; INTO\n");
179*4882a593Smuzhiyun 	sethandler(SIGSEGV, handle_and_return, SA_RESETHAND);
180*4882a593Smuzhiyun 	nr = -1;
181*4882a593Smuzhiyun 	asm volatile ("add $1, %[tmp]; mov %[ss], %%ss; into"
182*4882a593Smuzhiyun 		      : [tmp] "+r" (nr) : [ss] "m" (ss));
183*4882a593Smuzhiyun #endif
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	if (sigsetjmp(jmpbuf, 1) == 0) {
186*4882a593Smuzhiyun 		printf("[RUN]\tMOV SS; ICEBP\n");
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 		/* Some emulators (e.g. QEMU TCG) don't emulate ICEBP. */
189*4882a593Smuzhiyun 		sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND);
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 		asm volatile ("mov %[ss], %%ss; .byte 0xf1" :: [ss] "m" (ss));
192*4882a593Smuzhiyun 	}
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	if (sigsetjmp(jmpbuf, 1) == 0) {
195*4882a593Smuzhiyun 		printf("[RUN]\tMOV SS; CLI\n");
196*4882a593Smuzhiyun 		sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
197*4882a593Smuzhiyun 		asm volatile ("mov %[ss], %%ss; cli" :: [ss] "m" (ss));
198*4882a593Smuzhiyun 	}
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	if (sigsetjmp(jmpbuf, 1) == 0) {
201*4882a593Smuzhiyun 		printf("[RUN]\tMOV SS; #PF\n");
202*4882a593Smuzhiyun 		sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
203*4882a593Smuzhiyun 		asm volatile ("mov %[ss], %%ss; mov (-1), %[tmp]"
204*4882a593Smuzhiyun 			      : [tmp] "=r" (nr) : [ss] "m" (ss));
205*4882a593Smuzhiyun 	}
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	/*
208*4882a593Smuzhiyun 	 * INT $1: if #DB has DPL=3 and there isn't special handling,
209*4882a593Smuzhiyun 	 * then the kernel will die.
210*4882a593Smuzhiyun 	 */
211*4882a593Smuzhiyun 	if (sigsetjmp(jmpbuf, 1) == 0) {
212*4882a593Smuzhiyun 		printf("[RUN]\tMOV SS; INT 1\n");
213*4882a593Smuzhiyun 		sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
214*4882a593Smuzhiyun 		asm volatile ("mov %[ss], %%ss; int $1" :: [ss] "m" (ss));
215*4882a593Smuzhiyun 	}
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun #ifdef __x86_64__
218*4882a593Smuzhiyun 	/*
219*4882a593Smuzhiyun 	 * In principle, we should test 32-bit SYSCALL as well, but
220*4882a593Smuzhiyun 	 * the calling convention is so unpredictable that it's
221*4882a593Smuzhiyun 	 * not obviously worth the effort.
222*4882a593Smuzhiyun 	 */
223*4882a593Smuzhiyun 	if (sigsetjmp(jmpbuf, 1) == 0) {
224*4882a593Smuzhiyun 		printf("[RUN]\tMOV SS; SYSCALL\n");
225*4882a593Smuzhiyun 		sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND);
226*4882a593Smuzhiyun 		nr = SYS_getpid;
227*4882a593Smuzhiyun 		/*
228*4882a593Smuzhiyun 		 * Toggle the high bit of RSP to make it noncanonical to
229*4882a593Smuzhiyun 		 * strengthen this test on non-SMAP systems.
230*4882a593Smuzhiyun 		 */
231*4882a593Smuzhiyun 		asm volatile ("btc $63, %%rsp\n\t"
232*4882a593Smuzhiyun 			      "mov %[ss], %%ss; syscall\n\t"
233*4882a593Smuzhiyun 			      "btc $63, %%rsp"
234*4882a593Smuzhiyun 			      : "+a" (nr) : [ss] "m" (ss)
235*4882a593Smuzhiyun 			      : "rcx"
236*4882a593Smuzhiyun #ifdef __x86_64__
237*4882a593Smuzhiyun 				, "r11"
238*4882a593Smuzhiyun #endif
239*4882a593Smuzhiyun 			);
240*4882a593Smuzhiyun 	}
241*4882a593Smuzhiyun #endif
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	printf("[RUN]\tMOV SS; breakpointed NOP\n");
244*4882a593Smuzhiyun 	asm volatile ("mov %[ss], %%ss; breakpoint_insn: nop" :: [ss] "m" (ss));
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	/*
247*4882a593Smuzhiyun 	 * Invoking SYSENTER directly breaks all the rules.  Just handle
248*4882a593Smuzhiyun 	 * the SIGSEGV.
249*4882a593Smuzhiyun 	 */
250*4882a593Smuzhiyun 	if (sigsetjmp(jmpbuf, 1) == 0) {
251*4882a593Smuzhiyun 		printf("[RUN]\tMOV SS; SYSENTER\n");
252*4882a593Smuzhiyun 		stack_t stack = {
253*4882a593Smuzhiyun 			.ss_sp = altstack_data,
254*4882a593Smuzhiyun 			.ss_size = SIGSTKSZ,
255*4882a593Smuzhiyun 		};
256*4882a593Smuzhiyun 		if (sigaltstack(&stack, NULL) != 0)
257*4882a593Smuzhiyun 			err(1, "sigaltstack");
258*4882a593Smuzhiyun 		sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND | SA_ONSTACK);
259*4882a593Smuzhiyun 		nr = SYS_getpid;
260*4882a593Smuzhiyun 		/* Clear EBP first to make sure we segfault cleanly. */
261*4882a593Smuzhiyun 		asm volatile ("xorl %%ebp, %%ebp; mov %[ss], %%ss; SYSENTER" : "+a" (nr)
262*4882a593Smuzhiyun 			      : [ss] "m" (ss) : "flags", "rcx"
263*4882a593Smuzhiyun #ifdef __x86_64__
264*4882a593Smuzhiyun 				, "r11"
265*4882a593Smuzhiyun #endif
266*4882a593Smuzhiyun 			);
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 		/* We're unreachable here.  SYSENTER forgets RIP. */
269*4882a593Smuzhiyun 	}
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	if (sigsetjmp(jmpbuf, 1) == 0) {
272*4882a593Smuzhiyun 		printf("[RUN]\tMOV SS; INT $0x80\n");
273*4882a593Smuzhiyun 		sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
274*4882a593Smuzhiyun 		nr = 20;	/* compat getpid */
275*4882a593Smuzhiyun 		asm volatile ("mov %[ss], %%ss; int $0x80"
276*4882a593Smuzhiyun 			      : "+a" (nr) : [ss] "m" (ss)
277*4882a593Smuzhiyun 			      : "flags"
278*4882a593Smuzhiyun #ifdef __x86_64__
279*4882a593Smuzhiyun 				, "r8", "r9", "r10", "r11"
280*4882a593Smuzhiyun #endif
281*4882a593Smuzhiyun 			);
282*4882a593Smuzhiyun 	}
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	printf("[OK]\tI aten't dead\n");
285*4882a593Smuzhiyun 	return 0;
286*4882a593Smuzhiyun }
287