236 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args4 * Copyright (c) 2015 Andrew Lutomirski5 */6 7#define _GNU_SOURCE8 9#include <stdlib.h>10#include <stdio.h>11#include <string.h>12#include <sys/signal.h>13#include <sys/ucontext.h>14#include <err.h>15#include <setjmp.h>16#include <errno.h>17 18#include "helpers.h"19 20static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),21 int flags)22{23 struct sigaction sa;24 memset(&sa, 0, sizeof(sa));25 sa.sa_sigaction = handler;26 sa.sa_flags = SA_SIGINFO | flags;27 sigemptyset(&sa.sa_mask);28 if (sigaction(sig, &sa, 0))29 err(1, "sigaction");30}31 32static sigjmp_buf jmpbuf;33 34static volatile sig_atomic_t n_errs;35 36#ifdef __x86_64__37#define REG_AX REG_RAX38#define REG_IP REG_RIP39#else40#define REG_AX REG_EAX41#define REG_IP REG_EIP42#endif43 44static void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void)45{46 ucontext_t *ctx = (ucontext_t*)ctx_void;47 long ax = (long)ctx->uc_mcontext.gregs[REG_AX];48 49 if (ax != -EFAULT && ax != -ENOSYS) {50 printf("[FAIL]\tAX had the wrong value: 0x%lx\n",51 (unsigned long)ax);52 printf("\tIP = 0x%lx\n", (unsigned long)ctx->uc_mcontext.gregs[REG_IP]);53 n_errs++;54 } else {55 printf("[OK]\tSeems okay\n");56 }57 58 siglongjmp(jmpbuf, 1);59}60 61static volatile sig_atomic_t sigtrap_consecutive_syscalls;62 63static void sigtrap(int sig, siginfo_t *info, void *ctx_void)64{65 /*66 * KVM has some bugs that can cause us to stop making progress.67 * detect them and complain, but don't infinite loop or fail the68 * test.69 */70 71 ucontext_t *ctx = (ucontext_t*)ctx_void;72 unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];73 74 if (*ip == 0x340f || *ip == 0x050f) {75 /* The trap was on SYSCALL or SYSENTER */76 sigtrap_consecutive_syscalls++;77 if (sigtrap_consecutive_syscalls > 3) {78 printf("[WARN]\tGot stuck single-stepping -- you probably have a KVM bug\n");79 siglongjmp(jmpbuf, 1);80 }81 } else {82 sigtrap_consecutive_syscalls = 0;83 }84}85 86static void sigill(int sig, siginfo_t *info, void *ctx_void)87{88 ucontext_t *ctx = (ucontext_t*)ctx_void;89 unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP];90 91 if (*ip == 0x0b0f) {92 /* one of the ud2 instructions faulted */93 printf("[OK]\tSYSCALL returned normally\n");94 } else {95 printf("[SKIP]\tIllegal instruction\n");96 }97 siglongjmp(jmpbuf, 1);98}99 100int main()101{102 stack_t stack = {103 /* Our sigaltstack scratch space. */104 .ss_sp = malloc(sizeof(char) * SIGSTKSZ),105 .ss_size = SIGSTKSZ,106 };107 if (sigaltstack(&stack, NULL) != 0)108 err(1, "sigaltstack");109 110 sethandler(SIGSEGV, sigsegv_or_sigbus, SA_ONSTACK);111 /*112 * The actual exception can vary. On Atom CPUs, we get #SS113 * instead of #PF when the vDSO fails to access the stack when114 * ESP is too close to 2^32, and #SS causes SIGBUS.115 */116 sethandler(SIGBUS, sigsegv_or_sigbus, SA_ONSTACK);117 sethandler(SIGILL, sigill, SA_ONSTACK);118 119 /*120 * Exercise another nasty special case. The 32-bit SYSCALL121 * and SYSENTER instructions (even in compat mode) each122 * clobber one register. A Linux system call has a syscall123 * number and six arguments, and the user stack pointer124 * needs to live in some register on return. That means125 * that we need eight registers, but SYSCALL and SYSENTER126 * only preserve seven registers. As a result, one argument127 * ends up on the stack. The stack is user memory, which128 * means that the kernel can fail to read it.129 *130 * The 32-bit fast system calls don't have a defined ABI:131 * we're supposed to invoke them through the vDSO. So we'll132 * fudge it: we set all regs to invalid pointer values and133 * invoke the entry instruction. The return will fail no134 * matter what, and we completely lose our program state,135 * but we can fix it up with a signal handler.136 */137 138 printf("[RUN]\tSYSENTER with invalid state\n");139 if (sigsetjmp(jmpbuf, 1) == 0) {140 asm volatile (141 "movl $-1, %%eax\n\t"142 "movl $-1, %%ebx\n\t"143 "movl $-1, %%ecx\n\t"144 "movl $-1, %%edx\n\t"145 "movl $-1, %%esi\n\t"146 "movl $-1, %%edi\n\t"147 "movl $-1, %%ebp\n\t"148 "movl $-1, %%esp\n\t"149 "sysenter"150 : : : "memory", "flags");151 }152 153 printf("[RUN]\tSYSCALL with invalid state\n");154 if (sigsetjmp(jmpbuf, 1) == 0) {155 asm volatile (156 "movl $-1, %%eax\n\t"157 "movl $-1, %%ebx\n\t"158 "movl $-1, %%ecx\n\t"159 "movl $-1, %%edx\n\t"160 "movl $-1, %%esi\n\t"161 "movl $-1, %%edi\n\t"162 "movl $-1, %%ebp\n\t"163 "movl $-1, %%esp\n\t"164 "syscall\n\t"165 "ud2" /* make sure we recover cleanly */166 : : : "memory", "flags");167 }168 169 printf("[RUN]\tSYSENTER with TF and invalid state\n");170 sethandler(SIGTRAP, sigtrap, SA_ONSTACK);171 172 if (sigsetjmp(jmpbuf, 1) == 0) {173 sigtrap_consecutive_syscalls = 0;174 set_eflags(get_eflags() | X86_EFLAGS_TF);175 asm volatile (176 "movl $-1, %%eax\n\t"177 "movl $-1, %%ebx\n\t"178 "movl $-1, %%ecx\n\t"179 "movl $-1, %%edx\n\t"180 "movl $-1, %%esi\n\t"181 "movl $-1, %%edi\n\t"182 "movl $-1, %%ebp\n\t"183 "movl $-1, %%esp\n\t"184 "sysenter"185 : : : "memory", "flags");186 }187 set_eflags(get_eflags() & ~X86_EFLAGS_TF);188 189 printf("[RUN]\tSYSCALL with TF and invalid state\n");190 if (sigsetjmp(jmpbuf, 1) == 0) {191 sigtrap_consecutive_syscalls = 0;192 set_eflags(get_eflags() | X86_EFLAGS_TF);193 asm volatile (194 "movl $-1, %%eax\n\t"195 "movl $-1, %%ebx\n\t"196 "movl $-1, %%ecx\n\t"197 "movl $-1, %%edx\n\t"198 "movl $-1, %%esi\n\t"199 "movl $-1, %%edi\n\t"200 "movl $-1, %%ebp\n\t"201 "movl $-1, %%esp\n\t"202 "syscall\n\t"203 "ud2" /* make sure we recover cleanly */204 : : : "memory", "flags");205 }206 set_eflags(get_eflags() & ~X86_EFLAGS_TF);207 208#ifdef __x86_64__209 printf("[RUN]\tSYSENTER with TF, invalid state, and GSBASE < 0\n");210 211 if (sigsetjmp(jmpbuf, 1) == 0) {212 sigtrap_consecutive_syscalls = 0;213 214 asm volatile ("wrgsbase %%rax\n\t"215 :: "a" (0xffffffffffff0000UL));216 217 set_eflags(get_eflags() | X86_EFLAGS_TF);218 asm volatile (219 "movl $-1, %%eax\n\t"220 "movl $-1, %%ebx\n\t"221 "movl $-1, %%ecx\n\t"222 "movl $-1, %%edx\n\t"223 "movl $-1, %%esi\n\t"224 "movl $-1, %%edi\n\t"225 "movl $-1, %%ebp\n\t"226 "movl $-1, %%esp\n\t"227 "sysenter"228 : : : "memory", "flags");229 }230 set_eflags(get_eflags() & ~X86_EFLAGS_TF);231#endif232 233 free(stack.ss_sp);234 return 0;235}236