root/tools/testing/selftests/x86/mov_ss_trap.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. enable_watchpoint
  2. sethandler
  3. sigtrap
  4. handle_and_return
  5. handle_and_longjmp
  6. main

   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 }

/* [<][>][^][v][top][bottom][index][help] */