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