162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * mov_ss_trap.c: Exercise the bizarre side effects of a watchpoint on MOV SS 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This does MOV SS from a watchpointed address followed by various 662306a36Sopenharmony_ci * types of kernel entries. A MOV SS that hits a watchpoint will queue 762306a36Sopenharmony_ci * up a #DB trap but will not actually deliver that trap. The trap 862306a36Sopenharmony_ci * will be delivered after the next instruction instead. The CPU's logic 962306a36Sopenharmony_ci * seems to be: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * - Any fault: drop the pending #DB trap. 1262306a36Sopenharmony_ci * - INT $N, INT3, INTO, SYSCALL, SYSENTER: enter the kernel and then 1362306a36Sopenharmony_ci * deliver #DB. 1462306a36Sopenharmony_ci * - ICEBP: enter the kernel but do not deliver the watchpoint trap 1562306a36Sopenharmony_ci * - breakpoint: only one #DB is delivered (phew!) 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * There are plenty of ways for a kernel to handle this incorrectly. This 1862306a36Sopenharmony_ci * test tries to exercise all the cases. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * This should mostly cover CVE-2018-1087 and CVE-2018-8897. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci#define _GNU_SOURCE 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <stdlib.h> 2562306a36Sopenharmony_ci#include <sys/ptrace.h> 2662306a36Sopenharmony_ci#include <sys/types.h> 2762306a36Sopenharmony_ci#include <sys/wait.h> 2862306a36Sopenharmony_ci#include <sys/user.h> 2962306a36Sopenharmony_ci#include <sys/syscall.h> 3062306a36Sopenharmony_ci#include <unistd.h> 3162306a36Sopenharmony_ci#include <errno.h> 3262306a36Sopenharmony_ci#include <stddef.h> 3362306a36Sopenharmony_ci#include <stdio.h> 3462306a36Sopenharmony_ci#include <err.h> 3562306a36Sopenharmony_ci#include <string.h> 3662306a36Sopenharmony_ci#include <setjmp.h> 3762306a36Sopenharmony_ci#include <sys/prctl.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define X86_EFLAGS_RF (1UL << 16) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#if __x86_64__ 4262306a36Sopenharmony_ci# define REG_IP REG_RIP 4362306a36Sopenharmony_ci#else 4462306a36Sopenharmony_ci# define REG_IP REG_EIP 4562306a36Sopenharmony_ci#endif 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciunsigned short ss; 4862306a36Sopenharmony_ciextern unsigned char breakpoint_insn[]; 4962306a36Sopenharmony_cisigjmp_buf jmpbuf; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void enable_watchpoint(void) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci pid_t parent = getpid(); 5462306a36Sopenharmony_ci int status; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci pid_t child = fork(); 5762306a36Sopenharmony_ci if (child < 0) 5862306a36Sopenharmony_ci err(1, "fork"); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (child) { 6162306a36Sopenharmony_ci if (waitpid(child, &status, 0) != child) 6262306a36Sopenharmony_ci err(1, "waitpid for child"); 6362306a36Sopenharmony_ci } else { 6462306a36Sopenharmony_ci unsigned long dr0, dr1, dr7; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci dr0 = (unsigned long)&ss; 6762306a36Sopenharmony_ci dr1 = (unsigned long)breakpoint_insn; 6862306a36Sopenharmony_ci dr7 = ((1UL << 1) | /* G0 */ 6962306a36Sopenharmony_ci (3UL << 16) | /* RW0 = read or write */ 7062306a36Sopenharmony_ci (1UL << 18) | /* LEN0 = 2 bytes */ 7162306a36Sopenharmony_ci (1UL << 3)); /* G1, RW1 = insn */ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (ptrace(PTRACE_ATTACH, parent, NULL, NULL) != 0) 7462306a36Sopenharmony_ci err(1, "PTRACE_ATTACH"); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (waitpid(parent, &status, 0) != parent) 7762306a36Sopenharmony_ci err(1, "waitpid for child"); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[0]), dr0) != 0) 8062306a36Sopenharmony_ci err(1, "PTRACE_POKEUSER DR0"); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[1]), dr1) != 0) 8362306a36Sopenharmony_ci err(1, "PTRACE_POKEUSER DR1"); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[7]), dr7) != 0) 8662306a36Sopenharmony_ci err(1, "PTRACE_POKEUSER DR7"); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci printf("\tDR0 = %lx, DR1 = %lx, DR7 = %lx\n", dr0, dr1, dr7); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (ptrace(PTRACE_DETACH, parent, NULL, NULL) != 0) 9162306a36Sopenharmony_ci err(1, "PTRACE_DETACH"); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci exit(0); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 9862306a36Sopenharmony_ci int flags) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct sigaction sa; 10162306a36Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 10262306a36Sopenharmony_ci sa.sa_sigaction = handler; 10362306a36Sopenharmony_ci sa.sa_flags = SA_SIGINFO | flags; 10462306a36Sopenharmony_ci sigemptyset(&sa.sa_mask); 10562306a36Sopenharmony_ci if (sigaction(sig, &sa, 0)) 10662306a36Sopenharmony_ci err(1, "sigaction"); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic char const * const signames[] = { 11062306a36Sopenharmony_ci [SIGSEGV] = "SIGSEGV", 11162306a36Sopenharmony_ci [SIGBUS] = "SIBGUS", 11262306a36Sopenharmony_ci [SIGTRAP] = "SIGTRAP", 11362306a36Sopenharmony_ci [SIGILL] = "SIGILL", 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void sigtrap(int sig, siginfo_t *si, void *ctx_void) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci ucontext_t *ctx = ctx_void; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci printf("\tGot SIGTRAP with RIP=%lx, EFLAGS.RF=%d\n", 12162306a36Sopenharmony_ci (unsigned long)ctx->uc_mcontext.gregs[REG_IP], 12262306a36Sopenharmony_ci !!(ctx->uc_mcontext.gregs[REG_EFL] & X86_EFLAGS_RF)); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void handle_and_return(int sig, siginfo_t *si, void *ctx_void) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci ucontext_t *ctx = ctx_void; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci printf("\tGot %s with RIP=%lx\n", signames[sig], 13062306a36Sopenharmony_ci (unsigned long)ctx->uc_mcontext.gregs[REG_IP]); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void handle_and_longjmp(int sig, siginfo_t *si, void *ctx_void) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci ucontext_t *ctx = ctx_void; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci printf("\tGot %s with RIP=%lx\n", signames[sig], 13862306a36Sopenharmony_ci (unsigned long)ctx->uc_mcontext.gregs[REG_IP]); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci siglongjmp(jmpbuf, 1); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ciint main() 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci unsigned long nr; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci asm volatile ("mov %%ss, %[ss]" : [ss] "=m" (ss)); 14862306a36Sopenharmony_ci printf("\tSS = 0x%hx, &SS = 0x%p\n", ss, &ss); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0) == 0) 15162306a36Sopenharmony_ci printf("\tPR_SET_PTRACER_ANY succeeded\n"); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci printf("\tSet up a watchpoint\n"); 15462306a36Sopenharmony_ci sethandler(SIGTRAP, sigtrap, 0); 15562306a36Sopenharmony_ci enable_watchpoint(); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci printf("[RUN]\tRead from watched memory (should get SIGTRAP)\n"); 15862306a36Sopenharmony_ci asm volatile ("mov %[ss], %[tmp]" : [tmp] "=r" (nr) : [ss] "m" (ss)); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci printf("[RUN]\tMOV SS; INT3\n"); 16162306a36Sopenharmony_ci asm volatile ("mov %[ss], %%ss; int3" :: [ss] "m" (ss)); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci printf("[RUN]\tMOV SS; INT 3\n"); 16462306a36Sopenharmony_ci asm volatile ("mov %[ss], %%ss; .byte 0xcd, 0x3" :: [ss] "m" (ss)); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci printf("[RUN]\tMOV SS; CS CS INT3\n"); 16762306a36Sopenharmony_ci asm volatile ("mov %[ss], %%ss; .byte 0x2e, 0x2e; int3" :: [ss] "m" (ss)); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci printf("[RUN]\tMOV SS; CSx14 INT3\n"); 17062306a36Sopenharmony_ci asm volatile ("mov %[ss], %%ss; .fill 14,1,0x2e; int3" :: [ss] "m" (ss)); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci printf("[RUN]\tMOV SS; INT 4\n"); 17362306a36Sopenharmony_ci sethandler(SIGSEGV, handle_and_return, SA_RESETHAND); 17462306a36Sopenharmony_ci asm volatile ("mov %[ss], %%ss; int $4" :: [ss] "m" (ss)); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci#ifdef __i386__ 17762306a36Sopenharmony_ci printf("[RUN]\tMOV SS; INTO\n"); 17862306a36Sopenharmony_ci sethandler(SIGSEGV, handle_and_return, SA_RESETHAND); 17962306a36Sopenharmony_ci nr = -1; 18062306a36Sopenharmony_ci asm volatile ("add $1, %[tmp]; mov %[ss], %%ss; into" 18162306a36Sopenharmony_ci : [tmp] "+r" (nr) : [ss] "m" (ss)); 18262306a36Sopenharmony_ci#endif 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 18562306a36Sopenharmony_ci printf("[RUN]\tMOV SS; ICEBP\n"); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* Some emulators (e.g. QEMU TCG) don't emulate ICEBP. */ 18862306a36Sopenharmony_ci sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci asm volatile ("mov %[ss], %%ss; .byte 0xf1" :: [ss] "m" (ss)); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 19462306a36Sopenharmony_ci printf("[RUN]\tMOV SS; CLI\n"); 19562306a36Sopenharmony_ci sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND); 19662306a36Sopenharmony_ci asm volatile ("mov %[ss], %%ss; cli" :: [ss] "m" (ss)); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 20062306a36Sopenharmony_ci printf("[RUN]\tMOV SS; #PF\n"); 20162306a36Sopenharmony_ci sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND); 20262306a36Sopenharmony_ci asm volatile ("mov %[ss], %%ss; mov (-1), %[tmp]" 20362306a36Sopenharmony_ci : [tmp] "=r" (nr) : [ss] "m" (ss)); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * INT $1: if #DB has DPL=3 and there isn't special handling, 20862306a36Sopenharmony_ci * then the kernel will die. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 21162306a36Sopenharmony_ci printf("[RUN]\tMOV SS; INT 1\n"); 21262306a36Sopenharmony_ci sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND); 21362306a36Sopenharmony_ci asm volatile ("mov %[ss], %%ss; int $1" :: [ss] "m" (ss)); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci#ifdef __x86_64__ 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * In principle, we should test 32-bit SYSCALL as well, but 21962306a36Sopenharmony_ci * the calling convention is so unpredictable that it's 22062306a36Sopenharmony_ci * not obviously worth the effort. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 22362306a36Sopenharmony_ci printf("[RUN]\tMOV SS; SYSCALL\n"); 22462306a36Sopenharmony_ci sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND); 22562306a36Sopenharmony_ci nr = SYS_getpid; 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * Toggle the high bit of RSP to make it noncanonical to 22862306a36Sopenharmony_ci * strengthen this test on non-SMAP systems. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci asm volatile ("btc $63, %%rsp\n\t" 23162306a36Sopenharmony_ci "mov %[ss], %%ss; syscall\n\t" 23262306a36Sopenharmony_ci "btc $63, %%rsp" 23362306a36Sopenharmony_ci : "+a" (nr) : [ss] "m" (ss) 23462306a36Sopenharmony_ci : "rcx" 23562306a36Sopenharmony_ci#ifdef __x86_64__ 23662306a36Sopenharmony_ci , "r11" 23762306a36Sopenharmony_ci#endif 23862306a36Sopenharmony_ci ); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci#endif 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci printf("[RUN]\tMOV SS; breakpointed NOP\n"); 24362306a36Sopenharmony_ci asm volatile ("mov %[ss], %%ss; breakpoint_insn: nop" :: [ss] "m" (ss)); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* 24662306a36Sopenharmony_ci * Invoking SYSENTER directly breaks all the rules. Just handle 24762306a36Sopenharmony_ci * the SIGSEGV. 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 25062306a36Sopenharmony_ci printf("[RUN]\tMOV SS; SYSENTER\n"); 25162306a36Sopenharmony_ci stack_t stack = { 25262306a36Sopenharmony_ci .ss_sp = malloc(sizeof(char) * SIGSTKSZ), 25362306a36Sopenharmony_ci .ss_size = SIGSTKSZ, 25462306a36Sopenharmony_ci }; 25562306a36Sopenharmony_ci if (sigaltstack(&stack, NULL) != 0) 25662306a36Sopenharmony_ci err(1, "sigaltstack"); 25762306a36Sopenharmony_ci sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND | SA_ONSTACK); 25862306a36Sopenharmony_ci nr = SYS_getpid; 25962306a36Sopenharmony_ci free(stack.ss_sp); 26062306a36Sopenharmony_ci /* Clear EBP first to make sure we segfault cleanly. */ 26162306a36Sopenharmony_ci asm volatile ("xorl %%ebp, %%ebp; mov %[ss], %%ss; SYSENTER" : "+a" (nr) 26262306a36Sopenharmony_ci : [ss] "m" (ss) : "flags", "rcx" 26362306a36Sopenharmony_ci#ifdef __x86_64__ 26462306a36Sopenharmony_ci , "r11" 26562306a36Sopenharmony_ci#endif 26662306a36Sopenharmony_ci ); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* We're unreachable here. SYSENTER forgets RIP. */ 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 27262306a36Sopenharmony_ci printf("[RUN]\tMOV SS; INT $0x80\n"); 27362306a36Sopenharmony_ci sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND); 27462306a36Sopenharmony_ci nr = 20; /* compat getpid */ 27562306a36Sopenharmony_ci asm volatile ("mov %[ss], %%ss; int $0x80" 27662306a36Sopenharmony_ci : "+a" (nr) : [ss] "m" (ss) 27762306a36Sopenharmony_ci : "flags" 27862306a36Sopenharmony_ci#ifdef __x86_64__ 27962306a36Sopenharmony_ci , "r8", "r9", "r10", "r11" 28062306a36Sopenharmony_ci#endif 28162306a36Sopenharmony_ci ); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci printf("[OK]\tI aten't dead\n"); 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 287