diff options
author | Andy Lutomirski <luto@kernel.org> | 2015-04-15 19:10:07 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2015-04-16 06:41:49 -0400 |
commit | 0a15584d72760a3b83d97af85d37ffaa2c42068d (patch) | |
tree | 2d4820d541fd00505c3d179d982c3f4fef71e25a | |
parent | 6a16dda86ebbcfe690c753c3fb469b4f9ad5a5ef (diff) |
x86, selftests: Add single_step_syscall test
This is a very simple test that makes system calls with TF set.
This test currently fails when running the 32-bit build on a
64-bit kernel on an Intel CPU. This bug will be fixed by the
next commit.
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Shuah Khan <shuah.kh@samsung.com>
Link: http://lkml.kernel.org/r/20e68021155f6ab5c60590dcad81d37c68ea2c4f.1429139075.git.luto@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | tools/testing/selftests/x86/Makefile | 2 | ||||
-rw-r--r-- | tools/testing/selftests/x86/run_x86_tests.sh | 2 | ||||
-rw-r--r-- | tools/testing/selftests/x86/single_step_syscall.c | 181 |
3 files changed, 184 insertions, 1 deletions
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index f0a7918178dd..ddf63569df5a 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 | 3 | TARGETS_C_BOTHBITS := sigreturn single_step_syscall |
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) |
diff --git a/tools/testing/selftests/x86/run_x86_tests.sh b/tools/testing/selftests/x86/run_x86_tests.sh index 3d3ec65f3e7c..3fc19b376812 100644 --- a/tools/testing/selftests/x86/run_x86_tests.sh +++ b/tools/testing/selftests/x86/run_x86_tests.sh | |||
@@ -3,9 +3,11 @@ | |||
3 | # This is deliberately minimal. IMO kselftests should provide a standard | 3 | # This is deliberately minimal. IMO kselftests should provide a standard |
4 | # script here. | 4 | # script here. |
5 | ./sigreturn_32 || exit 1 | 5 | ./sigreturn_32 || exit 1 |
6 | ./single_step_syscall_32 || exit 1 | ||
6 | 7 | ||
7 | if [[ "$uname -p" -eq "x86_64" ]]; then | 8 | if [[ "$uname -p" -eq "x86_64" ]]; then |
8 | ./sigreturn_64 || exit 1 | 9 | ./sigreturn_64 || exit 1 |
10 | ./single_step_syscall_64 || exit 1 | ||
9 | fi | 11 | fi |
10 | 12 | ||
11 | exit 0 | 13 | exit 0 |
diff --git a/tools/testing/selftests/x86/single_step_syscall.c b/tools/testing/selftests/x86/single_step_syscall.c new file mode 100644 index 000000000000..50c26358e8b7 --- /dev/null +++ b/tools/testing/selftests/x86/single_step_syscall.c | |||
@@ -0,0 +1,181 @@ | |||
1 | /* | ||
2 | * single_step_syscall.c - single-steps various x86 syscalls | ||
3 | * Copyright (c) 2014-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 | * This is a very simple series of tests that makes system calls with | ||
15 | * the TF flag set. This exercises some nasty kernel code in the | ||
16 | * SYSENTER case: SYSENTER does not clear TF, so SYSENTER with TF set | ||
17 | * immediately issues #DB from CPL 0. This requires special handling in | ||
18 | * the kernel. | ||
19 | */ | ||
20 | |||
21 | #define _GNU_SOURCE | ||
22 | |||
23 | #include <sys/time.h> | ||
24 | #include <time.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <sys/syscall.h> | ||
27 | #include <unistd.h> | ||
28 | #include <stdio.h> | ||
29 | #include <string.h> | ||
30 | #include <inttypes.h> | ||
31 | #include <sys/mman.h> | ||
32 | #include <sys/signal.h> | ||
33 | #include <sys/ucontext.h> | ||
34 | #include <asm/ldt.h> | ||
35 | #include <err.h> | ||
36 | #include <setjmp.h> | ||
37 | #include <stddef.h> | ||
38 | #include <stdbool.h> | ||
39 | #include <sys/ptrace.h> | ||
40 | #include <sys/user.h> | ||
41 | |||
42 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), | ||
43 | int flags) | ||
44 | { | ||
45 | struct sigaction sa; | ||
46 | memset(&sa, 0, sizeof(sa)); | ||
47 | sa.sa_sigaction = handler; | ||
48 | sa.sa_flags = SA_SIGINFO | flags; | ||
49 | sigemptyset(&sa.sa_mask); | ||
50 | if (sigaction(sig, &sa, 0)) | ||
51 | err(1, "sigaction"); | ||
52 | } | ||
53 | |||
54 | static volatile sig_atomic_t sig_traps; | ||
55 | |||
56 | #ifdef __x86_64__ | ||
57 | # define REG_IP REG_RIP | ||
58 | # define WIDTH "q" | ||
59 | #else | ||
60 | # define REG_IP REG_EIP | ||
61 | # define WIDTH "l" | ||
62 | #endif | ||
63 | |||
64 | static unsigned long get_eflags(void) | ||
65 | { | ||
66 | unsigned long eflags; | ||
67 | asm volatile ("pushf" WIDTH "\n\tpop" WIDTH " %0" : "=rm" (eflags)); | ||
68 | return eflags; | ||
69 | } | ||
70 | |||
71 | static void set_eflags(unsigned long eflags) | ||
72 | { | ||
73 | asm volatile ("push" WIDTH " %0\n\tpopf" WIDTH | ||
74 | : : "rm" (eflags) : "flags"); | ||
75 | } | ||
76 | |||
77 | #define X86_EFLAGS_TF (1UL << 8) | ||
78 | |||
79 | static void sigtrap(int sig, siginfo_t *info, void *ctx_void) | ||
80 | { | ||
81 | ucontext_t *ctx = (ucontext_t*)ctx_void; | ||
82 | |||
83 | if (get_eflags() & X86_EFLAGS_TF) { | ||
84 | set_eflags(get_eflags() & ~X86_EFLAGS_TF); | ||
85 | printf("[WARN]\tSIGTRAP handler had TF set\n"); | ||
86 | _exit(1); | ||
87 | } | ||
88 | |||
89 | sig_traps++; | ||
90 | |||
91 | if (sig_traps == 10000 || sig_traps == 10001) { | ||
92 | printf("[WARN]\tHit %d SIGTRAPs with si_addr 0x%lx, ip 0x%lx\n", | ||
93 | (int)sig_traps, | ||
94 | (unsigned long)info->si_addr, | ||
95 | (unsigned long)ctx->uc_mcontext.gregs[REG_IP]); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | static void check_result(void) | ||
100 | { | ||
101 | unsigned long new_eflags = get_eflags(); | ||
102 | set_eflags(new_eflags & ~X86_EFLAGS_TF); | ||
103 | |||
104 | if (!sig_traps) { | ||
105 | printf("[FAIL]\tNo SIGTRAP\n"); | ||
106 | exit(1); | ||
107 | } | ||
108 | |||
109 | if (!(new_eflags & X86_EFLAGS_TF)) { | ||
110 | printf("[FAIL]\tTF was cleared\n"); | ||
111 | exit(1); | ||
112 | } | ||
113 | |||
114 | printf("[OK]\tSurvived with TF set and %d traps\n", (int)sig_traps); | ||
115 | sig_traps = 0; | ||
116 | } | ||
117 | |||
118 | int main() | ||
119 | { | ||
120 | int tmp; | ||
121 | |||
122 | sethandler(SIGTRAP, sigtrap, 0); | ||
123 | |||
124 | printf("[RUN]\tSet TF and check nop\n"); | ||
125 | set_eflags(get_eflags() | X86_EFLAGS_TF); | ||
126 | asm volatile ("nop"); | ||
127 | check_result(); | ||
128 | |||
129 | #ifdef __x86_64__ | ||
130 | printf("[RUN]\tSet TF and check syscall-less opportunistic sysret\n"); | ||
131 | set_eflags(get_eflags() | X86_EFLAGS_TF); | ||
132 | extern unsigned char post_nop[]; | ||
133 | asm volatile ("pushf" WIDTH "\n\t" | ||
134 | "pop" WIDTH " %%r11\n\t" | ||
135 | "nop\n\t" | ||
136 | "post_nop:" | ||
137 | : : "c" (post_nop) : "r11"); | ||
138 | check_result(); | ||
139 | #endif | ||
140 | |||
141 | printf("[RUN]\tSet TF and check int80\n"); | ||
142 | set_eflags(get_eflags() | X86_EFLAGS_TF); | ||
143 | asm volatile ("int $0x80" : "=a" (tmp) : "a" (SYS_getpid)); | ||
144 | check_result(); | ||
145 | |||
146 | /* | ||
147 | * This test is particularly interesting if fast syscalls use | ||
148 | * SYSENTER: it triggers a nasty design flaw in SYSENTER. | ||
149 | * Specifically, SYSENTER does not clear TF, so either SYSENTER | ||
150 | * or the next instruction traps at CPL0. (Of course, Intel | ||
151 | * mostly forgot to document exactly what happens here.) So we | ||
152 | * get a CPL0 fault with usergs (on 64-bit kernels) and possibly | ||
153 | * no stack. The only sane way the kernel can possibly handle | ||
154 | * it is to clear TF on return from the #DB handler, but this | ||
155 | * happens way too early to set TF in the saved pt_regs, so the | ||
156 | * kernel has to do something clever to avoid losing track of | ||
157 | * the TF bit. | ||
158 | * | ||
159 | * Needless to say, we've had bugs in this area. | ||
160 | */ | ||
161 | syscall(SYS_getpid); /* Force symbol binding without TF set. */ | ||
162 | printf("[RUN]\tSet TF and check a fast syscall\n"); | ||
163 | set_eflags(get_eflags() | X86_EFLAGS_TF); | ||
164 | syscall(SYS_getpid); | ||
165 | check_result(); | ||
166 | |||
167 | /* Now make sure that another fast syscall doesn't set TF again. */ | ||
168 | printf("[RUN]\tFast syscall with TF cleared\n"); | ||
169 | fflush(stdout); /* Force a syscall */ | ||
170 | if (get_eflags() & X86_EFLAGS_TF) { | ||
171 | printf("[FAIL]\tTF is now set\n"); | ||
172 | exit(1); | ||
173 | } | ||
174 | if (sig_traps) { | ||
175 | printf("[FAIL]\tGot SIGTRAP\n"); | ||
176 | exit(1); | ||
177 | } | ||
178 | printf("[OK]\tNothing unexpected happened\n"); | ||
179 | |||
180 | return 0; | ||
181 | } | ||