Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.15-rc2 223 lines 5.9 kB view raw
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 20static sigjmp_buf jmpbuf; 21 22static 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 32static 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 49static volatile sig_atomic_t sigtrap_consecutive_syscalls; 50 51static 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 74static 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 88int 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}