diff options
Diffstat (limited to 'tools/testing/selftests/x86/syscall_arg_fault.c')
-rw-r--r-- | tools/testing/selftests/x86/syscall_arg_fault.c | 112 |
1 files changed, 107 insertions, 5 deletions
diff --git a/tools/testing/selftests/x86/syscall_arg_fault.c b/tools/testing/selftests/x86/syscall_arg_fault.c index 4e25d38c8bbd..bc0ecc2e862e 100644 --- a/tools/testing/selftests/x86/syscall_arg_fault.c +++ b/tools/testing/selftests/x86/syscall_arg_fault.c | |||
@@ -15,9 +15,30 @@ | |||
15 | #include <setjmp.h> | 15 | #include <setjmp.h> |
16 | #include <errno.h> | 16 | #include <errno.h> |
17 | 17 | ||
18 | #ifdef __x86_64__ | ||
19 | # define WIDTH "q" | ||
20 | #else | ||
21 | # define WIDTH "l" | ||
22 | #endif | ||
23 | |||
18 | /* Our sigaltstack scratch space. */ | 24 | /* Our sigaltstack scratch space. */ |
19 | static unsigned char altstack_data[SIGSTKSZ]; | 25 | static unsigned char altstack_data[SIGSTKSZ]; |
20 | 26 | ||
27 | static unsigned long get_eflags(void) | ||
28 | { | ||
29 | unsigned long eflags; | ||
30 | asm volatile ("pushf" WIDTH "\n\tpop" WIDTH " %0" : "=rm" (eflags)); | ||
31 | return eflags; | ||
32 | } | ||
33 | |||
34 | static void set_eflags(unsigned long eflags) | ||
35 | { | ||
36 | asm volatile ("push" WIDTH " %0\n\tpopf" WIDTH | ||
37 | : : "rm" (eflags) : "flags"); | ||
38 | } | ||
39 | |||
40 | #define X86_EFLAGS_TF (1UL << 8) | ||
41 | |||
21 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), | 42 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), |
22 | int flags) | 43 | int flags) |
23 | { | 44 | { |
@@ -35,13 +56,22 @@ static sigjmp_buf jmpbuf; | |||
35 | 56 | ||
36 | static volatile sig_atomic_t n_errs; | 57 | static volatile sig_atomic_t n_errs; |
37 | 58 | ||
59 | #ifdef __x86_64__ | ||
60 | #define REG_AX REG_RAX | ||
61 | #define REG_IP REG_RIP | ||
62 | #else | ||
63 | #define REG_AX REG_EAX | ||
64 | #define REG_IP REG_EIP | ||
65 | #endif | ||
66 | |||
38 | static void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void) | 67 | static void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void) |
39 | { | 68 | { |
40 | ucontext_t *ctx = (ucontext_t*)ctx_void; | 69 | ucontext_t *ctx = (ucontext_t*)ctx_void; |
70 | long ax = (long)ctx->uc_mcontext.gregs[REG_AX]; | ||
41 | 71 | ||
42 | if (ctx->uc_mcontext.gregs[REG_EAX] != -EFAULT) { | 72 | if (ax != -EFAULT && ax != -ENOSYS) { |
43 | printf("[FAIL]\tAX had the wrong value: 0x%x\n", | 73 | printf("[FAIL]\tAX had the wrong value: 0x%lx\n", |
44 | ctx->uc_mcontext.gregs[REG_EAX]); | 74 | (unsigned long)ax); |
45 | n_errs++; | 75 | n_errs++; |
46 | } else { | 76 | } else { |
47 | printf("[OK]\tSeems okay\n"); | 77 | printf("[OK]\tSeems okay\n"); |
@@ -50,9 +80,42 @@ static void sigsegv_or_sigbus(int sig, siginfo_t *info, void *ctx_void) | |||
50 | siglongjmp(jmpbuf, 1); | 80 | siglongjmp(jmpbuf, 1); |
51 | } | 81 | } |
52 | 82 | ||
83 | static volatile sig_atomic_t sigtrap_consecutive_syscalls; | ||
84 | |||
85 | static void sigtrap(int sig, siginfo_t *info, void *ctx_void) | ||
86 | { | ||
87 | /* | ||
88 | * KVM has some bugs that can cause us to stop making progress. | ||
89 | * detect them and complain, but don't infinite loop or fail the | ||
90 | * test. | ||
91 | */ | ||
92 | |||
93 | ucontext_t *ctx = (ucontext_t*)ctx_void; | ||
94 | unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP]; | ||
95 | |||
96 | if (*ip == 0x340f || *ip == 0x050f) { | ||
97 | /* The trap was on SYSCALL or SYSENTER */ | ||
98 | sigtrap_consecutive_syscalls++; | ||
99 | if (sigtrap_consecutive_syscalls > 3) { | ||
100 | printf("[WARN]\tGot stuck single-stepping -- you probably have a KVM bug\n"); | ||
101 | siglongjmp(jmpbuf, 1); | ||
102 | } | ||
103 | } else { | ||
104 | sigtrap_consecutive_syscalls = 0; | ||
105 | } | ||
106 | } | ||
107 | |||
53 | static void sigill(int sig, siginfo_t *info, void *ctx_void) | 108 | static void sigill(int sig, siginfo_t *info, void *ctx_void) |
54 | { | 109 | { |
55 | printf("[SKIP]\tIllegal instruction\n"); | 110 | ucontext_t *ctx = (ucontext_t*)ctx_void; |
111 | unsigned short *ip = (unsigned short *)ctx->uc_mcontext.gregs[REG_IP]; | ||
112 | |||
113 | if (*ip == 0x0b0f) { | ||
114 | /* one of the ud2 instructions faulted */ | ||
115 | printf("[OK]\tSYSCALL returned normally\n"); | ||
116 | } else { | ||
117 | printf("[SKIP]\tIllegal instruction\n"); | ||
118 | } | ||
56 | siglongjmp(jmpbuf, 1); | 119 | siglongjmp(jmpbuf, 1); |
57 | } | 120 | } |
58 | 121 | ||
@@ -120,9 +183,48 @@ int main() | |||
120 | "movl $-1, %%ebp\n\t" | 183 | "movl $-1, %%ebp\n\t" |
121 | "movl $-1, %%esp\n\t" | 184 | "movl $-1, %%esp\n\t" |
122 | "syscall\n\t" | 185 | "syscall\n\t" |
123 | "pushl $0" /* make sure we segfault cleanly */ | 186 | "ud2" /* make sure we recover cleanly */ |
187 | : : : "memory", "flags"); | ||
188 | } | ||
189 | |||
190 | printf("[RUN]\tSYSENTER with TF and invalid state\n"); | ||
191 | sethandler(SIGTRAP, sigtrap, SA_ONSTACK); | ||
192 | |||
193 | if (sigsetjmp(jmpbuf, 1) == 0) { | ||
194 | sigtrap_consecutive_syscalls = 0; | ||
195 | set_eflags(get_eflags() | X86_EFLAGS_TF); | ||
196 | asm volatile ( | ||
197 | "movl $-1, %%eax\n\t" | ||
198 | "movl $-1, %%ebx\n\t" | ||
199 | "movl $-1, %%ecx\n\t" | ||
200 | "movl $-1, %%edx\n\t" | ||
201 | "movl $-1, %%esi\n\t" | ||
202 | "movl $-1, %%edi\n\t" | ||
203 | "movl $-1, %%ebp\n\t" | ||
204 | "movl $-1, %%esp\n\t" | ||
205 | "sysenter" | ||
206 | : : : "memory", "flags"); | ||
207 | } | ||
208 | set_eflags(get_eflags() & ~X86_EFLAGS_TF); | ||
209 | |||
210 | printf("[RUN]\tSYSCALL with TF and invalid state\n"); | ||
211 | if (sigsetjmp(jmpbuf, 1) == 0) { | ||
212 | sigtrap_consecutive_syscalls = 0; | ||
213 | set_eflags(get_eflags() | X86_EFLAGS_TF); | ||
214 | asm volatile ( | ||
215 | "movl $-1, %%eax\n\t" | ||
216 | "movl $-1, %%ebx\n\t" | ||
217 | "movl $-1, %%ecx\n\t" | ||
218 | "movl $-1, %%edx\n\t" | ||
219 | "movl $-1, %%esi\n\t" | ||
220 | "movl $-1, %%edi\n\t" | ||
221 | "movl $-1, %%ebp\n\t" | ||
222 | "movl $-1, %%esp\n\t" | ||
223 | "syscall\n\t" | ||
224 | "ud2" /* make sure we recover cleanly */ | ||
124 | : : : "memory", "flags"); | 225 | : : : "memory", "flags"); |
125 | } | 226 | } |
227 | set_eflags(get_eflags() & ~X86_EFLAGS_TF); | ||
126 | 228 | ||
127 | return 0; | 229 | return 0; |
128 | } | 230 | } |