sysret_rip.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /*
  2. * sigreturn.c - tests that x86 avoids Intel SYSRET pitfalls
  3. * Copyright (c) 2014-2016 Andrew Lutomirski
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms and conditions of the GNU General Public License,
  7. * version 2, as published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. */
  14. #define _GNU_SOURCE
  15. #include <stdlib.h>
  16. #include <unistd.h>
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include <inttypes.h>
  20. #include <sys/signal.h>
  21. #include <sys/ucontext.h>
  22. #include <sys/syscall.h>
  23. #include <err.h>
  24. #include <stddef.h>
  25. #include <stdbool.h>
  26. #include <setjmp.h>
  27. #include <sys/user.h>
  28. #include <sys/mman.h>
  29. #include <assert.h>
  30. asm (
  31. ".pushsection \".text\", \"ax\"\n\t"
  32. ".balign 4096\n\t"
  33. "test_page: .globl test_page\n\t"
  34. ".fill 4094,1,0xcc\n\t"
  35. "test_syscall_insn:\n\t"
  36. "syscall\n\t"
  37. ".ifne . - test_page - 4096\n\t"
  38. ".error \"test page is not one page long\"\n\t"
  39. ".endif\n\t"
  40. ".popsection"
  41. );
  42. extern const char test_page[];
  43. static void const *current_test_page_addr = test_page;
  44. static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
  45. int flags)
  46. {
  47. struct sigaction sa;
  48. memset(&sa, 0, sizeof(sa));
  49. sa.sa_sigaction = handler;
  50. sa.sa_flags = SA_SIGINFO | flags;
  51. sigemptyset(&sa.sa_mask);
  52. if (sigaction(sig, &sa, 0))
  53. err(1, "sigaction");
  54. }
  55. static void clearhandler(int sig)
  56. {
  57. struct sigaction sa;
  58. memset(&sa, 0, sizeof(sa));
  59. sa.sa_handler = SIG_DFL;
  60. sigemptyset(&sa.sa_mask);
  61. if (sigaction(sig, &sa, 0))
  62. err(1, "sigaction");
  63. }
  64. /* State used by our signal handlers. */
  65. static gregset_t initial_regs;
  66. static volatile unsigned long rip;
  67. static void sigsegv_for_sigreturn_test(int sig, siginfo_t *info, void *ctx_void)
  68. {
  69. ucontext_t *ctx = (ucontext_t*)ctx_void;
  70. if (rip != ctx->uc_mcontext.gregs[REG_RIP]) {
  71. printf("[FAIL]\tRequested RIP=0x%lx but got RIP=0x%lx\n",
  72. rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]);
  73. fflush(stdout);
  74. _exit(1);
  75. }
  76. memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t));
  77. printf("[OK]\tGot SIGSEGV at RIP=0x%lx\n", rip);
  78. }
  79. static void sigusr1(int sig, siginfo_t *info, void *ctx_void)
  80. {
  81. ucontext_t *ctx = (ucontext_t*)ctx_void;
  82. memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
  83. /* Set IP and CX to match so that SYSRET can happen. */
  84. ctx->uc_mcontext.gregs[REG_RIP] = rip;
  85. ctx->uc_mcontext.gregs[REG_RCX] = rip;
  86. /* R11 and EFLAGS should already match. */
  87. assert(ctx->uc_mcontext.gregs[REG_EFL] ==
  88. ctx->uc_mcontext.gregs[REG_R11]);
  89. sethandler(SIGSEGV, sigsegv_for_sigreturn_test, SA_RESETHAND);
  90. return;
  91. }
  92. static void test_sigreturn_to(unsigned long ip)
  93. {
  94. rip = ip;
  95. printf("[RUN]\tsigreturn to 0x%lx\n", ip);
  96. raise(SIGUSR1);
  97. }
  98. static jmp_buf jmpbuf;
  99. static void sigsegv_for_fallthrough(int sig, siginfo_t *info, void *ctx_void)
  100. {
  101. ucontext_t *ctx = (ucontext_t*)ctx_void;
  102. if (rip != ctx->uc_mcontext.gregs[REG_RIP]) {
  103. printf("[FAIL]\tExpected SIGSEGV at 0x%lx but got RIP=0x%lx\n",
  104. rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]);
  105. fflush(stdout);
  106. _exit(1);
  107. }
  108. siglongjmp(jmpbuf, 1);
  109. }
  110. static void test_syscall_fallthrough_to(unsigned long ip)
  111. {
  112. void *new_address = (void *)(ip - 4096);
  113. void *ret;
  114. printf("[RUN]\tTrying a SYSCALL that falls through to 0x%lx\n", ip);
  115. ret = mremap((void *)current_test_page_addr, 4096, 4096,
  116. MREMAP_MAYMOVE | MREMAP_FIXED, new_address);
  117. if (ret == MAP_FAILED) {
  118. if (ip <= (1UL << 47) - PAGE_SIZE) {
  119. err(1, "mremap to %p", new_address);
  120. } else {
  121. printf("[OK]\tmremap to %p failed\n", new_address);
  122. return;
  123. }
  124. }
  125. if (ret != new_address)
  126. errx(1, "mremap malfunctioned: asked for %p but got %p\n",
  127. new_address, ret);
  128. current_test_page_addr = new_address;
  129. rip = ip;
  130. if (sigsetjmp(jmpbuf, 1) == 0) {
  131. asm volatile ("call *%[syscall_insn]" :: "a" (SYS_getpid),
  132. [syscall_insn] "rm" (ip - 2));
  133. errx(1, "[FAIL]\tSyscall trampoline returned");
  134. }
  135. printf("[OK]\tWe survived\n");
  136. }
  137. int main()
  138. {
  139. /*
  140. * When the kernel returns from a slow-path syscall, it will
  141. * detect whether SYSRET is appropriate. If it incorrectly
  142. * thinks that SYSRET is appropriate when RIP is noncanonical,
  143. * it'll crash on Intel CPUs.
  144. */
  145. sethandler(SIGUSR1, sigusr1, 0);
  146. for (int i = 47; i < 64; i++)
  147. test_sigreturn_to(1UL<<i);
  148. clearhandler(SIGUSR1);
  149. sethandler(SIGSEGV, sigsegv_for_fallthrough, 0);
  150. /* One extra test to check that we didn't screw up the mremap logic. */
  151. test_syscall_fallthrough_to((1UL << 47) - 2*PAGE_SIZE);
  152. /* These are the interesting cases. */
  153. for (int i = 47; i < 64; i++) {
  154. test_syscall_fallthrough_to((1UL<<i) - PAGE_SIZE);
  155. test_syscall_fallthrough_to(1UL<<i);
  156. }
  157. return 0;
  158. }