diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-15 12:32:27 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-15 12:32:27 -0400 |
commit | ba33ea811e1ff6726abb7f8f96df38c2d7b50304 (patch) | |
tree | 29134e5cc7c19c8e520cb9336b476144d3d1252f /tools | |
parent | e23604edac2a7be6a8808a5d13fac6b9df4eb9a8 (diff) | |
parent | d05004944206cbbf1c453e179768163731c7c6f1 (diff) |
Merge branch 'x86-asm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 asm updates from Ingo Molnar:
"This is another big update. Main changes are:
- lots of x86 system call (and other traps/exceptions) entry code
enhancements. In particular the complex parts of the 64-bit entry
code have been migrated to C code as well, and a number of dusty
corners have been refreshed. (Andy Lutomirski)
- vDSO special mapping robustification and general cleanups (Andy
Lutomirski)
- cpufeature refactoring, cleanups and speedups (Borislav Petkov)
- lots of other changes ..."
* 'x86-asm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (64 commits)
x86/cpufeature: Enable new AVX-512 features
x86/entry/traps: Show unhandled signal for i386 in do_trap()
x86/entry: Call enter_from_user_mode() with IRQs off
x86/entry/32: Change INT80 to be an interrupt gate
x86/entry: Improve system call entry comments
x86/entry: Remove TIF_SINGLESTEP entry work
x86/entry/32: Add and check a stack canary for the SYSENTER stack
x86/entry/32: Simplify and fix up the SYSENTER stack #DB/NMI fixup
x86/entry: Only allocate space for tss_struct::SYSENTER_stack if needed
x86/entry: Vastly simplify SYSENTER TF (single-step) handling
x86/entry/traps: Clear DR6 early in do_debug() and improve the comment
x86/entry/traps: Clear TIF_BLOCKSTEP on all debug exceptions
x86/entry/32: Restore FLAGS on SYSEXIT
x86/entry/32: Filter NT and speed up AC filtering in SYSENTER
x86/entry/compat: In SYSENTER, sink AC clearing below the existing FLAGS test
selftests/x86: In syscall_nt, test NT|TF as well
x86/asm-offsets: Remove PARAVIRT_enabled
x86/entry/32: Introduce and use X86_BUG_ESPFIX instead of paravirt_enabled
uprobes: __create_xol_area() must nullify xol_mapping.fault
x86/cpufeature: Create a new synthetic cpu capability for machine check recovery
...
Diffstat (limited to 'tools')
-rw-r--r-- | tools/testing/selftests/x86/Makefile | 17 | ||||
-rw-r--r-- | tools/testing/selftests/x86/check_initial_reg_state.c | 109 | ||||
-rw-r--r-- | tools/testing/selftests/x86/ptrace_syscall.c | 132 | ||||
-rw-r--r-- | tools/testing/selftests/x86/sigreturn.c | 230 | ||||
-rw-r--r-- | tools/testing/selftests/x86/syscall_nt.c | 57 |
5 files changed, 501 insertions, 44 deletions
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index d0c473f65850..d5ce7d7aae3e 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile | |||
@@ -4,15 +4,16 @@ include ../lib.mk | |||
4 | 4 | ||
5 | .PHONY: all all_32 all_64 warn_32bit_failure clean | 5 | .PHONY: all all_32 all_64 warn_32bit_failure clean |
6 | 6 | ||
7 | TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall | 7 | TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall \ |
8 | TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault sigreturn test_syscall_vdso unwind_vdso \ | 8 | check_initial_reg_state sigreturn ldt_gdt |
9 | TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ | ||
9 | test_FCMOV test_FCOMI test_FISTTP \ | 10 | test_FCMOV test_FCOMI test_FISTTP \ |
10 | ldt_gdt \ | ||
11 | vdso_restorer | 11 | vdso_restorer |
12 | 12 | ||
13 | TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) | 13 | TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) |
14 | TARGETS_C_64BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_64BIT_ONLY) | ||
14 | BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32) | 15 | BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32) |
15 | BINARIES_64 := $(TARGETS_C_BOTHBITS:%=%_64) | 16 | BINARIES_64 := $(TARGETS_C_64BIT_ALL:%=%_64) |
16 | 17 | ||
17 | CFLAGS := -O2 -g -std=gnu99 -pthread -Wall | 18 | CFLAGS := -O2 -g -std=gnu99 -pthread -Wall |
18 | 19 | ||
@@ -40,7 +41,7 @@ clean: | |||
40 | $(TARGETS_C_32BIT_ALL:%=%_32): %_32: %.c | 41 | $(TARGETS_C_32BIT_ALL:%=%_32): %_32: %.c |
41 | $(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl -lm | 42 | $(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl -lm |
42 | 43 | ||
43 | $(TARGETS_C_BOTHBITS:%=%_64): %_64: %.c | 44 | $(TARGETS_C_64BIT_ALL:%=%_64): %_64: %.c |
44 | $(CC) -m64 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl | 45 | $(CC) -m64 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl |
45 | 46 | ||
46 | # x86_64 users should be encouraged to install 32-bit libraries | 47 | # x86_64 users should be encouraged to install 32-bit libraries |
@@ -65,3 +66,9 @@ endif | |||
65 | sysret_ss_attrs_64: thunks.S | 66 | sysret_ss_attrs_64: thunks.S |
66 | ptrace_syscall_32: raw_syscall_helper_32.S | 67 | ptrace_syscall_32: raw_syscall_helper_32.S |
67 | test_syscall_vdso_32: thunks_32.S | 68 | test_syscall_vdso_32: thunks_32.S |
69 | |||
70 | # check_initial_reg_state is special: it needs a custom entry, and it | ||
71 | # needs to be static so that its interpreter doesn't destroy its initial | ||
72 | # state. | ||
73 | check_initial_reg_state_32: CFLAGS += -Wl,-ereal_start -static | ||
74 | check_initial_reg_state_64: CFLAGS += -Wl,-ereal_start -static | ||
diff --git a/tools/testing/selftests/x86/check_initial_reg_state.c b/tools/testing/selftests/x86/check_initial_reg_state.c new file mode 100644 index 000000000000..6aaed9b85baf --- /dev/null +++ b/tools/testing/selftests/x86/check_initial_reg_state.c | |||
@@ -0,0 +1,109 @@ | |||
1 | /* | ||
2 | * check_initial_reg_state.c - check that execve sets the correct state | ||
3 | * Copyright (c) 2014-2016 Andrew Lutomirski | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but | ||
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | * General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #define _GNU_SOURCE | ||
16 | |||
17 | #include <stdio.h> | ||
18 | |||
19 | unsigned long ax, bx, cx, dx, si, di, bp, sp, flags; | ||
20 | unsigned long r8, r9, r10, r11, r12, r13, r14, r15; | ||
21 | |||
22 | asm ( | ||
23 | ".pushsection .text\n\t" | ||
24 | ".type real_start, @function\n\t" | ||
25 | ".global real_start\n\t" | ||
26 | "real_start:\n\t" | ||
27 | #ifdef __x86_64__ | ||
28 | "mov %rax, ax\n\t" | ||
29 | "mov %rbx, bx\n\t" | ||
30 | "mov %rcx, cx\n\t" | ||
31 | "mov %rdx, dx\n\t" | ||
32 | "mov %rsi, si\n\t" | ||
33 | "mov %rdi, di\n\t" | ||
34 | "mov %rbp, bp\n\t" | ||
35 | "mov %rsp, sp\n\t" | ||
36 | "mov %r8, r8\n\t" | ||
37 | "mov %r9, r9\n\t" | ||
38 | "mov %r10, r10\n\t" | ||
39 | "mov %r11, r11\n\t" | ||
40 | "mov %r12, r12\n\t" | ||
41 | "mov %r13, r13\n\t" | ||
42 | "mov %r14, r14\n\t" | ||
43 | "mov %r15, r15\n\t" | ||
44 | "pushfq\n\t" | ||
45 | "popq flags\n\t" | ||
46 | #else | ||
47 | "mov %eax, ax\n\t" | ||
48 | "mov %ebx, bx\n\t" | ||
49 | "mov %ecx, cx\n\t" | ||
50 | "mov %edx, dx\n\t" | ||
51 | "mov %esi, si\n\t" | ||
52 | "mov %edi, di\n\t" | ||
53 | "mov %ebp, bp\n\t" | ||
54 | "mov %esp, sp\n\t" | ||
55 | "pushfl\n\t" | ||
56 | "popl flags\n\t" | ||
57 | #endif | ||
58 | "jmp _start\n\t" | ||
59 | ".size real_start, . - real_start\n\t" | ||
60 | ".popsection"); | ||
61 | |||
62 | int main() | ||
63 | { | ||
64 | int nerrs = 0; | ||
65 | |||
66 | if (sp == 0) { | ||
67 | printf("[FAIL]\tTest was built incorrectly\n"); | ||
68 | return 1; | ||
69 | } | ||
70 | |||
71 | if (ax || bx || cx || dx || si || di || bp | ||
72 | #ifdef __x86_64__ | ||
73 | || r8 || r9 || r10 || r11 || r12 || r13 || r14 || r15 | ||
74 | #endif | ||
75 | ) { | ||
76 | printf("[FAIL]\tAll GPRs except SP should be 0\n"); | ||
77 | #define SHOW(x) printf("\t" #x " = 0x%lx\n", x); | ||
78 | SHOW(ax); | ||
79 | SHOW(bx); | ||
80 | SHOW(cx); | ||
81 | SHOW(dx); | ||
82 | SHOW(si); | ||
83 | SHOW(di); | ||
84 | SHOW(bp); | ||
85 | SHOW(sp); | ||
86 | #ifdef __x86_64__ | ||
87 | SHOW(r8); | ||
88 | SHOW(r9); | ||
89 | SHOW(r10); | ||
90 | SHOW(r11); | ||
91 | SHOW(r12); | ||
92 | SHOW(r13); | ||
93 | SHOW(r14); | ||
94 | SHOW(r15); | ||
95 | #endif | ||
96 | nerrs++; | ||
97 | } else { | ||
98 | printf("[OK]\tAll GPRs except SP are 0\n"); | ||
99 | } | ||
100 | |||
101 | if (flags != 0x202) { | ||
102 | printf("[FAIL]\tFLAGS is 0x%lx, but it should be 0x202\n", flags); | ||
103 | nerrs++; | ||
104 | } else { | ||
105 | printf("[OK]\tFLAGS is 0x202\n"); | ||
106 | } | ||
107 | |||
108 | return nerrs ? 1 : 0; | ||
109 | } | ||
diff --git a/tools/testing/selftests/x86/ptrace_syscall.c b/tools/testing/selftests/x86/ptrace_syscall.c index 5105b49cd8aa..421456784bc6 100644 --- a/tools/testing/selftests/x86/ptrace_syscall.c +++ b/tools/testing/selftests/x86/ptrace_syscall.c | |||
@@ -103,6 +103,17 @@ static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), | |||
103 | err(1, "sigaction"); | 103 | err(1, "sigaction"); |
104 | } | 104 | } |
105 | 105 | ||
106 | static void setsigign(int sig, int flags) | ||
107 | { | ||
108 | struct sigaction sa; | ||
109 | memset(&sa, 0, sizeof(sa)); | ||
110 | sa.sa_sigaction = (void *)SIG_IGN; | ||
111 | sa.sa_flags = flags; | ||
112 | sigemptyset(&sa.sa_mask); | ||
113 | if (sigaction(sig, &sa, 0)) | ||
114 | err(1, "sigaction"); | ||
115 | } | ||
116 | |||
106 | static void clearhandler(int sig) | 117 | static void clearhandler(int sig) |
107 | { | 118 | { |
108 | struct sigaction sa; | 119 | struct sigaction sa; |
@@ -187,7 +198,7 @@ static void test_ptrace_syscall_restart(void) | |||
187 | 198 | ||
188 | printf("[RUN]\tSYSEMU\n"); | 199 | printf("[RUN]\tSYSEMU\n"); |
189 | if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) | 200 | if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) |
190 | err(1, "PTRACE_SYSCALL"); | 201 | err(1, "PTRACE_SYSEMU"); |
191 | wait_trap(chld); | 202 | wait_trap(chld); |
192 | 203 | ||
193 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | 204 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) |
@@ -218,7 +229,7 @@ static void test_ptrace_syscall_restart(void) | |||
218 | err(1, "PTRACE_SETREGS"); | 229 | err(1, "PTRACE_SETREGS"); |
219 | 230 | ||
220 | if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) | 231 | if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) |
221 | err(1, "PTRACE_SYSCALL"); | 232 | err(1, "PTRACE_SYSEMU"); |
222 | wait_trap(chld); | 233 | wait_trap(chld); |
223 | 234 | ||
224 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | 235 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) |
@@ -250,7 +261,7 @@ static void test_ptrace_syscall_restart(void) | |||
250 | err(1, "PTRACE_SETREGS"); | 261 | err(1, "PTRACE_SETREGS"); |
251 | 262 | ||
252 | if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) | 263 | if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) |
253 | err(1, "PTRACE_SYSCALL"); | 264 | err(1, "PTRACE_SYSEMU"); |
254 | wait_trap(chld); | 265 | wait_trap(chld); |
255 | 266 | ||
256 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | 267 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) |
@@ -277,6 +288,119 @@ static void test_ptrace_syscall_restart(void) | |||
277 | } | 288 | } |
278 | } | 289 | } |
279 | 290 | ||
291 | static void test_restart_under_ptrace(void) | ||
292 | { | ||
293 | printf("[RUN]\tkernel syscall restart under ptrace\n"); | ||
294 | pid_t chld = fork(); | ||
295 | if (chld < 0) | ||
296 | err(1, "fork"); | ||
297 | |||
298 | if (chld == 0) { | ||
299 | if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0) | ||
300 | err(1, "PTRACE_TRACEME"); | ||
301 | |||
302 | printf("\tChild will take a nap until signaled\n"); | ||
303 | setsigign(SIGUSR1, SA_RESTART); | ||
304 | raise(SIGSTOP); | ||
305 | |||
306 | syscall(SYS_pause, 0, 0, 0, 0, 0, 0); | ||
307 | _exit(0); | ||
308 | } | ||
309 | |||
310 | int status; | ||
311 | |||
312 | /* Wait for SIGSTOP. */ | ||
313 | if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status)) | ||
314 | err(1, "waitpid"); | ||
315 | |||
316 | struct user_regs_struct regs; | ||
317 | |||
318 | printf("[RUN]\tSYSCALL\n"); | ||
319 | if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0) | ||
320 | err(1, "PTRACE_SYSCALL"); | ||
321 | wait_trap(chld); | ||
322 | |||
323 | /* We should be stopped at pause(2) entry. */ | ||
324 | |||
325 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | ||
326 | err(1, "PTRACE_GETREGS"); | ||
327 | |||
328 | if (regs.user_syscall_nr != SYS_pause || | ||
329 | regs.user_arg0 != 0 || regs.user_arg1 != 0 || | ||
330 | regs.user_arg2 != 0 || regs.user_arg3 != 0 || | ||
331 | regs.user_arg4 != 0 || regs.user_arg5 != 0) { | ||
332 | printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5); | ||
333 | nerrs++; | ||
334 | } else { | ||
335 | printf("[OK]\tInitial nr and args are correct\n"); | ||
336 | } | ||
337 | |||
338 | /* Interrupt it. */ | ||
339 | kill(chld, SIGUSR1); | ||
340 | |||
341 | /* Advance. We should be stopped at exit. */ | ||
342 | printf("[RUN]\tSYSCALL\n"); | ||
343 | if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0) | ||
344 | err(1, "PTRACE_SYSCALL"); | ||
345 | wait_trap(chld); | ||
346 | |||
347 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | ||
348 | err(1, "PTRACE_GETREGS"); | ||
349 | |||
350 | if (regs.user_syscall_nr != SYS_pause || | ||
351 | regs.user_arg0 != 0 || regs.user_arg1 != 0 || | ||
352 | regs.user_arg2 != 0 || regs.user_arg3 != 0 || | ||
353 | regs.user_arg4 != 0 || regs.user_arg5 != 0) { | ||
354 | printf("[FAIL]\tArgs after SIGUSR1 are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5); | ||
355 | nerrs++; | ||
356 | } else { | ||
357 | printf("[OK]\tArgs after SIGUSR1 are correct (ax = %ld)\n", | ||
358 | (long)regs.user_ax); | ||
359 | } | ||
360 | |||
361 | /* Poke the regs back in. This must not break anything. */ | ||
362 | if (ptrace(PTRACE_SETREGS, chld, 0, ®s) != 0) | ||
363 | err(1, "PTRACE_SETREGS"); | ||
364 | |||
365 | /* Catch the (ignored) SIGUSR1. */ | ||
366 | if (ptrace(PTRACE_CONT, chld, 0, 0) != 0) | ||
367 | err(1, "PTRACE_CONT"); | ||
368 | if (waitpid(chld, &status, 0) != chld) | ||
369 | err(1, "waitpid"); | ||
370 | if (!WIFSTOPPED(status)) { | ||
371 | printf("[FAIL]\tChild was stopped for SIGUSR1 (status = 0x%x)\n", status); | ||
372 | nerrs++; | ||
373 | } else { | ||
374 | printf("[OK]\tChild got SIGUSR1\n"); | ||
375 | } | ||
376 | |||
377 | /* The next event should be pause(2) again. */ | ||
378 | printf("[RUN]\tStep again\n"); | ||
379 | if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0) | ||
380 | err(1, "PTRACE_SYSCALL"); | ||
381 | wait_trap(chld); | ||
382 | |||
383 | /* We should be stopped at pause(2) entry. */ | ||
384 | |||
385 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | ||
386 | err(1, "PTRACE_GETREGS"); | ||
387 | |||
388 | if (regs.user_syscall_nr != SYS_pause || | ||
389 | regs.user_arg0 != 0 || regs.user_arg1 != 0 || | ||
390 | regs.user_arg2 != 0 || regs.user_arg3 != 0 || | ||
391 | regs.user_arg4 != 0 || regs.user_arg5 != 0) { | ||
392 | printf("[FAIL]\tpause did not restart (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5); | ||
393 | nerrs++; | ||
394 | } else { | ||
395 | printf("[OK]\tpause(2) restarted correctly\n"); | ||
396 | } | ||
397 | |||
398 | /* Kill it. */ | ||
399 | kill(chld, SIGKILL); | ||
400 | if (waitpid(chld, &status, 0) != chld) | ||
401 | err(1, "waitpid"); | ||
402 | } | ||
403 | |||
280 | int main() | 404 | int main() |
281 | { | 405 | { |
282 | printf("[RUN]\tCheck int80 return regs\n"); | 406 | printf("[RUN]\tCheck int80 return regs\n"); |
@@ -290,5 +414,7 @@ int main() | |||
290 | 414 | ||
291 | test_ptrace_syscall_restart(); | 415 | test_ptrace_syscall_restart(); |
292 | 416 | ||
417 | test_restart_under_ptrace(); | ||
418 | |||
293 | return 0; | 419 | return 0; |
294 | } | 420 | } |
diff --git a/tools/testing/selftests/x86/sigreturn.c b/tools/testing/selftests/x86/sigreturn.c index b5aa1bab7416..8a577e7070c6 100644 --- a/tools/testing/selftests/x86/sigreturn.c +++ b/tools/testing/selftests/x86/sigreturn.c | |||
@@ -54,6 +54,37 @@ | |||
54 | #include <sys/ptrace.h> | 54 | #include <sys/ptrace.h> |
55 | #include <sys/user.h> | 55 | #include <sys/user.h> |
56 | 56 | ||
57 | /* Pull in AR_xyz defines. */ | ||
58 | typedef unsigned int u32; | ||
59 | typedef unsigned short u16; | ||
60 | #include "../../../../arch/x86/include/asm/desc_defs.h" | ||
61 | |||
62 | /* | ||
63 | * Copied from asm/ucontext.h, as asm/ucontext.h conflicts badly with the glibc | ||
64 | * headers. | ||
65 | */ | ||
66 | #ifdef __x86_64__ | ||
67 | /* | ||
68 | * UC_SIGCONTEXT_SS will be set when delivering 64-bit or x32 signals on | ||
69 | * kernels that save SS in the sigcontext. All kernels that set | ||
70 | * UC_SIGCONTEXT_SS will correctly restore at least the low 32 bits of esp | ||
71 | * regardless of SS (i.e. they implement espfix). | ||
72 | * | ||
73 | * Kernels that set UC_SIGCONTEXT_SS will also set UC_STRICT_RESTORE_SS | ||
74 | * when delivering a signal that came from 64-bit code. | ||
75 | * | ||
76 | * Sigreturn restores SS as follows: | ||
77 | * | ||
78 | * if (saved SS is valid || UC_STRICT_RESTORE_SS is set || | ||
79 | * saved CS is not 64-bit) | ||
80 | * new SS = saved SS (will fail IRET and signal if invalid) | ||
81 | * else | ||
82 | * new SS = a flat 32-bit data segment | ||
83 | */ | ||
84 | #define UC_SIGCONTEXT_SS 0x2 | ||
85 | #define UC_STRICT_RESTORE_SS 0x4 | ||
86 | #endif | ||
87 | |||
57 | /* | 88 | /* |
58 | * In principle, this test can run on Linux emulation layers (e.g. | 89 | * In principle, this test can run on Linux emulation layers (e.g. |
59 | * Illumos "LX branded zones"). Solaris-based kernels reserve LDT | 90 | * Illumos "LX branded zones"). Solaris-based kernels reserve LDT |
@@ -267,6 +298,9 @@ static gregset_t initial_regs, requested_regs, resulting_regs; | |||
267 | /* Instructions for the SIGUSR1 handler. */ | 298 | /* Instructions for the SIGUSR1 handler. */ |
268 | static volatile unsigned short sig_cs, sig_ss; | 299 | static volatile unsigned short sig_cs, sig_ss; |
269 | static volatile sig_atomic_t sig_trapped, sig_err, sig_trapno; | 300 | static volatile sig_atomic_t sig_trapped, sig_err, sig_trapno; |
301 | #ifdef __x86_64__ | ||
302 | static volatile sig_atomic_t sig_corrupt_final_ss; | ||
303 | #endif | ||
270 | 304 | ||
271 | /* Abstractions for some 32-bit vs 64-bit differences. */ | 305 | /* Abstractions for some 32-bit vs 64-bit differences. */ |
272 | #ifdef __x86_64__ | 306 | #ifdef __x86_64__ |
@@ -305,9 +339,105 @@ static greg_t *csptr(ucontext_t *ctx) | |||
305 | } | 339 | } |
306 | #endif | 340 | #endif |
307 | 341 | ||
342 | /* | ||
343 | * Checks a given selector for its code bitness or returns -1 if it's not | ||
344 | * a usable code segment selector. | ||
345 | */ | ||
346 | int cs_bitness(unsigned short cs) | ||
347 | { | ||
348 | uint32_t valid = 0, ar; | ||
349 | asm ("lar %[cs], %[ar]\n\t" | ||
350 | "jnz 1f\n\t" | ||
351 | "mov $1, %[valid]\n\t" | ||
352 | "1:" | ||
353 | : [ar] "=r" (ar), [valid] "+rm" (valid) | ||
354 | : [cs] "r" (cs)); | ||
355 | |||
356 | if (!valid) | ||
357 | return -1; | ||
358 | |||
359 | bool db = (ar & (1 << 22)); | ||
360 | bool l = (ar & (1 << 21)); | ||
361 | |||
362 | if (!(ar & (1<<11))) | ||
363 | return -1; /* Not code. */ | ||
364 | |||
365 | if (l && !db) | ||
366 | return 64; | ||
367 | else if (!l && db) | ||
368 | return 32; | ||
369 | else if (!l && !db) | ||
370 | return 16; | ||
371 | else | ||
372 | return -1; /* Unknown bitness. */ | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | * Checks a given selector for its code bitness or returns -1 if it's not | ||
377 | * a usable code segment selector. | ||
378 | */ | ||
379 | bool is_valid_ss(unsigned short cs) | ||
380 | { | ||
381 | uint32_t valid = 0, ar; | ||
382 | asm ("lar %[cs], %[ar]\n\t" | ||
383 | "jnz 1f\n\t" | ||
384 | "mov $1, %[valid]\n\t" | ||
385 | "1:" | ||
386 | : [ar] "=r" (ar), [valid] "+rm" (valid) | ||
387 | : [cs] "r" (cs)); | ||
388 | |||
389 | if (!valid) | ||
390 | return false; | ||
391 | |||
392 | if ((ar & AR_TYPE_MASK) != AR_TYPE_RWDATA && | ||
393 | (ar & AR_TYPE_MASK) != AR_TYPE_RWDATA_EXPDOWN) | ||
394 | return false; | ||
395 | |||
396 | return (ar & AR_P); | ||
397 | } | ||
398 | |||
308 | /* Number of errors in the current test case. */ | 399 | /* Number of errors in the current test case. */ |
309 | static volatile sig_atomic_t nerrs; | 400 | static volatile sig_atomic_t nerrs; |
310 | 401 | ||
402 | static void validate_signal_ss(int sig, ucontext_t *ctx) | ||
403 | { | ||
404 | #ifdef __x86_64__ | ||
405 | bool was_64bit = (cs_bitness(*csptr(ctx)) == 64); | ||
406 | |||
407 | if (!(ctx->uc_flags & UC_SIGCONTEXT_SS)) { | ||
408 | printf("[FAIL]\tUC_SIGCONTEXT_SS was not set\n"); | ||
409 | nerrs++; | ||
410 | |||
411 | /* | ||
412 | * This happens on Linux 4.1. The rest will fail, too, so | ||
413 | * return now to reduce the noise. | ||
414 | */ | ||
415 | return; | ||
416 | } | ||
417 | |||
418 | /* UC_STRICT_RESTORE_SS is set iff we came from 64-bit mode. */ | ||
419 | if (!!(ctx->uc_flags & UC_STRICT_RESTORE_SS) != was_64bit) { | ||
420 | printf("[FAIL]\tUC_STRICT_RESTORE_SS was wrong in signal %d\n", | ||
421 | sig); | ||
422 | nerrs++; | ||
423 | } | ||
424 | |||
425 | if (is_valid_ss(*ssptr(ctx))) { | ||
426 | /* | ||
427 | * DOSEMU was written before 64-bit sigcontext had SS, and | ||
428 | * it tries to figure out the signal source SS by looking at | ||
429 | * the physical register. Make sure that keeps working. | ||
430 | */ | ||
431 | unsigned short hw_ss; | ||
432 | asm ("mov %%ss, %0" : "=rm" (hw_ss)); | ||
433 | if (hw_ss != *ssptr(ctx)) { | ||
434 | printf("[FAIL]\tHW SS didn't match saved SS\n"); | ||
435 | nerrs++; | ||
436 | } | ||
437 | } | ||
438 | #endif | ||
439 | } | ||
440 | |||
311 | /* | 441 | /* |
312 | * SIGUSR1 handler. Sets CS and SS as requested and points IP to the | 442 | * SIGUSR1 handler. Sets CS and SS as requested and points IP to the |
313 | * int3 trampoline. Sets SP to a large known value so that we can see | 443 | * int3 trampoline. Sets SP to a large known value so that we can see |
@@ -317,6 +447,8 @@ static void sigusr1(int sig, siginfo_t *info, void *ctx_void) | |||
317 | { | 447 | { |
318 | ucontext_t *ctx = (ucontext_t*)ctx_void; | 448 | ucontext_t *ctx = (ucontext_t*)ctx_void; |
319 | 449 | ||
450 | validate_signal_ss(sig, ctx); | ||
451 | |||
320 | memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t)); | 452 | memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t)); |
321 | 453 | ||
322 | *csptr(ctx) = sig_cs; | 454 | *csptr(ctx) = sig_cs; |
@@ -334,13 +466,16 @@ static void sigusr1(int sig, siginfo_t *info, void *ctx_void) | |||
334 | } | 466 | } |
335 | 467 | ||
336 | /* | 468 | /* |
337 | * Called after a successful sigreturn. Restores our state so that | 469 | * Called after a successful sigreturn (via int3) or from a failed |
338 | * the original raise(SIGUSR1) returns. | 470 | * sigreturn (directly by kernel). Restores our state so that the |
471 | * original raise(SIGUSR1) returns. | ||
339 | */ | 472 | */ |
340 | static void sigtrap(int sig, siginfo_t *info, void *ctx_void) | 473 | static void sigtrap(int sig, siginfo_t *info, void *ctx_void) |
341 | { | 474 | { |
342 | ucontext_t *ctx = (ucontext_t*)ctx_void; | 475 | ucontext_t *ctx = (ucontext_t*)ctx_void; |
343 | 476 | ||
477 | validate_signal_ss(sig, ctx); | ||
478 | |||
344 | sig_err = ctx->uc_mcontext.gregs[REG_ERR]; | 479 | sig_err = ctx->uc_mcontext.gregs[REG_ERR]; |
345 | sig_trapno = ctx->uc_mcontext.gregs[REG_TRAPNO]; | 480 | sig_trapno = ctx->uc_mcontext.gregs[REG_TRAPNO]; |
346 | 481 | ||
@@ -358,41 +493,62 @@ static void sigtrap(int sig, siginfo_t *info, void *ctx_void) | |||
358 | memcpy(&resulting_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t)); | 493 | memcpy(&resulting_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t)); |
359 | memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t)); | 494 | memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t)); |
360 | 495 | ||
496 | #ifdef __x86_64__ | ||
497 | if (sig_corrupt_final_ss) { | ||
498 | if (ctx->uc_flags & UC_STRICT_RESTORE_SS) { | ||
499 | printf("[FAIL]\tUC_STRICT_RESTORE_SS was set inappropriately\n"); | ||
500 | nerrs++; | ||
501 | } else { | ||
502 | /* | ||
503 | * DOSEMU transitions from 32-bit to 64-bit mode by | ||
504 | * adjusting sigcontext, and it requires that this work | ||
505 | * even if the saved SS is bogus. | ||
506 | */ | ||
507 | printf("\tCorrupting SS on return to 64-bit mode\n"); | ||
508 | *ssptr(ctx) = 0; | ||
509 | } | ||
510 | } | ||
511 | #endif | ||
512 | |||
361 | sig_trapped = sig; | 513 | sig_trapped = sig; |
362 | } | 514 | } |
363 | 515 | ||
364 | /* | 516 | #ifdef __x86_64__ |
365 | * Checks a given selector for its code bitness or returns -1 if it's not | 517 | /* Tests recovery if !UC_STRICT_RESTORE_SS */ |
366 | * a usable code segment selector. | 518 | static void sigusr2(int sig, siginfo_t *info, void *ctx_void) |
367 | */ | ||
368 | int cs_bitness(unsigned short cs) | ||
369 | { | 519 | { |
370 | uint32_t valid = 0, ar; | 520 | ucontext_t *ctx = (ucontext_t*)ctx_void; |
371 | asm ("lar %[cs], %[ar]\n\t" | ||
372 | "jnz 1f\n\t" | ||
373 | "mov $1, %[valid]\n\t" | ||
374 | "1:" | ||
375 | : [ar] "=r" (ar), [valid] "+rm" (valid) | ||
376 | : [cs] "r" (cs)); | ||
377 | 521 | ||
378 | if (!valid) | 522 | if (!(ctx->uc_flags & UC_STRICT_RESTORE_SS)) { |
379 | return -1; | 523 | printf("[FAIL]\traise(2) didn't set UC_STRICT_RESTORE_SS\n"); |
524 | nerrs++; | ||
525 | return; /* We can't do the rest. */ | ||
526 | } | ||
380 | 527 | ||
381 | bool db = (ar & (1 << 22)); | 528 | ctx->uc_flags &= ~UC_STRICT_RESTORE_SS; |
382 | bool l = (ar & (1 << 21)); | 529 | *ssptr(ctx) = 0; |
383 | 530 | ||
384 | if (!(ar & (1<<11))) | 531 | /* Return. The kernel should recover without sending another signal. */ |
385 | return -1; /* Not code. */ | 532 | } |
386 | 533 | ||
387 | if (l && !db) | 534 | static int test_nonstrict_ss(void) |
388 | return 64; | 535 | { |
389 | else if (!l && db) | 536 | clearhandler(SIGUSR1); |
390 | return 32; | 537 | clearhandler(SIGTRAP); |
391 | else if (!l && !db) | 538 | clearhandler(SIGSEGV); |
392 | return 16; | 539 | clearhandler(SIGILL); |
393 | else | 540 | sethandler(SIGUSR2, sigusr2, 0); |
394 | return -1; /* Unknown bitness. */ | 541 | |
542 | nerrs = 0; | ||
543 | |||
544 | printf("[RUN]\tClear UC_STRICT_RESTORE_SS and corrupt SS\n"); | ||
545 | raise(SIGUSR2); | ||
546 | if (!nerrs) | ||
547 | printf("[OK]\tIt worked\n"); | ||
548 | |||
549 | return nerrs; | ||
395 | } | 550 | } |
551 | #endif | ||
396 | 552 | ||
397 | /* Finds a usable code segment of the requested bitness. */ | 553 | /* Finds a usable code segment of the requested bitness. */ |
398 | int find_cs(int bitness) | 554 | int find_cs(int bitness) |
@@ -576,6 +732,12 @@ static int test_bad_iret(int cs_bits, unsigned short ss, int force_cs) | |||
576 | errdesc, strsignal(sig_trapped)); | 732 | errdesc, strsignal(sig_trapped)); |
577 | return 0; | 733 | return 0; |
578 | } else { | 734 | } else { |
735 | /* | ||
736 | * This also implicitly tests UC_STRICT_RESTORE_SS: | ||
737 | * We check that these signals set UC_STRICT_RESTORE_SS and, | ||
738 | * if UC_STRICT_RESTORE_SS doesn't cause strict behavior, | ||
739 | * then we won't get SIGSEGV. | ||
740 | */ | ||
579 | printf("[FAIL]\tDid not get SIGSEGV\n"); | 741 | printf("[FAIL]\tDid not get SIGSEGV\n"); |
580 | return 1; | 742 | return 1; |
581 | } | 743 | } |
@@ -632,6 +794,14 @@ int main() | |||
632 | GDT3(gdt_data16_idx)); | 794 | GDT3(gdt_data16_idx)); |
633 | } | 795 | } |
634 | 796 | ||
797 | #ifdef __x86_64__ | ||
798 | /* Nasty ABI case: check SS corruption handling. */ | ||
799 | sig_corrupt_final_ss = 1; | ||
800 | total_nerrs += test_valid_sigreturn(32, false, -1); | ||
801 | total_nerrs += test_valid_sigreturn(32, true, -1); | ||
802 | sig_corrupt_final_ss = 0; | ||
803 | #endif | ||
804 | |||
635 | /* | 805 | /* |
636 | * We're done testing valid sigreturn cases. Now we test states | 806 | * We're done testing valid sigreturn cases. Now we test states |
637 | * for which sigreturn itself will succeed but the subsequent | 807 | * for which sigreturn itself will succeed but the subsequent |
@@ -680,5 +850,9 @@ int main() | |||
680 | if (gdt_npdata32_idx) | 850 | if (gdt_npdata32_idx) |
681 | test_bad_iret(32, GDT3(gdt_npdata32_idx), -1); | 851 | test_bad_iret(32, GDT3(gdt_npdata32_idx), -1); |
682 | 852 | ||
853 | #ifdef __x86_64__ | ||
854 | total_nerrs += test_nonstrict_ss(); | ||
855 | #endif | ||
856 | |||
683 | return total_nerrs ? 1 : 0; | 857 | return total_nerrs ? 1 : 0; |
684 | } | 858 | } |
diff --git a/tools/testing/selftests/x86/syscall_nt.c b/tools/testing/selftests/x86/syscall_nt.c index 60c06af4646a..43fcab367fb0 100644 --- a/tools/testing/selftests/x86/syscall_nt.c +++ b/tools/testing/selftests/x86/syscall_nt.c | |||
@@ -17,6 +17,9 @@ | |||
17 | 17 | ||
18 | #include <stdio.h> | 18 | #include <stdio.h> |
19 | #include <unistd.h> | 19 | #include <unistd.h> |
20 | #include <string.h> | ||
21 | #include <signal.h> | ||
22 | #include <err.h> | ||
20 | #include <sys/syscall.h> | 23 | #include <sys/syscall.h> |
21 | #include <asm/processor-flags.h> | 24 | #include <asm/processor-flags.h> |
22 | 25 | ||
@@ -26,6 +29,8 @@ | |||
26 | # define WIDTH "l" | 29 | # define WIDTH "l" |
27 | #endif | 30 | #endif |
28 | 31 | ||
32 | static unsigned int nerrs; | ||
33 | |||
29 | static unsigned long get_eflags(void) | 34 | static unsigned long get_eflags(void) |
30 | { | 35 | { |
31 | unsigned long eflags; | 36 | unsigned long eflags; |
@@ -39,16 +44,52 @@ static void set_eflags(unsigned long eflags) | |||
39 | : : "rm" (eflags) : "flags"); | 44 | : : "rm" (eflags) : "flags"); |
40 | } | 45 | } |
41 | 46 | ||
42 | int main() | 47 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), |
48 | int flags) | ||
43 | { | 49 | { |
44 | printf("[RUN]\tSet NT and issue a syscall\n"); | 50 | struct sigaction sa; |
45 | set_eflags(get_eflags() | X86_EFLAGS_NT); | 51 | memset(&sa, 0, sizeof(sa)); |
52 | sa.sa_sigaction = handler; | ||
53 | sa.sa_flags = SA_SIGINFO | flags; | ||
54 | sigemptyset(&sa.sa_mask); | ||
55 | if (sigaction(sig, &sa, 0)) | ||
56 | err(1, "sigaction"); | ||
57 | } | ||
58 | |||
59 | static void sigtrap(int sig, siginfo_t *si, void *ctx_void) | ||
60 | { | ||
61 | } | ||
62 | |||
63 | static void do_it(unsigned long extraflags) | ||
64 | { | ||
65 | unsigned long flags; | ||
66 | |||
67 | set_eflags(get_eflags() | extraflags); | ||
46 | syscall(SYS_getpid); | 68 | syscall(SYS_getpid); |
47 | if (get_eflags() & X86_EFLAGS_NT) { | 69 | flags = get_eflags(); |
48 | printf("[OK]\tThe syscall worked and NT is still set\n"); | 70 | if ((flags & extraflags) == extraflags) { |
49 | return 0; | 71 | printf("[OK]\tThe syscall worked and flags are still set\n"); |
50 | } else { | 72 | } else { |
51 | printf("[FAIL]\tThe syscall worked but NT was cleared\n"); | 73 | printf("[FAIL]\tThe syscall worked but flags were cleared (flags = 0x%lx but expected 0x%lx set)\n", |
52 | return 1; | 74 | flags, extraflags); |
75 | nerrs++; | ||
53 | } | 76 | } |
54 | } | 77 | } |
78 | |||
79 | int main(void) | ||
80 | { | ||
81 | printf("[RUN]\tSet NT and issue a syscall\n"); | ||
82 | do_it(X86_EFLAGS_NT); | ||
83 | |||
84 | /* | ||
85 | * Now try it again with TF set -- TF forces returns via IRET in all | ||
86 | * cases except non-ptregs-using 64-bit full fast path syscalls. | ||
87 | */ | ||
88 | |||
89 | sethandler(SIGTRAP, sigtrap, 0); | ||
90 | |||
91 | printf("[RUN]\tSet NT|TF and issue a syscall\n"); | ||
92 | do_it(X86_EFLAGS_NT | X86_EFLAGS_TF); | ||
93 | |||
94 | return nerrs == 0 ? 0 : 1; | ||
95 | } | ||