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