syscall_arg_fault.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args
  4. * Copyright (c) 2015 Andrew Lutomirski
  5. */
  6. #define _GNU_SOURCE
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <sys/signal.h>
  11. #include <sys/ucontext.h>
  12. #include <err.h>
  13. #include <setjmp.h>
  14. #include <errno.h>
  15. #include "helpers.h"
  16. static sigjmp_buf jmpbuf;
  17. static volatile sig_atomic_t n_errs;
  18. #ifdef __x86_64__
  19. #define REG_AX REG_RAX
  20. #define REG_IP REG_RIP
  21. #else
  22. #define REG_AX REG_EAX
  23. #define REG_IP REG_EIP
  24. #endif
  25. static void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void)
  26. {
  27. ucontext_t *ctx = (ucontext_t*)ctx_void;
  28. long ax = (long)ctx->uc_mcontext.gregs[REG_AX];
  29. if (ax != -EFAULT && ax != -ENOSYS) {
  30. printf("[FAIL]\tAX had the wrong value: 0x%lx\n",
  31. (unsigned long)ax);
  32. printf("\tIP = 0x%lx\n", (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);
  33. n_errs++;
  34. } else {
  35. printf("[OK]\tSeems okay\n");
  36. }
  37. siglongjmp(jmpbuf, 1);
  38. }
  39. static volatile sig_atomic_t sigtrap_consecutive_syscalls;
  40. static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
  41. {
  42. /*
  43. * KVM has some bugs that can cause us to stop making progress.
  44. * detect them and complain, but don't infinite loop or fail the
  45. * test.
  46. */
  47. ucontext_t *ctx = (ucontext_t*)ctx_void;
  48. unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];
  49. if (*ip == 0x340f || *ip == 0x050f) {
  50. /* The trap was on SYSCALL or SYSENTER */
  51. sigtrap_consecutive_syscalls++;
  52. if (sigtrap_consecutive_syscalls > 3) {
  53. printf("[WARN]\tGot stuck single-stepping -- you probably have a KVM bug\n");
  54. siglongjmp(jmpbuf, 1);
  55. }
  56. } else {
  57. sigtrap_consecutive_syscalls = 0;
  58. }
  59. }
  60. static void sigill(int sig, siginfo_t *info, void *ctx_void)
  61. {
  62. ucontext_t *ctx = (ucontext_t*)ctx_void;
  63. unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];
  64. if (*ip == 0x0b0f) {
  65. /* one of the ud2 instructions faulted */
  66. printf("[OK]\tSYSCALL returned normally\n");
  67. } else {
  68. printf("[SKIP]\tIllegal instruction\n");
  69. }
  70. siglongjmp(jmpbuf, 1);
  71. }
  72. int main()
  73. {
  74. stack_t stack = {
  75. /* Our sigaltstack scratch space. */
  76. .ss_sp = malloc(sizeof(char) * SIGSTKSZ),
  77. .ss_size = SIGSTKSZ,
  78. };
  79. if (sigaltstack(&stack, NULL) != 0)
  80. err(1, "sigaltstack");
  81. sethandler(SIGSEGV, sigsegv_or_sigbus, SA_ONSTACK);
  82. /*
  83. * The actual exception can vary. On Atom CPUs, we get #SS
  84. * instead of #PF when the vDSO fails to access the stack when
  85. * ESP is too close to 2^32, and #SS causes SIGBUS.
  86. */
  87. sethandler(SIGBUS, sigsegv_or_sigbus, SA_ONSTACK);
  88. sethandler(SIGILL, sigill, SA_ONSTACK);
  89. /*
  90. * Exercise another nasty special case. The 32-bit SYSCALL
  91. * and SYSENTER instructions (even in compat mode) each
  92. * clobber one register. A Linux system call has a syscall
  93. * number and six arguments, and the user stack pointer
  94. * needs to live in some register on return. That means
  95. * that we need eight registers, but SYSCALL and SYSENTER
  96. * only preserve seven registers. As a result, one argument
  97. * ends up on the stack. The stack is user memory, which
  98. * means that the kernel can fail to read it.
  99. *
  100. * The 32-bit fast system calls don't have a defined ABI:
  101. * we're supposed to invoke them through the vDSO. So we'll
  102. * fudge it: we set all regs to invalid pointer values and
  103. * invoke the entry instruction. The return will fail no
  104. * matter what, and we completely lose our program state,
  105. * but we can fix it up with a signal handler.
  106. */
  107. printf("[RUN]\tSYSENTER with invalid state\n");
  108. if (sigsetjmp(jmpbuf, 1) == 0) {
  109. asm volatile (
  110. "movl $-1, %%eax\n\t"
  111. "movl $-1, %%ebx\n\t"
  112. "movl $-1, %%ecx\n\t"
  113. "movl $-1, %%edx\n\t"
  114. "movl $-1, %%esi\n\t"
  115. "movl $-1, %%edi\n\t"
  116. "movl $-1, %%ebp\n\t"
  117. "movl $-1, %%esp\n\t"
  118. "sysenter"
  119. : : : "memory", "flags");
  120. }
  121. printf("[RUN]\tSYSCALL with invalid state\n");
  122. if (sigsetjmp(jmpbuf, 1) == 0) {
  123. asm volatile (
  124. "movl $-1, %%eax\n\t"
  125. "movl $-1, %%ebx\n\t"
  126. "movl $-1, %%ecx\n\t"
  127. "movl $-1, %%edx\n\t"
  128. "movl $-1, %%esi\n\t"
  129. "movl $-1, %%edi\n\t"
  130. "movl $-1, %%ebp\n\t"
  131. "movl $-1, %%esp\n\t"
  132. "syscall\n\t"
  133. "ud2" /* make sure we recover cleanly */
  134. : : : "memory", "flags");
  135. }
  136. printf("[RUN]\tSYSENTER with TF and invalid state\n");
  137. sethandler(SIGTRAP, sigtrap, SA_ONSTACK);
  138. if (sigsetjmp(jmpbuf, 1) == 0) {
  139. sigtrap_consecutive_syscalls = 0;
  140. set_eflags(get_eflags() | X86_EFLAGS_TF);
  141. asm volatile (
  142. "movl $-1, %%eax\n\t"
  143. "movl $-1, %%ebx\n\t"
  144. "movl $-1, %%ecx\n\t"
  145. "movl $-1, %%edx\n\t"
  146. "movl $-1, %%esi\n\t"
  147. "movl $-1, %%edi\n\t"
  148. "movl $-1, %%ebp\n\t"
  149. "movl $-1, %%esp\n\t"
  150. "sysenter"
  151. : : : "memory", "flags");
  152. }
  153. set_eflags(get_eflags() & ~X86_EFLAGS_TF);
  154. printf("[RUN]\tSYSCALL with TF and invalid state\n");
  155. if (sigsetjmp(jmpbuf, 1) == 0) {
  156. sigtrap_consecutive_syscalls = 0;
  157. set_eflags(get_eflags() | X86_EFLAGS_TF);
  158. asm volatile (
  159. "movl $-1, %%eax\n\t"
  160. "movl $-1, %%ebx\n\t"
  161. "movl $-1, %%ecx\n\t"
  162. "movl $-1, %%edx\n\t"
  163. "movl $-1, %%esi\n\t"
  164. "movl $-1, %%edi\n\t"
  165. "movl $-1, %%ebp\n\t"
  166. "movl $-1, %%esp\n\t"
  167. "syscall\n\t"
  168. "ud2" /* make sure we recover cleanly */
  169. : : : "memory", "flags");
  170. }
  171. set_eflags(get_eflags() & ~X86_EFLAGS_TF);
  172. #ifdef __x86_64__
  173. printf("[RUN]\tSYSENTER with TF, invalid state, and GSBASE < 0\n");
  174. if (sigsetjmp(jmpbuf, 1) == 0) {
  175. sigtrap_consecutive_syscalls = 0;
  176. asm volatile ("wrgsbase %%rax\n\t"
  177. :: "a" (0xffffffffffff0000UL));
  178. set_eflags(get_eflags() | X86_EFLAGS_TF);
  179. asm volatile (
  180. "movl $-1, %%eax\n\t"
  181. "movl $-1, %%ebx\n\t"
  182. "movl $-1, %%ecx\n\t"
  183. "movl $-1, %%edx\n\t"
  184. "movl $-1, %%esi\n\t"
  185. "movl $-1, %%edi\n\t"
  186. "movl $-1, %%ebp\n\t"
  187. "movl $-1, %%esp\n\t"
  188. "sysenter"
  189. : : : "memory", "flags");
  190. }
  191. set_eflags(get_eflags() & ~X86_EFLAGS_TF);
  192. #endif
  193. free(stack.ss_sp);
  194. return 0;
  195. }