162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args 462306a36Sopenharmony_ci * Copyright (c) 2015 Andrew Lutomirski 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define _GNU_SOURCE 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <stdlib.h> 1062306a36Sopenharmony_ci#include <stdio.h> 1162306a36Sopenharmony_ci#include <string.h> 1262306a36Sopenharmony_ci#include <sys/signal.h> 1362306a36Sopenharmony_ci#include <sys/ucontext.h> 1462306a36Sopenharmony_ci#include <err.h> 1562306a36Sopenharmony_ci#include <setjmp.h> 1662306a36Sopenharmony_ci#include <errno.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "helpers.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 2162306a36Sopenharmony_ci int flags) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct sigaction sa; 2462306a36Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 2562306a36Sopenharmony_ci sa.sa_sigaction = handler; 2662306a36Sopenharmony_ci sa.sa_flags = SA_SIGINFO | flags; 2762306a36Sopenharmony_ci sigemptyset(&sa.sa_mask); 2862306a36Sopenharmony_ci if (sigaction(sig, &sa, 0)) 2962306a36Sopenharmony_ci err(1, "sigaction"); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic volatile sig_atomic_t sig_traps; 3362306a36Sopenharmony_cistatic sigjmp_buf jmpbuf; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic volatile sig_atomic_t n_errs; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#ifdef __x86_64__ 3862306a36Sopenharmony_ci#define REG_AX REG_RAX 3962306a36Sopenharmony_ci#define REG_IP REG_RIP 4062306a36Sopenharmony_ci#else 4162306a36Sopenharmony_ci#define REG_AX REG_EAX 4262306a36Sopenharmony_ci#define REG_IP REG_EIP 4362306a36Sopenharmony_ci#endif 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci ucontext_t *ctx = (ucontext_t*)ctx_void; 4862306a36Sopenharmony_ci long ax = (long)ctx->uc_mcontext.gregs[REG_AX]; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (ax != -EFAULT && ax != -ENOSYS) { 5162306a36Sopenharmony_ci printf("[FAIL]\tAX had the wrong value: 0x%lx\n", 5262306a36Sopenharmony_ci (unsigned long)ax); 5362306a36Sopenharmony_ci printf("\tIP = 0x%lx\n", (unsigned long)ctx->uc_mcontext.gregs[REG_IP]); 5462306a36Sopenharmony_ci n_errs++; 5562306a36Sopenharmony_ci } else { 5662306a36Sopenharmony_ci printf("[OK]\tSeems okay\n"); 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci siglongjmp(jmpbuf, 1); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic volatile sig_atomic_t sigtrap_consecutive_syscalls; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void sigtrap(int sig, siginfo_t *info, void *ctx_void) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci /* 6762306a36Sopenharmony_ci * KVM has some bugs that can cause us to stop making progress. 6862306a36Sopenharmony_ci * detect them and complain, but don't infinite loop or fail the 6962306a36Sopenharmony_ci * test. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci ucontext_t *ctx = (ucontext_t*)ctx_void; 7362306a36Sopenharmony_ci unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP]; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (*ip == 0x340f || *ip == 0x050f) { 7662306a36Sopenharmony_ci /* The trap was on SYSCALL or SYSENTER */ 7762306a36Sopenharmony_ci sigtrap_consecutive_syscalls++; 7862306a36Sopenharmony_ci if (sigtrap_consecutive_syscalls > 3) { 7962306a36Sopenharmony_ci printf("[WARN]\tGot stuck single-stepping -- you probably have a KVM bug\n"); 8062306a36Sopenharmony_ci siglongjmp(jmpbuf, 1); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci } else { 8362306a36Sopenharmony_ci sigtrap_consecutive_syscalls = 0; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void sigill(int sig, siginfo_t *info, void *ctx_void) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci ucontext_t *ctx = (ucontext_t*)ctx_void; 9062306a36Sopenharmony_ci unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP]; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (*ip == 0x0b0f) { 9362306a36Sopenharmony_ci /* one of the ud2 instructions faulted */ 9462306a36Sopenharmony_ci printf("[OK]\tSYSCALL returned normally\n"); 9562306a36Sopenharmony_ci } else { 9662306a36Sopenharmony_ci printf("[SKIP]\tIllegal instruction\n"); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci siglongjmp(jmpbuf, 1); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ciint main() 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci stack_t stack = { 10462306a36Sopenharmony_ci /* Our sigaltstack scratch space. */ 10562306a36Sopenharmony_ci .ss_sp = malloc(sizeof(char) * SIGSTKSZ), 10662306a36Sopenharmony_ci .ss_size = SIGSTKSZ, 10762306a36Sopenharmony_ci }; 10862306a36Sopenharmony_ci if (sigaltstack(&stack, NULL) != 0) 10962306a36Sopenharmony_ci err(1, "sigaltstack"); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci sethandler(SIGSEGV, sigsegv_or_sigbus, SA_ONSTACK); 11262306a36Sopenharmony_ci /* 11362306a36Sopenharmony_ci * The actual exception can vary. On Atom CPUs, we get #SS 11462306a36Sopenharmony_ci * instead of #PF when the vDSO fails to access the stack when 11562306a36Sopenharmony_ci * ESP is too close to 2^32, and #SS causes SIGBUS. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci sethandler(SIGBUS, sigsegv_or_sigbus, SA_ONSTACK); 11862306a36Sopenharmony_ci sethandler(SIGILL, sigill, SA_ONSTACK); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * Exercise another nasty special case. The 32-bit SYSCALL 12262306a36Sopenharmony_ci * and SYSENTER instructions (even in compat mode) each 12362306a36Sopenharmony_ci * clobber one register. A Linux system call has a syscall 12462306a36Sopenharmony_ci * number and six arguments, and the user stack pointer 12562306a36Sopenharmony_ci * needs to live in some register on return. That means 12662306a36Sopenharmony_ci * that we need eight registers, but SYSCALL and SYSENTER 12762306a36Sopenharmony_ci * only preserve seven registers. As a result, one argument 12862306a36Sopenharmony_ci * ends up on the stack. The stack is user memory, which 12962306a36Sopenharmony_ci * means that the kernel can fail to read it. 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * The 32-bit fast system calls don't have a defined ABI: 13262306a36Sopenharmony_ci * we're supposed to invoke them through the vDSO. So we'll 13362306a36Sopenharmony_ci * fudge it: we set all regs to invalid pointer values and 13462306a36Sopenharmony_ci * invoke the entry instruction. The return will fail no 13562306a36Sopenharmony_ci * matter what, and we completely lose our program state, 13662306a36Sopenharmony_ci * but we can fix it up with a signal handler. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci printf("[RUN]\tSYSENTER with invalid state\n"); 14062306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 14162306a36Sopenharmony_ci asm volatile ( 14262306a36Sopenharmony_ci "movl $-1, %%eax\n\t" 14362306a36Sopenharmony_ci "movl $-1, %%ebx\n\t" 14462306a36Sopenharmony_ci "movl $-1, %%ecx\n\t" 14562306a36Sopenharmony_ci "movl $-1, %%edx\n\t" 14662306a36Sopenharmony_ci "movl $-1, %%esi\n\t" 14762306a36Sopenharmony_ci "movl $-1, %%edi\n\t" 14862306a36Sopenharmony_ci "movl $-1, %%ebp\n\t" 14962306a36Sopenharmony_ci "movl $-1, %%esp\n\t" 15062306a36Sopenharmony_ci "sysenter" 15162306a36Sopenharmony_ci : : : "memory", "flags"); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci printf("[RUN]\tSYSCALL with invalid state\n"); 15562306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 15662306a36Sopenharmony_ci asm volatile ( 15762306a36Sopenharmony_ci "movl $-1, %%eax\n\t" 15862306a36Sopenharmony_ci "movl $-1, %%ebx\n\t" 15962306a36Sopenharmony_ci "movl $-1, %%ecx\n\t" 16062306a36Sopenharmony_ci "movl $-1, %%edx\n\t" 16162306a36Sopenharmony_ci "movl $-1, %%esi\n\t" 16262306a36Sopenharmony_ci "movl $-1, %%edi\n\t" 16362306a36Sopenharmony_ci "movl $-1, %%ebp\n\t" 16462306a36Sopenharmony_ci "movl $-1, %%esp\n\t" 16562306a36Sopenharmony_ci "syscall\n\t" 16662306a36Sopenharmony_ci "ud2" /* make sure we recover cleanly */ 16762306a36Sopenharmony_ci : : : "memory", "flags"); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci printf("[RUN]\tSYSENTER with TF and invalid state\n"); 17162306a36Sopenharmony_ci sethandler(SIGTRAP, sigtrap, SA_ONSTACK); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 17462306a36Sopenharmony_ci sigtrap_consecutive_syscalls = 0; 17562306a36Sopenharmony_ci set_eflags(get_eflags() | X86_EFLAGS_TF); 17662306a36Sopenharmony_ci asm volatile ( 17762306a36Sopenharmony_ci "movl $-1, %%eax\n\t" 17862306a36Sopenharmony_ci "movl $-1, %%ebx\n\t" 17962306a36Sopenharmony_ci "movl $-1, %%ecx\n\t" 18062306a36Sopenharmony_ci "movl $-1, %%edx\n\t" 18162306a36Sopenharmony_ci "movl $-1, %%esi\n\t" 18262306a36Sopenharmony_ci "movl $-1, %%edi\n\t" 18362306a36Sopenharmony_ci "movl $-1, %%ebp\n\t" 18462306a36Sopenharmony_ci "movl $-1, %%esp\n\t" 18562306a36Sopenharmony_ci "sysenter" 18662306a36Sopenharmony_ci : : : "memory", "flags"); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci set_eflags(get_eflags() & ~X86_EFLAGS_TF); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci printf("[RUN]\tSYSCALL with TF and invalid state\n"); 19162306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 19262306a36Sopenharmony_ci sigtrap_consecutive_syscalls = 0; 19362306a36Sopenharmony_ci set_eflags(get_eflags() | X86_EFLAGS_TF); 19462306a36Sopenharmony_ci asm volatile ( 19562306a36Sopenharmony_ci "movl $-1, %%eax\n\t" 19662306a36Sopenharmony_ci "movl $-1, %%ebx\n\t" 19762306a36Sopenharmony_ci "movl $-1, %%ecx\n\t" 19862306a36Sopenharmony_ci "movl $-1, %%edx\n\t" 19962306a36Sopenharmony_ci "movl $-1, %%esi\n\t" 20062306a36Sopenharmony_ci "movl $-1, %%edi\n\t" 20162306a36Sopenharmony_ci "movl $-1, %%ebp\n\t" 20262306a36Sopenharmony_ci "movl $-1, %%esp\n\t" 20362306a36Sopenharmony_ci "syscall\n\t" 20462306a36Sopenharmony_ci "ud2" /* make sure we recover cleanly */ 20562306a36Sopenharmony_ci : : : "memory", "flags"); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci set_eflags(get_eflags() & ~X86_EFLAGS_TF); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci#ifdef __x86_64__ 21062306a36Sopenharmony_ci printf("[RUN]\tSYSENTER with TF, invalid state, and GSBASE < 0\n"); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (sigsetjmp(jmpbuf, 1) == 0) { 21362306a36Sopenharmony_ci sigtrap_consecutive_syscalls = 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci asm volatile ("wrgsbase %%rax\n\t" 21662306a36Sopenharmony_ci :: "a" (0xffffffffffff0000UL)); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci set_eflags(get_eflags() | X86_EFLAGS_TF); 21962306a36Sopenharmony_ci asm volatile ( 22062306a36Sopenharmony_ci "movl $-1, %%eax\n\t" 22162306a36Sopenharmony_ci "movl $-1, %%ebx\n\t" 22262306a36Sopenharmony_ci "movl $-1, %%ecx\n\t" 22362306a36Sopenharmony_ci "movl $-1, %%edx\n\t" 22462306a36Sopenharmony_ci "movl $-1, %%esi\n\t" 22562306a36Sopenharmony_ci "movl $-1, %%edi\n\t" 22662306a36Sopenharmony_ci "movl $-1, %%ebp\n\t" 22762306a36Sopenharmony_ci "movl $-1, %%esp\n\t" 22862306a36Sopenharmony_ci "sysenter" 22962306a36Sopenharmony_ci : : : "memory", "flags"); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci set_eflags(get_eflags() & ~X86_EFLAGS_TF); 23262306a36Sopenharmony_ci#endif 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci free(stack.ss_sp); 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci} 237