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