syscall_arg_fault.c 6.1 KB

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