diff options
author | Andy Lutomirski <luto@kernel.org> | 2015-04-24 18:09:19 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2015-05-08 07:33:59 -0400 |
commit | e22438f8e997ac1c8911d8808b6a4c492cd8bc6e (patch) | |
tree | 38697c66741d33e743d8f93dd70dd0f89180f5b7 | |
parent | 7ae383be81781c5e1347f71c3eb0d53ce5188200 (diff) |
x86, selftests: Add a test for the "sysret_ss_attrs" bug
On AMD CPUs, SYSRET can return with a valid SS descriptor with
with the hidden attributes set to an unusable state. Make sure
the kernel doesn't let this happen. This detects an
as-yet-unfixed regression.
Note that the 64-bit version of this test fails on AMD CPUs on
all kernel versions, although the issue in the 64-bit case is
much less severe than in the 32-bit case.
Reported-by: Brian Gerst <brgerst@gmail.com>
Tested-by: Denys Vlasenko <dvlasenk@redhat.com>
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Tests: e7d6eefaaa44 ("x86/vdso32/syscall.S: Do not load __USER32_DS to %ss")
Cc: Alexei Starovoitov <ast@plumgrid.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Will Drewry <wad@chromium.org>
Link: http://lkml.kernel.org/r/resend_4d740841bac383742949e2fefb03982736595087.git.luto@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | tools/testing/selftests/x86/Makefile | 5 | ||||
-rw-r--r-- | tools/testing/selftests/x86/run_x86_tests.sh | 2 | ||||
-rw-r--r-- | tools/testing/selftests/x86/sysret_ss_attrs.c | 112 | ||||
-rw-r--r-- | tools/testing/selftests/x86/thunks.S | 67 |
4 files changed, 185 insertions, 1 deletions
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index ddf63569df5a..9309097f58e8 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile | |||
@@ -1,6 +1,6 @@ | |||
1 | .PHONY: all all_32 all_64 check_build32 clean run_tests | 1 | .PHONY: all all_32 all_64 check_build32 clean run_tests |
2 | 2 | ||
3 | TARGETS_C_BOTHBITS := sigreturn single_step_syscall | 3 | TARGETS_C_BOTHBITS := sigreturn single_step_syscall sysret_ss_attrs |
4 | 4 | ||
5 | BINARIES_32 := $(TARGETS_C_BOTHBITS:%=%_32) | 5 | BINARIES_32 := $(TARGETS_C_BOTHBITS:%=%_32) |
6 | BINARIES_64 := $(TARGETS_C_BOTHBITS:%=%_64) | 6 | BINARIES_64 := $(TARGETS_C_BOTHBITS:%=%_64) |
@@ -46,3 +46,6 @@ check_build32: | |||
46 | echo " yum install glibc-devel.*i686"; \ | 46 | echo " yum install glibc-devel.*i686"; \ |
47 | exit 1; \ | 47 | exit 1; \ |
48 | fi | 48 | fi |
49 | |||
50 | # Some tests have additional dependencies. | ||
51 | sysret_ss_attrs_64: thunks.S | ||
diff --git a/tools/testing/selftests/x86/run_x86_tests.sh b/tools/testing/selftests/x86/run_x86_tests.sh index 3fc19b376812..d25034280dd5 100644 --- a/tools/testing/selftests/x86/run_x86_tests.sh +++ b/tools/testing/selftests/x86/run_x86_tests.sh | |||
@@ -4,10 +4,12 @@ | |||
4 | # script here. | 4 | # script here. |
5 | ./sigreturn_32 || exit 1 | 5 | ./sigreturn_32 || exit 1 |
6 | ./single_step_syscall_32 || exit 1 | 6 | ./single_step_syscall_32 || exit 1 |
7 | ./sysret_ss_attrs_32 || exit 1 | ||
7 | 8 | ||
8 | if [[ "$uname -p" -eq "x86_64" ]]; then | 9 | if [[ "$uname -p" -eq "x86_64" ]]; then |
9 | ./sigreturn_64 || exit 1 | 10 | ./sigreturn_64 || exit 1 |
10 | ./single_step_syscall_64 || exit 1 | 11 | ./single_step_syscall_64 || exit 1 |
12 | ./sysret_ss_attrs_64 || exit 1 | ||
11 | fi | 13 | fi |
12 | 14 | ||
13 | exit 0 | 15 | exit 0 |
diff --git a/tools/testing/selftests/x86/sysret_ss_attrs.c b/tools/testing/selftests/x86/sysret_ss_attrs.c new file mode 100644 index 000000000000..ce42d5a64009 --- /dev/null +++ b/tools/testing/selftests/x86/sysret_ss_attrs.c | |||
@@ -0,0 +1,112 @@ | |||
1 | /* | ||
2 | * sysret_ss_attrs.c - test that syscalls return valid hidden SS attributes | ||
3 | * Copyright (c) 2015 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 | * On AMD CPUs, SYSRET can return with a valid SS descriptor with with | ||
15 | * the hidden attributes set to an unusable state. Make sure the kernel | ||
16 | * doesn't let this happen. | ||
17 | */ | ||
18 | |||
19 | #define _GNU_SOURCE | ||
20 | |||
21 | #include <stdlib.h> | ||
22 | #include <unistd.h> | ||
23 | #include <stdio.h> | ||
24 | #include <string.h> | ||
25 | #include <sys/mman.h> | ||
26 | #include <err.h> | ||
27 | #include <stddef.h> | ||
28 | #include <stdbool.h> | ||
29 | #include <pthread.h> | ||
30 | |||
31 | static void *threadproc(void *ctx) | ||
32 | { | ||
33 | /* | ||
34 | * Do our best to cause sleeps on this CPU to exit the kernel and | ||
35 | * re-enter with SS = 0. | ||
36 | */ | ||
37 | while (true) | ||
38 | ; | ||
39 | |||
40 | return NULL; | ||
41 | } | ||
42 | |||
43 | #ifdef __x86_64__ | ||
44 | extern unsigned long call32_from_64(void *stack, void (*function)(void)); | ||
45 | |||
46 | asm (".pushsection .text\n\t" | ||
47 | ".code32\n\t" | ||
48 | "test_ss:\n\t" | ||
49 | "pushl $0\n\t" | ||
50 | "popl %eax\n\t" | ||
51 | "ret\n\t" | ||
52 | ".code64"); | ||
53 | extern void test_ss(void); | ||
54 | #endif | ||
55 | |||
56 | int main() | ||
57 | { | ||
58 | /* | ||
59 | * Start a busy-looping thread on the same CPU we're on. | ||
60 | * For simplicity, just stick everything to CPU 0. This will | ||
61 | * fail in some containers, but that's probably okay. | ||
62 | */ | ||
63 | cpu_set_t cpuset; | ||
64 | CPU_ZERO(&cpuset); | ||
65 | CPU_SET(0, &cpuset); | ||
66 | if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) | ||
67 | printf("[WARN]\tsched_setaffinity failed\n"); | ||
68 | |||
69 | pthread_t thread; | ||
70 | if (pthread_create(&thread, 0, threadproc, 0) != 0) | ||
71 | err(1, "pthread_create"); | ||
72 | |||
73 | #ifdef __x86_64__ | ||
74 | unsigned char *stack32 = mmap(NULL, 4096, PROT_READ | PROT_WRITE, | ||
75 | MAP_32BIT | MAP_ANONYMOUS | MAP_PRIVATE, | ||
76 | -1, 0); | ||
77 | if (stack32 == MAP_FAILED) | ||
78 | err(1, "mmap"); | ||
79 | #endif | ||
80 | |||
81 | printf("[RUN]\tSyscalls followed by SS validation\n"); | ||
82 | |||
83 | for (int i = 0; i < 1000; i++) { | ||
84 | /* | ||
85 | * Go to sleep and return using sysret (if we're 64-bit | ||
86 | * or we're 32-bit on AMD on a 64-bit kernel). On AMD CPUs, | ||
87 | * SYSRET doesn't fix up the cached SS descriptor, so the | ||
88 | * kernel needs some kind of workaround to make sure that we | ||
89 | * end the system call with a valid stack segment. This | ||
90 | * can be a confusing failure because the SS *selector* | ||
91 | * is the same regardless. | ||
92 | */ | ||
93 | usleep(2); | ||
94 | |||
95 | #ifdef __x86_64__ | ||
96 | /* | ||
97 | * On 32-bit, just doing a syscall through glibc is enough | ||
98 | * to cause a crash if our cached SS descriptor is invalid. | ||
99 | * On 64-bit, it's not, so try extra hard. | ||
100 | */ | ||
101 | call32_from_64(stack32 + 4088, test_ss); | ||
102 | #endif | ||
103 | } | ||
104 | |||
105 | printf("[OK]\tWe survived\n"); | ||
106 | |||
107 | #ifdef __x86_64__ | ||
108 | munmap(stack32, 4096); | ||
109 | #endif | ||
110 | |||
111 | return 0; | ||
112 | } | ||
diff --git a/tools/testing/selftests/x86/thunks.S b/tools/testing/selftests/x86/thunks.S new file mode 100644 index 000000000000..ce8a995bbb17 --- /dev/null +++ b/tools/testing/selftests/x86/thunks.S | |||
@@ -0,0 +1,67 @@ | |||
1 | /* | ||
2 | * thunks.S - assembly helpers for mixed-bitness code | ||
3 | * Copyright (c) 2015 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 | * These are little helpers that make it easier to switch bitness on | ||
15 | * the fly. | ||
16 | */ | ||
17 | |||
18 | .text | ||
19 | |||
20 | .global call32_from_64 | ||
21 | .type call32_from_64, @function | ||
22 | call32_from_64: | ||
23 | // rdi: stack to use | ||
24 | // esi: function to call | ||
25 | |||
26 | // Save registers | ||
27 | pushq %rbx | ||
28 | pushq %rbp | ||
29 | pushq %r12 | ||
30 | pushq %r13 | ||
31 | pushq %r14 | ||
32 | pushq %r15 | ||
33 | pushfq | ||
34 | |||
35 | // Switch stacks | ||
36 | mov %rsp,(%rdi) | ||
37 | mov %rdi,%rsp | ||
38 | |||
39 | // Switch to compatibility mode | ||
40 | pushq $0x23 /* USER32_CS */ | ||
41 | pushq $1f | ||
42 | lretq | ||
43 | |||
44 | 1: | ||
45 | .code32 | ||
46 | // Call the function | ||
47 | call *%esi | ||
48 | // Switch back to long mode | ||
49 | jmp $0x33,$1f | ||
50 | .code64 | ||
51 | |||
52 | 1: | ||
53 | // Restore the stack | ||
54 | mov (%rsp),%rsp | ||
55 | |||
56 | // Restore registers | ||
57 | popfq | ||
58 | popq %r15 | ||
59 | popq %r14 | ||
60 | popq %r13 | ||
61 | popq %r12 | ||
62 | popq %rbp | ||
63 | popq %rbx | ||
64 | |||
65 | ret | ||
66 | |||
67 | .size call32_from_64, .-call32_from_64 | ||