single_step_syscall.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * single_step_syscall.c - single-steps various x86 syscalls
  4. * Copyright (c) 2014-2015 Andrew Lutomirski
  5. *
  6. * This is a very simple series of tests that makes system calls with
  7. * the TF flag set. This exercises some nasty kernel code in the
  8. * SYSENTER case: SYSENTER does not clear TF, so SYSENTER with TF set
  9. * immediately issues #DB from CPL 0. This requires special handling in
  10. * the kernel.
  11. */
  12. #define _GNU_SOURCE
  13. #include <sys/time.h>
  14. #include <time.h>
  15. #include <stdlib.h>
  16. #include <sys/syscall.h>
  17. #include <unistd.h>
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <inttypes.h>
  21. #include <sys/mman.h>
  22. #include <sys/signal.h>
  23. #include <sys/ucontext.h>
  24. #include <asm/ldt.h>
  25. #include <err.h>
  26. #include <setjmp.h>
  27. #include <stddef.h>
  28. #include <stdbool.h>
  29. #include <sys/ptrace.h>
  30. #include <sys/user.h>
  31. #include "helpers.h"
  32. static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
  33. int flags)
  34. {
  35. struct sigaction sa;
  36. memset(&sa, 0, sizeof(sa));
  37. sa.sa_sigaction = handler;
  38. sa.sa_flags = SA_SIGINFO | flags;
  39. sigemptyset(&sa.sa_mask);
  40. if (sigaction(sig, &sa, 0))
  41. err(1, "sigaction");
  42. }
  43. static void clearhandler(int sig)
  44. {
  45. struct sigaction sa;
  46. memset(&sa, 0, sizeof(sa));
  47. sa.sa_handler = SIG_DFL;
  48. sigemptyset(&sa.sa_mask);
  49. if (sigaction(sig, &sa, 0))
  50. err(1, "sigaction");
  51. }
  52. static volatile sig_atomic_t sig_traps, sig_eflags;
  53. sigjmp_buf jmpbuf;
  54. #ifdef __x86_64__
  55. # define REG_IP REG_RIP
  56. # define WIDTH "q"
  57. # define INT80_CLOBBERS "r8", "r9", "r10", "r11"
  58. #else
  59. # define REG_IP REG_EIP
  60. # define WIDTH "l"
  61. # define INT80_CLOBBERS
  62. #endif
  63. static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
  64. {
  65. ucontext_t *ctx = (ucontext_t*)ctx_void;
  66. if (get_eflags() & X86_EFLAGS_TF) {
  67. set_eflags(get_eflags() & ~X86_EFLAGS_TF);
  68. printf("[WARN]\tSIGTRAP handler had TF set\n");
  69. _exit(1);
  70. }
  71. sig_traps++;
  72. if (sig_traps == 10000 || sig_traps == 10001) {
  73. printf("[WARN]\tHit %d SIGTRAPs with si_addr 0x%lx, ip 0x%lx\n",
  74. (int)sig_traps,
  75. (unsigned long)info->si_addr,
  76. (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
  77. }
  78. }
  79. static char const * const signames[] = {
  80. [SIGSEGV] = "SIGSEGV",
  81. [SIGBUS] = "SIBGUS",
  82. [SIGTRAP] = "SIGTRAP",
  83. [SIGILL] = "SIGILL",
  84. };
  85. static void print_and_longjmp(int sig, siginfo_t *si, void *ctx_void)
  86. {
  87. ucontext_t *ctx = ctx_void;
  88. printf("\tGot %s with RIP=%lx, TF=%ld\n", signames[sig],
  89. (unsigned long)ctx->uc_mcontext.gregs[REG_IP],
  90. (unsigned long)ctx->uc_mcontext.gregs[REG_EFL] & X86_EFLAGS_TF);
  91. sig_eflags = (unsigned long)ctx->uc_mcontext.gregs[REG_EFL];
  92. siglongjmp(jmpbuf, 1);
  93. }
  94. static void check_result(void)
  95. {
  96. unsigned long new_eflags = get_eflags();
  97. set_eflags(new_eflags & ~X86_EFLAGS_TF);
  98. if (!sig_traps) {
  99. printf("[FAIL]\tNo SIGTRAP\n");
  100. exit(1);
  101. }
  102. if (!(new_eflags & X86_EFLAGS_TF)) {
  103. printf("[FAIL]\tTF was cleared\n");
  104. exit(1);
  105. }
  106. printf("[OK]\tSurvived with TF set and %d traps\n", (int)sig_traps);
  107. sig_traps = 0;
  108. }
  109. static void fast_syscall_no_tf(void)
  110. {
  111. sig_traps = 0;
  112. printf("[RUN]\tFast syscall with TF cleared\n");
  113. fflush(stdout); /* Force a syscall */
  114. if (get_eflags() & X86_EFLAGS_TF) {
  115. printf("[FAIL]\tTF is now set\n");
  116. exit(1);
  117. }
  118. if (sig_traps) {
  119. printf("[FAIL]\tGot SIGTRAP\n");
  120. exit(1);
  121. }
  122. printf("[OK]\tNothing unexpected happened\n");
  123. }
  124. int main()
  125. {
  126. #ifdef CAN_BUILD_32
  127. int tmp;
  128. #endif
  129. sethandler(SIGTRAP, sigtrap, 0);
  130. printf("[RUN]\tSet TF and check nop\n");
  131. set_eflags(get_eflags() | X86_EFLAGS_TF);
  132. asm volatile ("nop");
  133. check_result();
  134. #ifdef __x86_64__
  135. printf("[RUN]\tSet TF and check syscall-less opportunistic sysret\n");
  136. set_eflags(get_eflags() | X86_EFLAGS_TF);
  137. extern unsigned char post_nop[];
  138. asm volatile ("pushf" WIDTH "\n\t"
  139. "pop" WIDTH " %%r11\n\t"
  140. "nop\n\t"
  141. "post_nop:"
  142. : : "c" (post_nop) : "r11");
  143. check_result();
  144. #endif
  145. #ifdef CAN_BUILD_32
  146. printf("[RUN]\tSet TF and check int80\n");
  147. set_eflags(get_eflags() | X86_EFLAGS_TF);
  148. asm volatile ("int $0x80" : "=a" (tmp) : "a" (SYS_getpid)
  149. : INT80_CLOBBERS);
  150. check_result();
  151. #endif
  152. /*
  153. * This test is particularly interesting if fast syscalls use
  154. * SYSENTER: it triggers a nasty design flaw in SYSENTER.
  155. * Specifically, SYSENTER does not clear TF, so either SYSENTER
  156. * or the next instruction traps at CPL0. (Of course, Intel
  157. * mostly forgot to document exactly what happens here.) So we
  158. * get a CPL0 fault with usergs (on 64-bit kernels) and possibly
  159. * no stack. The only sane way the kernel can possibly handle
  160. * it is to clear TF on return from the #DB handler, but this
  161. * happens way too early to set TF in the saved pt_regs, so the
  162. * kernel has to do something clever to avoid losing track of
  163. * the TF bit.
  164. *
  165. * Needless to say, we've had bugs in this area.
  166. */
  167. syscall(SYS_getpid); /* Force symbol binding without TF set. */
  168. printf("[RUN]\tSet TF and check a fast syscall\n");
  169. set_eflags(get_eflags() | X86_EFLAGS_TF);
  170. syscall(SYS_getpid);
  171. check_result();
  172. /* Now make sure that another fast syscall doesn't set TF again. */
  173. fast_syscall_no_tf();
  174. /*
  175. * And do a forced SYSENTER to make sure that this works even if
  176. * fast syscalls don't use SYSENTER.
  177. *
  178. * Invoking SYSENTER directly breaks all the rules. Just handle
  179. * the SIGSEGV.
  180. */
  181. if (sigsetjmp(jmpbuf, 1) == 0) {
  182. unsigned long nr = SYS_getpid;
  183. printf("[RUN]\tSet TF and check SYSENTER\n");
  184. stack_t stack = {
  185. .ss_sp = malloc(sizeof(char) * SIGSTKSZ),
  186. .ss_size = SIGSTKSZ,
  187. };
  188. if (sigaltstack(&stack, NULL) != 0)
  189. err(1, "sigaltstack");
  190. sethandler(SIGSEGV, print_and_longjmp,
  191. SA_RESETHAND | SA_ONSTACK);
  192. sethandler(SIGILL, print_and_longjmp, SA_RESETHAND);
  193. set_eflags(get_eflags() | X86_EFLAGS_TF);
  194. free(stack.ss_sp);
  195. /* Clear EBP first to make sure we segfault cleanly. */
  196. asm volatile ("xorl %%ebp, %%ebp; SYSENTER" : "+a" (nr) :: "flags", "rcx"
  197. #ifdef __x86_64__
  198. , "r11"
  199. #endif
  200. );
  201. /* We're unreachable here. SYSENTER forgets RIP. */
  202. }
  203. clearhandler(SIGSEGV);
  204. clearhandler(SIGILL);
  205. if (!(sig_eflags & X86_EFLAGS_TF)) {
  206. printf("[FAIL]\tTF was cleared\n");
  207. exit(1);
  208. }
  209. /* Now make sure that another fast syscall doesn't set TF again. */
  210. fast_syscall_no_tf();
  211. return 0;
  212. }