Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0 */
0002 /*
0003  * mov_ss_trap.c: Exercise the bizarre side effects of a watchpoint on MOV SS
0004  *
0005  * This does MOV SS from a watchpointed address followed by various
0006  * types of kernel entries.  A MOV SS that hits a watchpoint will queue
0007  * up a #DB trap but will not actually deliver that trap.  The trap
0008  * will be delivered after the next instruction instead.  The CPU's logic
0009  * seems to be:
0010  *
0011  *  - Any fault: drop the pending #DB trap.
0012  *  - INT $N, INT3, INTO, SYSCALL, SYSENTER: enter the kernel and then
0013  *    deliver #DB.
0014  *  - ICEBP: enter the kernel but do not deliver the watchpoint trap
0015  *  - breakpoint: only one #DB is delivered (phew!)
0016  *
0017  * There are plenty of ways for a kernel to handle this incorrectly.  This
0018  * test tries to exercise all the cases.
0019  *
0020  * This should mostly cover CVE-2018-1087 and CVE-2018-8897.
0021  */
0022 #define _GNU_SOURCE
0023 
0024 #include <stdlib.h>
0025 #include <sys/ptrace.h>
0026 #include <sys/types.h>
0027 #include <sys/wait.h>
0028 #include <sys/user.h>
0029 #include <sys/syscall.h>
0030 #include <unistd.h>
0031 #include <errno.h>
0032 #include <stddef.h>
0033 #include <stdio.h>
0034 #include <err.h>
0035 #include <string.h>
0036 #include <setjmp.h>
0037 #include <sys/prctl.h>
0038 
0039 #define X86_EFLAGS_RF (1UL << 16)
0040 
0041 #if __x86_64__
0042 # define REG_IP REG_RIP
0043 #else
0044 # define REG_IP REG_EIP
0045 #endif
0046 
0047 unsigned short ss;
0048 extern unsigned char breakpoint_insn[];
0049 sigjmp_buf jmpbuf;
0050 
0051 static void enable_watchpoint(void)
0052 {
0053     pid_t parent = getpid();
0054     int status;
0055 
0056     pid_t child = fork();
0057     if (child < 0)
0058         err(1, "fork");
0059 
0060     if (child) {
0061         if (waitpid(child, &status, 0) != child)
0062             err(1, "waitpid for child");
0063     } else {
0064         unsigned long dr0, dr1, dr7;
0065 
0066         dr0 = (unsigned long)&ss;
0067         dr1 = (unsigned long)breakpoint_insn;
0068         dr7 = ((1UL << 1) | /* G0 */
0069                (3UL << 16) |    /* RW0 = read or write */
0070                (1UL << 18) |    /* LEN0 = 2 bytes */
0071                (1UL << 3)); /* G1, RW1 = insn */
0072 
0073         if (ptrace(PTRACE_ATTACH, parent, NULL, NULL) != 0)
0074             err(1, "PTRACE_ATTACH");
0075 
0076         if (waitpid(parent, &status, 0) != parent)
0077             err(1, "waitpid for child");
0078 
0079         if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[0]), dr0) != 0)
0080             err(1, "PTRACE_POKEUSER DR0");
0081 
0082         if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[1]), dr1) != 0)
0083             err(1, "PTRACE_POKEUSER DR1");
0084 
0085         if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[7]), dr7) != 0)
0086             err(1, "PTRACE_POKEUSER DR7");
0087 
0088         printf("\tDR0 = %lx, DR1 = %lx, DR7 = %lx\n", dr0, dr1, dr7);
0089 
0090         if (ptrace(PTRACE_DETACH, parent, NULL, NULL) != 0)
0091             err(1, "PTRACE_DETACH");
0092 
0093         exit(0);
0094     }
0095 }
0096 
0097 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
0098                int flags)
0099 {
0100     struct sigaction sa;
0101     memset(&sa, 0, sizeof(sa));
0102     sa.sa_sigaction = handler;
0103     sa.sa_flags = SA_SIGINFO | flags;
0104     sigemptyset(&sa.sa_mask);
0105     if (sigaction(sig, &sa, 0))
0106         err(1, "sigaction");
0107 }
0108 
0109 static char const * const signames[] = {
0110     [SIGSEGV] = "SIGSEGV",
0111     [SIGBUS] = "SIBGUS",
0112     [SIGTRAP] = "SIGTRAP",
0113     [SIGILL] = "SIGILL",
0114 };
0115 
0116 static void sigtrap(int sig, siginfo_t *si, void *ctx_void)
0117 {
0118     ucontext_t *ctx = ctx_void;
0119 
0120     printf("\tGot SIGTRAP with RIP=%lx, EFLAGS.RF=%d\n",
0121            (unsigned long)ctx->uc_mcontext.gregs[REG_IP],
0122            !!(ctx->uc_mcontext.gregs[REG_EFL] & X86_EFLAGS_RF));
0123 }
0124 
0125 static void handle_and_return(int sig, siginfo_t *si, void *ctx_void)
0126 {
0127     ucontext_t *ctx = ctx_void;
0128 
0129     printf("\tGot %s with RIP=%lx\n", signames[sig],
0130            (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
0131 }
0132 
0133 static void handle_and_longjmp(int sig, siginfo_t *si, void *ctx_void)
0134 {
0135     ucontext_t *ctx = ctx_void;
0136 
0137     printf("\tGot %s with RIP=%lx\n", signames[sig],
0138            (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
0139 
0140     siglongjmp(jmpbuf, 1);
0141 }
0142 
0143 int main()
0144 {
0145     unsigned long nr;
0146 
0147     asm volatile ("mov %%ss, %[ss]" : [ss] "=m" (ss));
0148     printf("\tSS = 0x%hx, &SS = 0x%p\n", ss, &ss);
0149 
0150     if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0) == 0)
0151         printf("\tPR_SET_PTRACER_ANY succeeded\n");
0152 
0153     printf("\tSet up a watchpoint\n");
0154     sethandler(SIGTRAP, sigtrap, 0);
0155     enable_watchpoint();
0156 
0157     printf("[RUN]\tRead from watched memory (should get SIGTRAP)\n");
0158     asm volatile ("mov %[ss], %[tmp]" : [tmp] "=r" (nr) : [ss] "m" (ss));
0159 
0160     printf("[RUN]\tMOV SS; INT3\n");
0161     asm volatile ("mov %[ss], %%ss; int3" :: [ss] "m" (ss));
0162 
0163     printf("[RUN]\tMOV SS; INT 3\n");
0164     asm volatile ("mov %[ss], %%ss; .byte 0xcd, 0x3" :: [ss] "m" (ss));
0165 
0166     printf("[RUN]\tMOV SS; CS CS INT3\n");
0167     asm volatile ("mov %[ss], %%ss; .byte 0x2e, 0x2e; int3" :: [ss] "m" (ss));
0168 
0169     printf("[RUN]\tMOV SS; CSx14 INT3\n");
0170     asm volatile ("mov %[ss], %%ss; .fill 14,1,0x2e; int3" :: [ss] "m" (ss));
0171 
0172     printf("[RUN]\tMOV SS; INT 4\n");
0173     sethandler(SIGSEGV, handle_and_return, SA_RESETHAND);
0174     asm volatile ("mov %[ss], %%ss; int $4" :: [ss] "m" (ss));
0175 
0176 #ifdef __i386__
0177     printf("[RUN]\tMOV SS; INTO\n");
0178     sethandler(SIGSEGV, handle_and_return, SA_RESETHAND);
0179     nr = -1;
0180     asm volatile ("add $1, %[tmp]; mov %[ss], %%ss; into"
0181               : [tmp] "+r" (nr) : [ss] "m" (ss));
0182 #endif
0183 
0184     if (sigsetjmp(jmpbuf, 1) == 0) {
0185         printf("[RUN]\tMOV SS; ICEBP\n");
0186 
0187         /* Some emulators (e.g. QEMU TCG) don't emulate ICEBP. */
0188         sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND);
0189 
0190         asm volatile ("mov %[ss], %%ss; .byte 0xf1" :: [ss] "m" (ss));
0191     }
0192 
0193     if (sigsetjmp(jmpbuf, 1) == 0) {
0194         printf("[RUN]\tMOV SS; CLI\n");
0195         sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
0196         asm volatile ("mov %[ss], %%ss; cli" :: [ss] "m" (ss));
0197     }
0198 
0199     if (sigsetjmp(jmpbuf, 1) == 0) {
0200         printf("[RUN]\tMOV SS; #PF\n");
0201         sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
0202         asm volatile ("mov %[ss], %%ss; mov (-1), %[tmp]"
0203                   : [tmp] "=r" (nr) : [ss] "m" (ss));
0204     }
0205 
0206     /*
0207      * INT $1: if #DB has DPL=3 and there isn't special handling,
0208      * then the kernel will die.
0209      */
0210     if (sigsetjmp(jmpbuf, 1) == 0) {
0211         printf("[RUN]\tMOV SS; INT 1\n");
0212         sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
0213         asm volatile ("mov %[ss], %%ss; int $1" :: [ss] "m" (ss));
0214     }
0215 
0216 #ifdef __x86_64__
0217     /*
0218      * In principle, we should test 32-bit SYSCALL as well, but
0219      * the calling convention is so unpredictable that it's
0220      * not obviously worth the effort.
0221      */
0222     if (sigsetjmp(jmpbuf, 1) == 0) {
0223         printf("[RUN]\tMOV SS; SYSCALL\n");
0224         sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND);
0225         nr = SYS_getpid;
0226         /*
0227          * Toggle the high bit of RSP to make it noncanonical to
0228          * strengthen this test on non-SMAP systems.
0229          */
0230         asm volatile ("btc $63, %%rsp\n\t"
0231                   "mov %[ss], %%ss; syscall\n\t"
0232                   "btc $63, %%rsp"
0233                   : "+a" (nr) : [ss] "m" (ss)
0234                   : "rcx"
0235 #ifdef __x86_64__
0236                 , "r11"
0237 #endif
0238             );
0239     }
0240 #endif
0241 
0242     printf("[RUN]\tMOV SS; breakpointed NOP\n");
0243     asm volatile ("mov %[ss], %%ss; breakpoint_insn: nop" :: [ss] "m" (ss));
0244 
0245     /*
0246      * Invoking SYSENTER directly breaks all the rules.  Just handle
0247      * the SIGSEGV.
0248      */
0249     if (sigsetjmp(jmpbuf, 1) == 0) {
0250         printf("[RUN]\tMOV SS; SYSENTER\n");
0251         stack_t stack = {
0252             .ss_sp = malloc(sizeof(char) * SIGSTKSZ),
0253             .ss_size = SIGSTKSZ,
0254         };
0255         if (sigaltstack(&stack, NULL) != 0)
0256             err(1, "sigaltstack");
0257         sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND | SA_ONSTACK);
0258         nr = SYS_getpid;
0259         free(stack.ss_sp);
0260         /* Clear EBP first to make sure we segfault cleanly. */
0261         asm volatile ("xorl %%ebp, %%ebp; mov %[ss], %%ss; SYSENTER" : "+a" (nr)
0262                   : [ss] "m" (ss) : "flags", "rcx"
0263 #ifdef __x86_64__
0264                 , "r11"
0265 #endif
0266             );
0267 
0268         /* We're unreachable here.  SYSENTER forgets RIP. */
0269     }
0270 
0271     if (sigsetjmp(jmpbuf, 1) == 0) {
0272         printf("[RUN]\tMOV SS; INT $0x80\n");
0273         sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
0274         nr = 20;    /* compat getpid */
0275         asm volatile ("mov %[ss], %%ss; int $0x80"
0276                   : "+a" (nr) : [ss] "m" (ss)
0277                   : "flags"
0278 #ifdef __x86_64__
0279                 , "r8", "r9", "r10", "r11"
0280 #endif
0281             );
0282     }
0283 
0284     printf("[OK]\tI aten't dead\n");
0285     return 0;
0286 }