diff options
author | Ingo Molnar <mingo@kernel.org> | 2015-08-03 05:04:00 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2015-08-03 05:04:00 -0400 |
commit | f320ead76a87a9c533f681ecce3bf3241d07c47d (patch) | |
tree | 76ae437119088b180737670cb99a307e163a1ca0 /tools | |
parent | 76695af20c015206cffb84b15912be6797d0cca2 (diff) | |
parent | decd275e62d5eef4b947fab89652fa6afdadf2f2 (diff) |
Merge branch 'x86/asm' into locking/core
Upcoming changes to static keys is interacting/conflicting with the following
pending TSC commits in tip:x86/asm:
4ea1636b04db x86/asm/tsc: Rename native_read_tsc() to rdtsc()
...
So merge it into the locking tree to have a smoother resolution.
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c | 4 | ||||
-rw-r--r-- | tools/testing/selftests/x86/Makefile | 4 | ||||
-rw-r--r-- | tools/testing/selftests/x86/entry_from_vm86.c | 139 | ||||
-rw-r--r-- | tools/testing/selftests/x86/ldt_gdt.c | 576 | ||||
-rw-r--r-- | tools/testing/selftests/x86/syscall_arg_fault.c | 130 |
5 files changed, 839 insertions, 14 deletions
diff --git a/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c b/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c index 5224ee5b392d..6ff8383f2941 100644 --- a/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c +++ b/tools/power/cpupower/debug/kernel/cpufreq-test_tsc.c | |||
@@ -81,11 +81,11 @@ static int __init cpufreq_test_tsc(void) | |||
81 | 81 | ||
82 | printk(KERN_DEBUG "start--> \n"); | 82 | printk(KERN_DEBUG "start--> \n"); |
83 | then = read_pmtmr(); | 83 | then = read_pmtmr(); |
84 | rdtscll(then_tsc); | 84 | then_tsc = rdtsc(); |
85 | for (i=0;i<20;i++) { | 85 | for (i=0;i<20;i++) { |
86 | mdelay(100); | 86 | mdelay(100); |
87 | now = read_pmtmr(); | 87 | now = read_pmtmr(); |
88 | rdtscll(now_tsc); | 88 | now_tsc = rdtsc(); |
89 | diff = (now - then) & 0xFFFFFF; | 89 | diff = (now - then) & 0xFFFFFF; |
90 | diff_tsc = now_tsc - then_tsc; | 90 | diff_tsc = now_tsc - then_tsc; |
91 | printk(KERN_DEBUG "t1: %08u t2: %08u diff_pmtmr: %08u diff_tsc: %016llu\n", then, now, diff, diff_tsc); | 91 | printk(KERN_DEBUG "t1: %08u t2: %08u diff_pmtmr: %08u diff_tsc: %016llu\n", then, now, diff, diff_tsc); |
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index caa60d56d7d1..b70da4acb4e5 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile | |||
@@ -4,8 +4,8 @@ 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 := sigreturn single_step_syscall sysret_ss_attrs | 7 | TARGETS_C_BOTHBITS := sigreturn single_step_syscall sysret_ss_attrs ldt_gdt |
8 | TARGETS_C_32BIT_ONLY := entry_from_vm86 | 8 | TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault |
9 | 9 | ||
10 | TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) | 10 | TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) |
11 | BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32) | 11 | BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32) |
diff --git a/tools/testing/selftests/x86/entry_from_vm86.c b/tools/testing/selftests/x86/entry_from_vm86.c index 5c38a187677b..9a43a59a9bb4 100644 --- a/tools/testing/selftests/x86/entry_from_vm86.c +++ b/tools/testing/selftests/x86/entry_from_vm86.c | |||
@@ -28,6 +28,55 @@ | |||
28 | static unsigned long load_addr = 0x10000; | 28 | static unsigned long load_addr = 0x10000; |
29 | static int nerrs = 0; | 29 | static int nerrs = 0; |
30 | 30 | ||
31 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), | ||
32 | int flags) | ||
33 | { | ||
34 | struct sigaction sa; | ||
35 | memset(&sa, 0, sizeof(sa)); | ||
36 | sa.sa_sigaction = handler; | ||
37 | sa.sa_flags = SA_SIGINFO | flags; | ||
38 | sigemptyset(&sa.sa_mask); | ||
39 | if (sigaction(sig, &sa, 0)) | ||
40 | err(1, "sigaction"); | ||
41 | } | ||
42 | |||
43 | static void clearhandler(int sig) | ||
44 | { | ||
45 | struct sigaction sa; | ||
46 | memset(&sa, 0, sizeof(sa)); | ||
47 | sa.sa_handler = SIG_DFL; | ||
48 | sigemptyset(&sa.sa_mask); | ||
49 | if (sigaction(sig, &sa, 0)) | ||
50 | err(1, "sigaction"); | ||
51 | } | ||
52 | |||
53 | static sig_atomic_t got_signal; | ||
54 | |||
55 | static void sighandler(int sig, siginfo_t *info, void *ctx_void) | ||
56 | { | ||
57 | ucontext_t *ctx = (ucontext_t*)ctx_void; | ||
58 | |||
59 | if (ctx->uc_mcontext.gregs[REG_EFL] & X86_EFLAGS_VM || | ||
60 | (ctx->uc_mcontext.gregs[REG_CS] & 3) != 3) { | ||
61 | printf("[FAIL]\tSignal frame should not reflect vm86 mode\n"); | ||
62 | nerrs++; | ||
63 | } | ||
64 | |||
65 | const char *signame; | ||
66 | if (sig == SIGSEGV) | ||
67 | signame = "SIGSEGV"; | ||
68 | else if (sig == SIGILL) | ||
69 | signame = "SIGILL"; | ||
70 | else | ||
71 | signame = "unexpected signal"; | ||
72 | |||
73 | printf("[INFO]\t%s: FLAGS = 0x%lx, CS = 0x%hx\n", signame, | ||
74 | (unsigned long)ctx->uc_mcontext.gregs[REG_EFL], | ||
75 | (unsigned short)ctx->uc_mcontext.gregs[REG_CS]); | ||
76 | |||
77 | got_signal = 1; | ||
78 | } | ||
79 | |||
31 | asm ( | 80 | asm ( |
32 | ".pushsection .rodata\n\t" | 81 | ".pushsection .rodata\n\t" |
33 | ".type vmcode_bound, @object\n\t" | 82 | ".type vmcode_bound, @object\n\t" |
@@ -38,6 +87,14 @@ asm ( | |||
38 | "int3\n\t" | 87 | "int3\n\t" |
39 | "vmcode_sysenter:\n\t" | 88 | "vmcode_sysenter:\n\t" |
40 | "sysenter\n\t" | 89 | "sysenter\n\t" |
90 | "vmcode_syscall:\n\t" | ||
91 | "syscall\n\t" | ||
92 | "vmcode_sti:\n\t" | ||
93 | "sti\n\t" | ||
94 | "vmcode_int3:\n\t" | ||
95 | "int3\n\t" | ||
96 | "vmcode_int80:\n\t" | ||
97 | "int $0x80\n\t" | ||
41 | ".size vmcode, . - vmcode\n\t" | 98 | ".size vmcode, . - vmcode\n\t" |
42 | "end_vmcode:\n\t" | 99 | "end_vmcode:\n\t" |
43 | ".code32\n\t" | 100 | ".code32\n\t" |
@@ -45,9 +102,12 @@ asm ( | |||
45 | ); | 102 | ); |
46 | 103 | ||
47 | extern unsigned char vmcode[], end_vmcode[]; | 104 | extern unsigned char vmcode[], end_vmcode[]; |
48 | extern unsigned char vmcode_bound[], vmcode_sysenter[]; | 105 | extern unsigned char vmcode_bound[], vmcode_sysenter[], vmcode_syscall[], |
106 | vmcode_sti[], vmcode_int3[], vmcode_int80[]; | ||
49 | 107 | ||
50 | static void do_test(struct vm86plus_struct *v86, unsigned long eip, | 108 | /* Returns false if the test was skipped. */ |
109 | static bool do_test(struct vm86plus_struct *v86, unsigned long eip, | ||
110 | unsigned int rettype, unsigned int retarg, | ||
51 | const char *text) | 111 | const char *text) |
52 | { | 112 | { |
53 | long ret; | 113 | long ret; |
@@ -58,7 +118,7 @@ static void do_test(struct vm86plus_struct *v86, unsigned long eip, | |||
58 | 118 | ||
59 | if (ret == -1 && errno == ENOSYS) { | 119 | if (ret == -1 && errno == ENOSYS) { |
60 | printf("[SKIP]\tvm86 not supported\n"); | 120 | printf("[SKIP]\tvm86 not supported\n"); |
61 | return; | 121 | return false; |
62 | } | 122 | } |
63 | 123 | ||
64 | if (VM86_TYPE(ret) == VM86_INTx) { | 124 | if (VM86_TYPE(ret) == VM86_INTx) { |
@@ -73,13 +133,30 @@ static void do_test(struct vm86plus_struct *v86, unsigned long eip, | |||
73 | else | 133 | else |
74 | sprintf(trapname, "%d", trapno); | 134 | sprintf(trapname, "%d", trapno); |
75 | 135 | ||
76 | printf("[OK]\tExited vm86 mode due to #%s\n", trapname); | 136 | printf("[INFO]\tExited vm86 mode due to #%s\n", trapname); |
77 | } else if (VM86_TYPE(ret) == VM86_UNKNOWN) { | 137 | } else if (VM86_TYPE(ret) == VM86_UNKNOWN) { |
78 | printf("[OK]\tExited vm86 mode due to unhandled GP fault\n"); | 138 | printf("[INFO]\tExited vm86 mode due to unhandled GP fault\n"); |
139 | } else if (VM86_TYPE(ret) == VM86_TRAP) { | ||
140 | printf("[INFO]\tExited vm86 mode due to a trap (arg=%ld)\n", | ||
141 | VM86_ARG(ret)); | ||
142 | } else if (VM86_TYPE(ret) == VM86_SIGNAL) { | ||
143 | printf("[INFO]\tExited vm86 mode due to a signal\n"); | ||
144 | } else if (VM86_TYPE(ret) == VM86_STI) { | ||
145 | printf("[INFO]\tExited vm86 mode due to STI\n"); | ||
79 | } else { | 146 | } else { |
80 | printf("[OK]\tExited vm86 mode due to type %ld, arg %ld\n", | 147 | printf("[INFO]\tExited vm86 mode due to type %ld, arg %ld\n", |
81 | VM86_TYPE(ret), VM86_ARG(ret)); | 148 | VM86_TYPE(ret), VM86_ARG(ret)); |
82 | } | 149 | } |
150 | |||
151 | if (rettype == -1 || | ||
152 | (VM86_TYPE(ret) == rettype && VM86_ARG(ret) == retarg)) { | ||
153 | printf("[OK]\tReturned correctly\n"); | ||
154 | } else { | ||
155 | printf("[FAIL]\tIncorrect return reason\n"); | ||
156 | nerrs++; | ||
157 | } | ||
158 | |||
159 | return true; | ||
83 | } | 160 | } |
84 | 161 | ||
85 | int main(void) | 162 | int main(void) |
@@ -105,10 +182,52 @@ int main(void) | |||
105 | assert((v86.regs.cs & 3) == 0); /* Looks like RPL = 0 */ | 182 | assert((v86.regs.cs & 3) == 0); /* Looks like RPL = 0 */ |
106 | 183 | ||
107 | /* #BR -- should deliver SIG??? */ | 184 | /* #BR -- should deliver SIG??? */ |
108 | do_test(&v86, vmcode_bound - vmcode, "#BR"); | 185 | do_test(&v86, vmcode_bound - vmcode, VM86_INTx, 5, "#BR"); |
109 | 186 | ||
110 | /* SYSENTER -- should cause #GP or #UD depending on CPU */ | 187 | /* |
111 | do_test(&v86, vmcode_sysenter - vmcode, "SYSENTER"); | 188 | * SYSENTER -- should cause #GP or #UD depending on CPU. |
189 | * Expected return type -1 means that we shouldn't validate | ||
190 | * the vm86 return value. This will avoid problems on non-SEP | ||
191 | * CPUs. | ||
192 | */ | ||
193 | sethandler(SIGILL, sighandler, 0); | ||
194 | do_test(&v86, vmcode_sysenter - vmcode, -1, 0, "SYSENTER"); | ||
195 | clearhandler(SIGILL); | ||
196 | |||
197 | /* | ||
198 | * SYSCALL would be a disaster in VM86 mode. Fortunately, | ||
199 | * there is no kernel that both enables SYSCALL and sets | ||
200 | * EFER.SCE, so it's #UD on all systems. But vm86 is | ||
201 | * buggy (or has a "feature"), so the SIGILL will actually | ||
202 | * be delivered. | ||
203 | */ | ||
204 | sethandler(SIGILL, sighandler, 0); | ||
205 | do_test(&v86, vmcode_syscall - vmcode, VM86_SIGNAL, 0, "SYSCALL"); | ||
206 | clearhandler(SIGILL); | ||
207 | |||
208 | /* STI with VIP set */ | ||
209 | v86.regs.eflags |= X86_EFLAGS_VIP; | ||
210 | v86.regs.eflags &= ~X86_EFLAGS_IF; | ||
211 | do_test(&v86, vmcode_sti - vmcode, VM86_STI, 0, "STI with VIP set"); | ||
212 | |||
213 | /* INT3 -- should cause #BP */ | ||
214 | do_test(&v86, vmcode_int3 - vmcode, VM86_TRAP, 3, "INT3"); | ||
215 | |||
216 | /* INT80 -- should exit with "INTx 0x80" */ | ||
217 | v86.regs.eax = (unsigned int)-1; | ||
218 | do_test(&v86, vmcode_int80 - vmcode, VM86_INTx, 0x80, "int80"); | ||
219 | |||
220 | /* Execute a null pointer */ | ||
221 | v86.regs.cs = 0; | ||
222 | v86.regs.ss = 0; | ||
223 | sethandler(SIGSEGV, sighandler, 0); | ||
224 | got_signal = 0; | ||
225 | if (do_test(&v86, 0, VM86_SIGNAL, 0, "Execute null pointer") && | ||
226 | !got_signal) { | ||
227 | printf("[FAIL]\tDid not receive SIGSEGV\n"); | ||
228 | nerrs++; | ||
229 | } | ||
230 | clearhandler(SIGSEGV); | ||
112 | 231 | ||
113 | return (nerrs == 0 ? 0 : 1); | 232 | return (nerrs == 0 ? 0 : 1); |
114 | } | 233 | } |
diff --git a/tools/testing/selftests/x86/ldt_gdt.c b/tools/testing/selftests/x86/ldt_gdt.c new file mode 100644 index 000000000000..31a3035cd4eb --- /dev/null +++ b/tools/testing/selftests/x86/ldt_gdt.c | |||
@@ -0,0 +1,576 @@ | |||
1 | /* | ||
2 | * ldt_gdt.c - Test cases for LDT and GDT access | ||
3 | * Copyright (c) 2015 Andrew Lutomirski | ||
4 | */ | ||
5 | |||
6 | #define _GNU_SOURCE | ||
7 | #include <err.h> | ||
8 | #include <stdio.h> | ||
9 | #include <stdint.h> | ||
10 | #include <signal.h> | ||
11 | #include <setjmp.h> | ||
12 | #include <stdlib.h> | ||
13 | #include <string.h> | ||
14 | #include <errno.h> | ||
15 | #include <unistd.h> | ||
16 | #include <sys/syscall.h> | ||
17 | #include <asm/ldt.h> | ||
18 | #include <sys/types.h> | ||
19 | #include <sys/wait.h> | ||
20 | #include <stdbool.h> | ||
21 | #include <pthread.h> | ||
22 | #include <sched.h> | ||
23 | #include <linux/futex.h> | ||
24 | |||
25 | #define AR_ACCESSED (1<<8) | ||
26 | |||
27 | #define AR_TYPE_RODATA (0 * (1<<9)) | ||
28 | #define AR_TYPE_RWDATA (1 * (1<<9)) | ||
29 | #define AR_TYPE_RODATA_EXPDOWN (2 * (1<<9)) | ||
30 | #define AR_TYPE_RWDATA_EXPDOWN (3 * (1<<9)) | ||
31 | #define AR_TYPE_XOCODE (4 * (1<<9)) | ||
32 | #define AR_TYPE_XRCODE (5 * (1<<9)) | ||
33 | #define AR_TYPE_XOCODE_CONF (6 * (1<<9)) | ||
34 | #define AR_TYPE_XRCODE_CONF (7 * (1<<9)) | ||
35 | |||
36 | #define AR_DPL3 (3 * (1<<13)) | ||
37 | |||
38 | #define AR_S (1 << 12) | ||
39 | #define AR_P (1 << 15) | ||
40 | #define AR_AVL (1 << 20) | ||
41 | #define AR_L (1 << 21) | ||
42 | #define AR_DB (1 << 22) | ||
43 | #define AR_G (1 << 23) | ||
44 | |||
45 | static int nerrs; | ||
46 | |||
47 | static void check_invalid_segment(uint16_t index, int ldt) | ||
48 | { | ||
49 | uint32_t has_limit = 0, has_ar = 0, limit, ar; | ||
50 | uint32_t selector = (index << 3) | (ldt << 2) | 3; | ||
51 | |||
52 | asm ("lsl %[selector], %[limit]\n\t" | ||
53 | "jnz 1f\n\t" | ||
54 | "movl $1, %[has_limit]\n\t" | ||
55 | "1:" | ||
56 | : [limit] "=r" (limit), [has_limit] "+rm" (has_limit) | ||
57 | : [selector] "r" (selector)); | ||
58 | asm ("larl %[selector], %[ar]\n\t" | ||
59 | "jnz 1f\n\t" | ||
60 | "movl $1, %[has_ar]\n\t" | ||
61 | "1:" | ||
62 | : [ar] "=r" (ar), [has_ar] "+rm" (has_ar) | ||
63 | : [selector] "r" (selector)); | ||
64 | |||
65 | if (has_limit || has_ar) { | ||
66 | printf("[FAIL]\t%s entry %hu is valid but should be invalid\n", | ||
67 | (ldt ? "LDT" : "GDT"), index); | ||
68 | nerrs++; | ||
69 | } else { | ||
70 | printf("[OK]\t%s entry %hu is invalid\n", | ||
71 | (ldt ? "LDT" : "GDT"), index); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | static void check_valid_segment(uint16_t index, int ldt, | ||
76 | uint32_t expected_ar, uint32_t expected_limit, | ||
77 | bool verbose) | ||
78 | { | ||
79 | uint32_t has_limit = 0, has_ar = 0, limit, ar; | ||
80 | uint32_t selector = (index << 3) | (ldt << 2) | 3; | ||
81 | |||
82 | asm ("lsl %[selector], %[limit]\n\t" | ||
83 | "jnz 1f\n\t" | ||
84 | "movl $1, %[has_limit]\n\t" | ||
85 | "1:" | ||
86 | : [limit] "=r" (limit), [has_limit] "+rm" (has_limit) | ||
87 | : [selector] "r" (selector)); | ||
88 | asm ("larl %[selector], %[ar]\n\t" | ||
89 | "jnz 1f\n\t" | ||
90 | "movl $1, %[has_ar]\n\t" | ||
91 | "1:" | ||
92 | : [ar] "=r" (ar), [has_ar] "+rm" (has_ar) | ||
93 | : [selector] "r" (selector)); | ||
94 | |||
95 | if (!has_limit || !has_ar) { | ||
96 | printf("[FAIL]\t%s entry %hu is invalid but should be valid\n", | ||
97 | (ldt ? "LDT" : "GDT"), index); | ||
98 | nerrs++; | ||
99 | return; | ||
100 | } | ||
101 | |||
102 | if (ar != expected_ar) { | ||
103 | printf("[FAIL]\t%s entry %hu has AR 0x%08X but expected 0x%08X\n", | ||
104 | (ldt ? "LDT" : "GDT"), index, ar, expected_ar); | ||
105 | nerrs++; | ||
106 | } else if (limit != expected_limit) { | ||
107 | printf("[FAIL]\t%s entry %hu has limit 0x%08X but expected 0x%08X\n", | ||
108 | (ldt ? "LDT" : "GDT"), index, limit, expected_limit); | ||
109 | nerrs++; | ||
110 | } else if (verbose) { | ||
111 | printf("[OK]\t%s entry %hu has AR 0x%08X and limit 0x%08X\n", | ||
112 | (ldt ? "LDT" : "GDT"), index, ar, limit); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | static bool install_valid_mode(const struct user_desc *desc, uint32_t ar, | ||
117 | bool oldmode) | ||
118 | { | ||
119 | int ret = syscall(SYS_modify_ldt, oldmode ? 1 : 0x11, | ||
120 | desc, sizeof(*desc)); | ||
121 | if (ret < -1) | ||
122 | errno = -ret; | ||
123 | if (ret == 0) { | ||
124 | uint32_t limit = desc->limit; | ||
125 | if (desc->limit_in_pages) | ||
126 | limit = (limit << 12) + 4095; | ||
127 | check_valid_segment(desc->entry_number, 1, ar, limit, true); | ||
128 | return true; | ||
129 | } else if (errno == ENOSYS) { | ||
130 | printf("[OK]\tmodify_ldt returned -ENOSYS\n"); | ||
131 | return false; | ||
132 | } else { | ||
133 | if (desc->seg_32bit) { | ||
134 | printf("[FAIL]\tUnexpected modify_ldt failure %d\n", | ||
135 | errno); | ||
136 | nerrs++; | ||
137 | return false; | ||
138 | } else { | ||
139 | printf("[OK]\tmodify_ldt rejected 16 bit segment\n"); | ||
140 | return false; | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | |||
145 | static bool install_valid(const struct user_desc *desc, uint32_t ar) | ||
146 | { | ||
147 | return install_valid_mode(desc, ar, false); | ||
148 | } | ||
149 | |||
150 | static void install_invalid(const struct user_desc *desc, bool oldmode) | ||
151 | { | ||
152 | int ret = syscall(SYS_modify_ldt, oldmode ? 1 : 0x11, | ||
153 | desc, sizeof(*desc)); | ||
154 | if (ret < -1) | ||
155 | errno = -ret; | ||
156 | if (ret == 0) { | ||
157 | check_invalid_segment(desc->entry_number, 1); | ||
158 | } else if (errno == ENOSYS) { | ||
159 | printf("[OK]\tmodify_ldt returned -ENOSYS\n"); | ||
160 | } else { | ||
161 | if (desc->seg_32bit) { | ||
162 | printf("[FAIL]\tUnexpected modify_ldt failure %d\n", | ||
163 | errno); | ||
164 | nerrs++; | ||
165 | } else { | ||
166 | printf("[OK]\tmodify_ldt rejected 16 bit segment\n"); | ||
167 | } | ||
168 | } | ||
169 | } | ||
170 | |||
171 | static int safe_modify_ldt(int func, struct user_desc *ptr, | ||
172 | unsigned long bytecount) | ||
173 | { | ||
174 | int ret = syscall(SYS_modify_ldt, 0x11, ptr, bytecount); | ||
175 | if (ret < -1) | ||
176 | errno = -ret; | ||
177 | return ret; | ||
178 | } | ||
179 | |||
180 | static void fail_install(struct user_desc *desc) | ||
181 | { | ||
182 | if (safe_modify_ldt(0x11, desc, sizeof(*desc)) == 0) { | ||
183 | printf("[FAIL]\tmodify_ldt accepted a bad descriptor\n"); | ||
184 | nerrs++; | ||
185 | } else if (errno == ENOSYS) { | ||
186 | printf("[OK]\tmodify_ldt returned -ENOSYS\n"); | ||
187 | } else { | ||
188 | printf("[OK]\tmodify_ldt failure %d\n", errno); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | static void do_simple_tests(void) | ||
193 | { | ||
194 | struct user_desc desc = { | ||
195 | .entry_number = 0, | ||
196 | .base_addr = 0, | ||
197 | .limit = 10, | ||
198 | .seg_32bit = 1, | ||
199 | .contents = 2, /* Code, not conforming */ | ||
200 | .read_exec_only = 0, | ||
201 | .limit_in_pages = 0, | ||
202 | .seg_not_present = 0, | ||
203 | .useable = 0 | ||
204 | }; | ||
205 | install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | AR_S | AR_P | AR_DB); | ||
206 | |||
207 | desc.limit_in_pages = 1; | ||
208 | install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | | ||
209 | AR_S | AR_P | AR_DB | AR_G); | ||
210 | |||
211 | check_invalid_segment(1, 1); | ||
212 | |||
213 | desc.entry_number = 2; | ||
214 | install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | | ||
215 | AR_S | AR_P | AR_DB | AR_G); | ||
216 | |||
217 | check_invalid_segment(1, 1); | ||
218 | |||
219 | desc.base_addr = 0xf0000000; | ||
220 | install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | | ||
221 | AR_S | AR_P | AR_DB | AR_G); | ||
222 | |||
223 | desc.useable = 1; | ||
224 | install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | | ||
225 | AR_S | AR_P | AR_DB | AR_G | AR_AVL); | ||
226 | |||
227 | desc.seg_not_present = 1; | ||
228 | install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | | ||
229 | AR_S | AR_DB | AR_G | AR_AVL); | ||
230 | |||
231 | desc.seg_32bit = 0; | ||
232 | install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | | ||
233 | AR_S | AR_G | AR_AVL); | ||
234 | |||
235 | desc.seg_32bit = 1; | ||
236 | desc.contents = 0; | ||
237 | install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | | ||
238 | AR_S | AR_DB | AR_G | AR_AVL); | ||
239 | |||
240 | desc.read_exec_only = 1; | ||
241 | install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | | ||
242 | AR_S | AR_DB | AR_G | AR_AVL); | ||
243 | |||
244 | desc.contents = 1; | ||
245 | install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA_EXPDOWN | | ||
246 | AR_S | AR_DB | AR_G | AR_AVL); | ||
247 | |||
248 | desc.read_exec_only = 0; | ||
249 | desc.limit_in_pages = 0; | ||
250 | install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA_EXPDOWN | | ||
251 | AR_S | AR_DB | AR_AVL); | ||
252 | |||
253 | desc.contents = 3; | ||
254 | install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE_CONF | | ||
255 | AR_S | AR_DB | AR_AVL); | ||
256 | |||
257 | desc.read_exec_only = 1; | ||
258 | install_valid(&desc, AR_DPL3 | AR_TYPE_XOCODE_CONF | | ||
259 | AR_S | AR_DB | AR_AVL); | ||
260 | |||
261 | desc.read_exec_only = 0; | ||
262 | desc.contents = 2; | ||
263 | install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | | ||
264 | AR_S | AR_DB | AR_AVL); | ||
265 | |||
266 | desc.read_exec_only = 1; | ||
267 | |||
268 | #ifdef __x86_64__ | ||
269 | desc.lm = 1; | ||
270 | install_valid(&desc, AR_DPL3 | AR_TYPE_XOCODE | | ||
271 | AR_S | AR_DB | AR_AVL); | ||
272 | desc.lm = 0; | ||
273 | #endif | ||
274 | |||
275 | bool entry1_okay = install_valid(&desc, AR_DPL3 | AR_TYPE_XOCODE | | ||
276 | AR_S | AR_DB | AR_AVL); | ||
277 | |||
278 | if (entry1_okay) { | ||
279 | printf("[RUN]\tTest fork\n"); | ||
280 | pid_t child = fork(); | ||
281 | if (child == 0) { | ||
282 | nerrs = 0; | ||
283 | check_valid_segment(desc.entry_number, 1, | ||
284 | AR_DPL3 | AR_TYPE_XOCODE | | ||
285 | AR_S | AR_DB | AR_AVL, desc.limit, | ||
286 | true); | ||
287 | check_invalid_segment(1, 1); | ||
288 | exit(nerrs ? 1 : 0); | ||
289 | } else { | ||
290 | int status; | ||
291 | if (waitpid(child, &status, 0) != child || | ||
292 | !WIFEXITED(status)) { | ||
293 | printf("[FAIL]\tChild died\n"); | ||
294 | nerrs++; | ||
295 | } else if (WEXITSTATUS(status) != 0) { | ||
296 | printf("[FAIL]\tChild failed\n"); | ||
297 | nerrs++; | ||
298 | } else { | ||
299 | printf("[OK]\tChild succeeded\n"); | ||
300 | } | ||
301 | } | ||
302 | |||
303 | printf("[RUN]\tTest size\n"); | ||
304 | int i; | ||
305 | for (i = 0; i < 8192; i++) { | ||
306 | desc.entry_number = i; | ||
307 | desc.limit = i; | ||
308 | if (safe_modify_ldt(0x11, &desc, sizeof(desc)) != 0) { | ||
309 | printf("[FAIL]\tFailed to install entry %d\n", i); | ||
310 | nerrs++; | ||
311 | break; | ||
312 | } | ||
313 | } | ||
314 | for (int j = 0; j < i; j++) { | ||
315 | check_valid_segment(j, 1, AR_DPL3 | AR_TYPE_XOCODE | | ||
316 | AR_S | AR_DB | AR_AVL, j, false); | ||
317 | } | ||
318 | printf("[DONE]\tSize test\n"); | ||
319 | } else { | ||
320 | printf("[SKIP]\tSkipping fork and size tests because we have no LDT\n"); | ||
321 | } | ||
322 | |||
323 | /* Test entry_number too high. */ | ||
324 | desc.entry_number = 8192; | ||
325 | fail_install(&desc); | ||
326 | |||
327 | /* Test deletion and actions mistakeable for deletion. */ | ||
328 | memset(&desc, 0, sizeof(desc)); | ||
329 | install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S | AR_P); | ||
330 | |||
331 | desc.seg_not_present = 1; | ||
332 | install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S); | ||
333 | |||
334 | desc.seg_not_present = 0; | ||
335 | desc.read_exec_only = 1; | ||
336 | install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | AR_S | AR_P); | ||
337 | |||
338 | desc.read_exec_only = 0; | ||
339 | desc.seg_not_present = 1; | ||
340 | install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S); | ||
341 | |||
342 | desc.read_exec_only = 1; | ||
343 | desc.limit = 1; | ||
344 | install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | AR_S); | ||
345 | |||
346 | desc.limit = 0; | ||
347 | desc.base_addr = 1; | ||
348 | install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | AR_S); | ||
349 | |||
350 | desc.base_addr = 0; | ||
351 | install_invalid(&desc, false); | ||
352 | |||
353 | desc.seg_not_present = 0; | ||
354 | desc.read_exec_only = 0; | ||
355 | desc.seg_32bit = 1; | ||
356 | install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S | AR_P | AR_DB); | ||
357 | install_invalid(&desc, true); | ||
358 | } | ||
359 | |||
360 | /* | ||
361 | * 0: thread is idle | ||
362 | * 1: thread armed | ||
363 | * 2: thread should clear LDT entry 0 | ||
364 | * 3: thread should exit | ||
365 | */ | ||
366 | static volatile unsigned int ftx; | ||
367 | |||
368 | static void *threadproc(void *ctx) | ||
369 | { | ||
370 | cpu_set_t cpuset; | ||
371 | CPU_ZERO(&cpuset); | ||
372 | CPU_SET(1, &cpuset); | ||
373 | if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) | ||
374 | err(1, "sched_setaffinity to CPU 1"); /* should never fail */ | ||
375 | |||
376 | while (1) { | ||
377 | syscall(SYS_futex, &ftx, FUTEX_WAIT, 0, NULL, NULL, 0); | ||
378 | while (ftx != 2) { | ||
379 | if (ftx >= 3) | ||
380 | return NULL; | ||
381 | } | ||
382 | |||
383 | /* clear LDT entry 0 */ | ||
384 | const struct user_desc desc = {}; | ||
385 | if (syscall(SYS_modify_ldt, 1, &desc, sizeof(desc)) != 0) | ||
386 | err(1, "modify_ldt"); | ||
387 | |||
388 | /* If ftx == 2, set it to zero. If ftx == 100, quit. */ | ||
389 | unsigned int x = -2; | ||
390 | asm volatile ("lock xaddl %[x], %[ftx]" : | ||
391 | [x] "+r" (x), [ftx] "+m" (ftx)); | ||
392 | if (x != 2) | ||
393 | return NULL; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), | ||
398 | int flags) | ||
399 | { | ||
400 | struct sigaction sa; | ||
401 | memset(&sa, 0, sizeof(sa)); | ||
402 | sa.sa_sigaction = handler; | ||
403 | sa.sa_flags = SA_SIGINFO | flags; | ||
404 | sigemptyset(&sa.sa_mask); | ||
405 | if (sigaction(sig, &sa, 0)) | ||
406 | err(1, "sigaction"); | ||
407 | |||
408 | } | ||
409 | |||
410 | static jmp_buf jmpbuf; | ||
411 | |||
412 | static void sigsegv(int sig, siginfo_t *info, void *ctx_void) | ||
413 | { | ||
414 | siglongjmp(jmpbuf, 1); | ||
415 | } | ||
416 | |||
417 | static void do_multicpu_tests(void) | ||
418 | { | ||
419 | cpu_set_t cpuset; | ||
420 | pthread_t thread; | ||
421 | int failures = 0, iters = 5, i; | ||
422 | unsigned short orig_ss; | ||
423 | |||
424 | CPU_ZERO(&cpuset); | ||
425 | CPU_SET(1, &cpuset); | ||
426 | if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) { | ||
427 | printf("[SKIP]\tCannot set affinity to CPU 1\n"); | ||
428 | return; | ||
429 | } | ||
430 | |||
431 | CPU_ZERO(&cpuset); | ||
432 | CPU_SET(0, &cpuset); | ||
433 | if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) { | ||
434 | printf("[SKIP]\tCannot set affinity to CPU 0\n"); | ||
435 | return; | ||
436 | } | ||
437 | |||
438 | sethandler(SIGSEGV, sigsegv, 0); | ||
439 | #ifdef __i386__ | ||
440 | /* True 32-bit kernels send SIGILL instead of SIGSEGV on IRET faults. */ | ||
441 | sethandler(SIGILL, sigsegv, 0); | ||
442 | #endif | ||
443 | |||
444 | printf("[RUN]\tCross-CPU LDT invalidation\n"); | ||
445 | |||
446 | if (pthread_create(&thread, 0, threadproc, 0) != 0) | ||
447 | err(1, "pthread_create"); | ||
448 | |||
449 | asm volatile ("mov %%ss, %0" : "=rm" (orig_ss)); | ||
450 | |||
451 | for (i = 0; i < 5; i++) { | ||
452 | if (sigsetjmp(jmpbuf, 1) != 0) | ||
453 | continue; | ||
454 | |||
455 | /* Make sure the thread is ready after the last test. */ | ||
456 | while (ftx != 0) | ||
457 | ; | ||
458 | |||
459 | struct user_desc desc = { | ||
460 | .entry_number = 0, | ||
461 | .base_addr = 0, | ||
462 | .limit = 0xfffff, | ||
463 | .seg_32bit = 1, | ||
464 | .contents = 0, /* Data */ | ||
465 | .read_exec_only = 0, | ||
466 | .limit_in_pages = 1, | ||
467 | .seg_not_present = 0, | ||
468 | .useable = 0 | ||
469 | }; | ||
470 | |||
471 | if (safe_modify_ldt(0x11, &desc, sizeof(desc)) != 0) { | ||
472 | if (errno != ENOSYS) | ||
473 | err(1, "modify_ldt"); | ||
474 | printf("[SKIP]\tmodify_ldt unavailable\n"); | ||
475 | break; | ||
476 | } | ||
477 | |||
478 | /* Arm the thread. */ | ||
479 | ftx = 1; | ||
480 | syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); | ||
481 | |||
482 | asm volatile ("mov %0, %%ss" : : "r" (0x7)); | ||
483 | |||
484 | /* Go! */ | ||
485 | ftx = 2; | ||
486 | |||
487 | while (ftx != 0) | ||
488 | ; | ||
489 | |||
490 | /* | ||
491 | * On success, modify_ldt will segfault us synchronously, | ||
492 | * and we'll escape via siglongjmp. | ||
493 | */ | ||
494 | |||
495 | failures++; | ||
496 | asm volatile ("mov %0, %%ss" : : "rm" (orig_ss)); | ||
497 | }; | ||
498 | |||
499 | ftx = 100; /* Kill the thread. */ | ||
500 | syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); | ||
501 | |||
502 | if (pthread_join(thread, NULL) != 0) | ||
503 | err(1, "pthread_join"); | ||
504 | |||
505 | if (failures) { | ||
506 | printf("[FAIL]\t%d of %d iterations failed\n", failures, iters); | ||
507 | nerrs++; | ||
508 | } else { | ||
509 | printf("[OK]\tAll %d iterations succeeded\n", iters); | ||
510 | } | ||
511 | } | ||
512 | |||
513 | static int finish_exec_test(void) | ||
514 | { | ||
515 | /* | ||
516 | * In a sensible world, this would be check_invalid_segment(0, 1); | ||
517 | * For better or for worse, though, the LDT is inherited across exec. | ||
518 | * We can probably change this safely, but for now we test it. | ||
519 | */ | ||
520 | check_valid_segment(0, 1, | ||
521 | AR_DPL3 | AR_TYPE_XRCODE | AR_S | AR_P | AR_DB, | ||
522 | 42, true); | ||
523 | |||
524 | return nerrs ? 1 : 0; | ||
525 | } | ||
526 | |||
527 | static void do_exec_test(void) | ||
528 | { | ||
529 | printf("[RUN]\tTest exec\n"); | ||
530 | |||
531 | struct user_desc desc = { | ||
532 | .entry_number = 0, | ||
533 | .base_addr = 0, | ||
534 | .limit = 42, | ||
535 | .seg_32bit = 1, | ||
536 | .contents = 2, /* Code, not conforming */ | ||
537 | .read_exec_only = 0, | ||
538 | .limit_in_pages = 0, | ||
539 | .seg_not_present = 0, | ||
540 | .useable = 0 | ||
541 | }; | ||
542 | install_valid(&desc, AR_DPL3 | AR_TYPE_XRCODE | AR_S | AR_P | AR_DB); | ||
543 | |||
544 | pid_t child = fork(); | ||
545 | if (child == 0) { | ||
546 | execl("/proc/self/exe", "ldt_gdt_test_exec", NULL); | ||
547 | printf("[FAIL]\tCould not exec self\n"); | ||
548 | exit(1); /* exec failed */ | ||
549 | } else { | ||
550 | int status; | ||
551 | if (waitpid(child, &status, 0) != child || | ||
552 | !WIFEXITED(status)) { | ||
553 | printf("[FAIL]\tChild died\n"); | ||
554 | nerrs++; | ||
555 | } else if (WEXITSTATUS(status) != 0) { | ||
556 | printf("[FAIL]\tChild failed\n"); | ||
557 | nerrs++; | ||
558 | } else { | ||
559 | printf("[OK]\tChild succeeded\n"); | ||
560 | } | ||
561 | } | ||
562 | } | ||
563 | |||
564 | int main(int argc, char **argv) | ||
565 | { | ||
566 | if (argc == 1 && !strcmp(argv[0], "ldt_gdt_test_exec")) | ||
567 | return finish_exec_test(); | ||
568 | |||
569 | do_simple_tests(); | ||
570 | |||
571 | do_multicpu_tests(); | ||
572 | |||
573 | do_exec_test(); | ||
574 | |||
575 | return nerrs ? 1 : 0; | ||
576 | } | ||
diff --git a/tools/testing/selftests/x86/syscall_arg_fault.c b/tools/testing/selftests/x86/syscall_arg_fault.c new file mode 100644 index 000000000000..7db4fc9fa09f --- /dev/null +++ b/tools/testing/selftests/x86/syscall_arg_fault.c | |||
@@ -0,0 +1,130 @@ | |||
1 | /* | ||
2 | * syscall_arg_fault.c - tests faults 32-bit fast syscall stack args | ||
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 | |||
15 | #define _GNU_SOURCE | ||
16 | |||
17 | #include <stdlib.h> | ||
18 | #include <stdio.h> | ||
19 | #include <string.h> | ||
20 | #include <sys/signal.h> | ||
21 | #include <sys/ucontext.h> | ||
22 | #include <err.h> | ||
23 | #include <setjmp.h> | ||
24 | #include <errno.h> | ||
25 | |||
26 | /* Our sigaltstack scratch space. */ | ||
27 | static unsigned char altstack_data[SIGSTKSZ]; | ||
28 | |||
29 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), | ||
30 | int flags) | ||
31 | { | ||
32 | struct sigaction sa; | ||
33 | memset(&sa, 0, sizeof(sa)); | ||
34 | sa.sa_sigaction = handler; | ||
35 | sa.sa_flags = SA_SIGINFO | flags; | ||
36 | sigemptyset(&sa.sa_mask); | ||
37 | if (sigaction(sig, &sa, 0)) | ||
38 | err(1, "sigaction"); | ||
39 | } | ||
40 | |||
41 | static volatile sig_atomic_t sig_traps; | ||
42 | static sigjmp_buf jmpbuf; | ||
43 | |||
44 | static volatile sig_atomic_t n_errs; | ||
45 | |||
46 | static void sigsegv(int sig, siginfo_t *info, void *ctx_void) | ||
47 | { | ||
48 | ucontext_t *ctx = (ucontext_t*)ctx_void; | ||
49 | |||
50 | if (ctx->uc_mcontext.gregs[REG_EAX] != -EFAULT) { | ||
51 | printf("[FAIL]\tAX had the wrong value: 0x%x\n", | ||
52 | ctx->uc_mcontext.gregs[REG_EAX]); | ||
53 | n_errs++; | ||
54 | } else { | ||
55 | printf("[OK]\tSeems okay\n"); | ||
56 | } | ||
57 | |||
58 | siglongjmp(jmpbuf, 1); | ||
59 | } | ||
60 | |||
61 | static void sigill(int sig, siginfo_t *info, void *ctx_void) | ||
62 | { | ||
63 | printf("[SKIP]\tIllegal instruction\n"); | ||
64 | siglongjmp(jmpbuf, 1); | ||
65 | } | ||
66 | |||
67 | int main() | ||
68 | { | ||
69 | stack_t stack = { | ||
70 | .ss_sp = altstack_data, | ||
71 | .ss_size = SIGSTKSZ, | ||
72 | }; | ||
73 | if (sigaltstack(&stack, NULL) != 0) | ||
74 | err(1, "sigaltstack"); | ||
75 | |||
76 | sethandler(SIGSEGV, sigsegv, SA_ONSTACK); | ||
77 | sethandler(SIGILL, sigill, SA_ONSTACK); | ||
78 | |||
79 | /* | ||
80 | * Exercise another nasty special case. The 32-bit SYSCALL | ||
81 | * and SYSENTER instructions (even in compat mode) each | ||
82 | * clobber one register. A Linux system call has a syscall | ||
83 | * number and six arguments, and the user stack pointer | ||
84 | * needs to live in some register on return. That means | ||
85 | * that we need eight registers, but SYSCALL and SYSENTER | ||
86 | * only preserve seven registers. As a result, one argument | ||
87 | * ends up on the stack. The stack is user memory, which | ||
88 | * means that the kernel can fail to read it. | ||
89 | * | ||
90 | * The 32-bit fast system calls don't have a defined ABI: | ||
91 | * we're supposed to invoke them through the vDSO. So we'll | ||
92 | * fudge it: we set all regs to invalid pointer values and | ||
93 | * invoke the entry instruction. The return will fail no | ||
94 | * matter what, and we completely lose our program state, | ||
95 | * but we can fix it up with a signal handler. | ||
96 | */ | ||
97 | |||
98 | printf("[RUN]\tSYSENTER with invalid state\n"); | ||
99 | if (sigsetjmp(jmpbuf, 1) == 0) { | ||
100 | asm volatile ( | ||
101 | "movl $-1, %%eax\n\t" | ||
102 | "movl $-1, %%ebx\n\t" | ||
103 | "movl $-1, %%ecx\n\t" | ||
104 | "movl $-1, %%edx\n\t" | ||
105 | "movl $-1, %%esi\n\t" | ||
106 | "movl $-1, %%edi\n\t" | ||
107 | "movl $-1, %%ebp\n\t" | ||
108 | "movl $-1, %%esp\n\t" | ||
109 | "sysenter" | ||
110 | : : : "memory", "flags"); | ||
111 | } | ||
112 | |||
113 | printf("[RUN]\tSYSCALL with invalid state\n"); | ||
114 | if (sigsetjmp(jmpbuf, 1) == 0) { | ||
115 | asm volatile ( | ||
116 | "movl $-1, %%eax\n\t" | ||
117 | "movl $-1, %%ebx\n\t" | ||
118 | "movl $-1, %%ecx\n\t" | ||
119 | "movl $-1, %%edx\n\t" | ||
120 | "movl $-1, %%esi\n\t" | ||
121 | "movl $-1, %%edi\n\t" | ||
122 | "movl $-1, %%ebp\n\t" | ||
123 | "movl $-1, %%esp\n\t" | ||
124 | "syscall\n\t" | ||
125 | "pushl $0" /* make sure we segfault cleanly */ | ||
126 | : : : "memory", "flags"); | ||
127 | } | ||
128 | |||
129 | return 0; | ||
130 | } | ||