Commit | Line | Data |
---|---|---|
2025cf9e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
5e5c684a AL |
2 | /* |
3 | * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args | |
4 | * Copyright (c) 2015 Andrew Lutomirski | |
5e5c684a AL |
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 | /* Our sigaltstack scratch space. */ | |
19 | static unsigned char altstack_data[SIGSTKSZ]; | |
20 | ||
21 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), | |
22 | int flags) | |
23 | { | |
24 | struct sigaction sa; | |
25 | memset(&sa, 0, sizeof(sa)); | |
26 | sa.sa_sigaction = handler; | |
27 | sa.sa_flags = SA_SIGINFO | flags; | |
28 | sigemptyset(&sa.sa_mask); | |
29 | if (sigaction(sig, &sa, 0)) | |
30 | err(1, "sigaction"); | |
31 | } | |
32 | ||
33 | static volatile sig_atomic_t sig_traps; | |
34 | static sigjmp_buf jmpbuf; | |
35 | ||
36 | static volatile sig_atomic_t n_errs; | |
37 | ||
a20d452a | 38 | static void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void) |
5e5c684a AL |
39 | { |
40 | ucontext_t *ctx = (ucontext_t*)ctx_void; | |
41 | ||
42 | if (ctx->uc_mcontext.gregs[REG_EAX] != -EFAULT) { | |
43 | printf("[FAIL]\tAX had the wrong value: 0x%x\n", | |
44 | ctx->uc_mcontext.gregs[REG_EAX]); | |
45 | n_errs++; | |
46 | } else { | |
47 | printf("[OK]\tSeems okay\n"); | |
48 | } | |
49 | ||
50 | siglongjmp(jmpbuf, 1); | |
51 | } | |
52 | ||
53 | static void sigill(int sig, siginfo_t *info, void *ctx_void) | |
54 | { | |
55 | printf("[SKIP]\tIllegal instruction\n"); | |
56 | siglongjmp(jmpbuf, 1); | |
57 | } | |
58 | ||
59 | int main() | |
60 | { | |
61 | stack_t stack = { | |
62 | .ss_sp = altstack_data, | |
63 | .ss_size = SIGSTKSZ, | |
64 | }; | |
65 | if (sigaltstack(&stack, NULL) != 0) | |
66 | err(1, "sigaltstack"); | |
67 | ||
a20d452a TB |
68 | sethandler(SIGSEGV, sigsegv_or_sigbus, SA_ONSTACK); |
69 | /* | |
70 | * The actual exception can vary. On Atom CPUs, we get #SS | |
71 | * instead of #PF when the vDSO fails to access the stack when | |
72 | * ESP is too close to 2^32, and #SS causes SIGBUS. | |
73 | */ | |
74 | sethandler(SIGBUS, sigsegv_or_sigbus, SA_ONSTACK); | |
5e5c684a AL |
75 | sethandler(SIGILL, sigill, SA_ONSTACK); |
76 | ||
77 | /* | |
78 | * Exercise another nasty special case. The 32-bit SYSCALL | |
79 | * and SYSENTER instructions (even in compat mode) each | |
80 | * clobber one register. A Linux system call has a syscall | |
81 | * number and six arguments, and the user stack pointer | |
82 | * needs to live in some register on return. That means | |
83 | * that we need eight registers, but SYSCALL and SYSENTER | |
84 | * only preserve seven registers. As a result, one argument | |
85 | * ends up on the stack. The stack is user memory, which | |
86 | * means that the kernel can fail to read it. | |
87 | * | |
88 | * The 32-bit fast system calls don't have a defined ABI: | |
89 | * we're supposed to invoke them through the vDSO. So we'll | |
90 | * fudge it: we set all regs to invalid pointer values and | |
91 | * invoke the entry instruction. The return will fail no | |
92 | * matter what, and we completely lose our program state, | |
93 | * but we can fix it up with a signal handler. | |
94 | */ | |
95 | ||
96 | printf("[RUN]\tSYSENTER with invalid state\n"); | |
97 | if (sigsetjmp(jmpbuf, 1) == 0) { | |
98 | asm volatile ( | |
99 | "movl $-1, %%eax\n\t" | |
100 | "movl $-1, %%ebx\n\t" | |
101 | "movl $-1, %%ecx\n\t" | |
102 | "movl $-1, %%edx\n\t" | |
103 | "movl $-1, %%esi\n\t" | |
104 | "movl $-1, %%edi\n\t" | |
105 | "movl $-1, %%ebp\n\t" | |
106 | "movl $-1, %%esp\n\t" | |
107 | "sysenter" | |
108 | : : : "memory", "flags"); | |
109 | } | |
110 | ||
111 | printf("[RUN]\tSYSCALL with invalid state\n"); | |
112 | if (sigsetjmp(jmpbuf, 1) == 0) { | |
113 | asm volatile ( | |
114 | "movl $-1, %%eax\n\t" | |
115 | "movl $-1, %%ebx\n\t" | |
116 | "movl $-1, %%ecx\n\t" | |
117 | "movl $-1, %%edx\n\t" | |
118 | "movl $-1, %%esi\n\t" | |
119 | "movl $-1, %%edi\n\t" | |
120 | "movl $-1, %%ebp\n\t" | |
121 | "movl $-1, %%esp\n\t" | |
122 | "syscall\n\t" | |
123 | "pushl $0" /* make sure we segfault cleanly */ | |
124 | : : : "memory", "flags"); | |
125 | } | |
126 | ||
127 | return 0; | |
128 | } |