mov_ss_trap.c 7.6 KB

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