~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/x86/mov_ss_trap.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
 51 static void enable_watchpoint(void)
 52 {
 53         pid_t parent = getpid();
 54         int status;
 55 
 56         pid_t child = fork();
 57         if (child < 0)
 58                 err(1, "fork");
 59 
 60         if (child) {
 61                 if (waitpid(child, &status, 0) != child)
 62                         err(1, "waitpid for child");
 63         } else {
 64                 unsigned long dr0, dr1, dr7;
 65 
 66                 dr0 = (unsigned long)&ss;
 67                 dr1 = (unsigned long)breakpoint_insn;
 68                 dr7 = ((1UL << 1) |     /* G0 */
 69                        (3UL << 16) |    /* RW0 = read or write */
 70                        (1UL << 18) |    /* LEN0 = 2 bytes */
 71                        (1UL << 3));     /* G1, RW1 = insn */
 72 
 73                 if (ptrace(PTRACE_ATTACH, parent, NULL, NULL) != 0)
 74                         err(1, "PTRACE_ATTACH");
 75 
 76                 if (waitpid(parent, &status, 0) != parent)
 77                         err(1, "waitpid for child");
 78 
 79                 if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[0]), dr0) != 0)
 80                         err(1, "PTRACE_POKEUSER DR0");
 81 
 82                 if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[1]), dr1) != 0)
 83                         err(1, "PTRACE_POKEUSER DR1");
 84 
 85                 if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[7]), dr7) != 0)
 86                         err(1, "PTRACE_POKEUSER DR7");
 87 
 88                 printf("\tDR0 = %lx, DR1 = %lx, DR7 = %lx\n", dr0, dr1, dr7);
 89 
 90                 if (ptrace(PTRACE_DETACH, parent, NULL, NULL) != 0)
 91                         err(1, "PTRACE_DETACH");
 92 
 93                 exit(0);
 94         }
 95 }
 96 
 97 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
 98                        int flags)
 99 {
100         struct sigaction sa;
101         memset(&sa, 0, sizeof(sa));
102         sa.sa_sigaction = handler;
103         sa.sa_flags = SA_SIGINFO | flags;
104         sigemptyset(&sa.sa_mask);
105         if (sigaction(sig, &sa, 0))
106                 err(1, "sigaction");
107 }
108 
109 static char const * const signames[] = {
110         [SIGSEGV] = "SIGSEGV",
111         [SIGBUS] = "SIBGUS",
112         [SIGTRAP] = "SIGTRAP",
113         [SIGILL] = "SIGILL",
114 };
115 
116 static void sigtrap(int sig, siginfo_t *si, void *ctx_void)
117 {
118         ucontext_t *ctx = ctx_void;
119 
120         printf("\tGot SIGTRAP with RIP=%lx, EFLAGS.RF=%d\n",
121                (unsigned long)ctx->uc_mcontext.gregs[REG_IP],
122                !!(ctx->uc_mcontext.gregs[REG_EFL] & X86_EFLAGS_RF));
123 }
124 
125 static void handle_and_return(int sig, siginfo_t *si, void *ctx_void)
126 {
127         ucontext_t *ctx = ctx_void;
128 
129         printf("\tGot %s with RIP=%lx\n", signames[sig],
130                (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
131 }
132 
133 static void handle_and_longjmp(int sig, siginfo_t *si, void *ctx_void)
134 {
135         ucontext_t *ctx = ctx_void;
136 
137         printf("\tGot %s with RIP=%lx\n", signames[sig],
138                (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
139 
140         siglongjmp(jmpbuf, 1);
141 }
142 
143 int main()
144 {
145         unsigned long nr;
146 
147         asm volatile ("mov %%ss, %[ss]" : [ss] "=m" (ss));
148         printf("\tSS = 0x%hx, &SS = 0x%p\n", ss, &ss);
149 
150         if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0) == 0)
151                 printf("\tPR_SET_PTRACER_ANY succeeded\n");
152 
153         printf("\tSet up a watchpoint\n");
154         sethandler(SIGTRAP, sigtrap, 0);
155         enable_watchpoint();
156 
157         printf("[RUN]\tRead from watched memory (should get SIGTRAP)\n");
158         asm volatile ("mov %[ss], %[tmp]" : [tmp] "=r" (nr) : [ss] "m" (ss));
159 
160         printf("[RUN]\tMOV SS; INT3\n");
161         asm volatile ("mov %[ss], %%ss; int3" :: [ss] "m" (ss));
162 
163         printf("[RUN]\tMOV SS; INT 3\n");
164         asm volatile ("mov %[ss], %%ss; .byte 0xcd, 0x3" :: [ss] "m" (ss));
165 
166         printf("[RUN]\tMOV SS; CS CS INT3\n");
167         asm volatile ("mov %[ss], %%ss; .byte 0x2e, 0x2e; int3" :: [ss] "m" (ss));
168 
169         printf("[RUN]\tMOV SS; CSx14 INT3\n");
170         asm volatile ("mov %[ss], %%ss; .fill 14,1,0x2e; int3" :: [ss] "m" (ss));
171 
172         printf("[RUN]\tMOV SS; INT 4\n");
173         sethandler(SIGSEGV, handle_and_return, SA_RESETHAND);
174         asm volatile ("mov %[ss], %%ss; int $4" :: [ss] "m" (ss));
175 
176 #ifdef __i386__
177         printf("[RUN]\tMOV SS; INTO\n");
178         sethandler(SIGSEGV, handle_and_return, SA_RESETHAND);
179         nr = -1;
180         asm volatile ("add $1, %[tmp]; mov %[ss], %%ss; into"
181                       : [tmp] "+r" (nr) : [ss] "m" (ss));
182 #endif
183 
184         if (sigsetjmp(jmpbuf, 1) == 0) {
185                 printf("[RUN]\tMOV SS; ICEBP\n");
186 
187                 /* Some emulators (e.g. QEMU TCG) don't emulate ICEBP. */
188                 sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND);
189 
190                 asm volatile ("mov %[ss], %%ss; .byte 0xf1" :: [ss] "m" (ss));
191         }
192 
193         if (sigsetjmp(jmpbuf, 1) == 0) {
194                 printf("[RUN]\tMOV SS; CLI\n");
195                 sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
196                 asm volatile ("mov %[ss], %%ss; cli" :: [ss] "m" (ss));
197         }
198 
199         if (sigsetjmp(jmpbuf, 1) == 0) {
200                 printf("[RUN]\tMOV SS; #PF\n");
201                 sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
202                 asm volatile ("mov %[ss], %%ss; mov (-1), %[tmp]"
203                               : [tmp] "=r" (nr) : [ss] "m" (ss));
204         }
205 
206         /*
207          * INT $1: if #DB has DPL=3 and there isn't special handling,
208          * then the kernel will die.
209          */
210         if (sigsetjmp(jmpbuf, 1) == 0) {
211                 printf("[RUN]\tMOV SS; INT 1\n");
212                 sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
213                 asm volatile ("mov %[ss], %%ss; int $1" :: [ss] "m" (ss));
214         }
215 
216 #ifdef __x86_64__
217         /*
218          * In principle, we should test 32-bit SYSCALL as well, but
219          * the calling convention is so unpredictable that it's
220          * not obviously worth the effort.
221          */
222         if (sigsetjmp(jmpbuf, 1) == 0) {
223                 printf("[RUN]\tMOV SS; SYSCALL\n");
224                 sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND);
225                 nr = SYS_getpid;
226                 /*
227                  * Toggle the high bit of RSP to make it noncanonical to
228                  * strengthen this test on non-SMAP systems.
229                  */
230                 asm volatile ("btc $63, %%rsp\n\t"
231                               "mov %[ss], %%ss; syscall\n\t"
232                               "btc $63, %%rsp"
233                               : "+a" (nr) : [ss] "m" (ss)
234                               : "rcx"
235 #ifdef __x86_64__
236                                 , "r11"
237 #endif
238                         );
239         }
240 #endif
241 
242         printf("[RUN]\tMOV SS; breakpointed NOP\n");
243         asm volatile ("mov %[ss], %%ss; breakpoint_insn: nop" :: [ss] "m" (ss));
244 
245         /*
246          * Invoking SYSENTER directly breaks all the rules.  Just handle
247          * the SIGSEGV.
248          */
249         if (sigsetjmp(jmpbuf, 1) == 0) {
250                 printf("[RUN]\tMOV SS; SYSENTER\n");
251                 stack_t stack = {
252                         .ss_sp = malloc(sizeof(char) * SIGSTKSZ),
253                         .ss_size = SIGSTKSZ,
254                 };
255                 if (sigaltstack(&stack, NULL) != 0)
256                         err(1, "sigaltstack");
257                 sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND | SA_ONSTACK);
258                 nr = SYS_getpid;
259                 free(stack.ss_sp);
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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php