diff options
author | Ingo Molnar <mingo@kernel.org> | 2019-07-04 04:36:20 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2019-07-04 04:36:20 -0400 |
commit | f584dd32edc5d4400d7ceb92111a89f0c1f6651f (patch) | |
tree | e31ee9615fc9f07e8791fca0e77cd35f2dd1041a /tools | |
parent | a328a259ced0c0fa5aabcd29238779a536335884 (diff) | |
parent | 049331f277fef1c3f2527c2c9afa1d285e9a1247 (diff) |
Merge branch 'x86/cpu' into perf/core, to pick up revert
perf/core has an earlier version of the x86/cpu tree merged, to avoid
conflicts, and due to this we want to pick up this ABI impacting
revert as well:
049331f277fe: ("x86/fsgsbase: Revert FSGSBASE support")
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/testing/selftests/x86/Makefile | 5 | ||||
-rw-r--r-- | tools/testing/selftests/x86/fsgsbase.c | 74 | ||||
-rw-r--r-- | tools/testing/selftests/x86/syscall_arg_fault.c | 112 |
3 files changed, 150 insertions, 41 deletions
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index 186520198de7..fa07d526fe39 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile | |||
@@ -12,8 +12,9 @@ CAN_BUILD_WITH_NOPIE := $(shell ./check_cc.sh $(CC) trivial_program.c -no-pie) | |||
12 | 12 | ||
13 | TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \ | 13 | TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \ |
14 | check_initial_reg_state sigreturn iopl mpx-mini-test ioperm \ | 14 | check_initial_reg_state sigreturn iopl mpx-mini-test ioperm \ |
15 | protection_keys test_vdso test_vsyscall mov_ss_trap | 15 | protection_keys test_vdso test_vsyscall mov_ss_trap \ |
16 | TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ | 16 | syscall_arg_fault |
17 | TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \ | ||
17 | test_FCMOV test_FCOMI test_FISTTP \ | 18 | test_FCMOV test_FCOMI test_FISTTP \ |
18 | vdso_restorer | 19 | vdso_restorer |
19 | TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip | 20 | TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip |
diff --git a/tools/testing/selftests/x86/fsgsbase.c b/tools/testing/selftests/x86/fsgsbase.c index 21fd4f94b5b0..5ab4c60c100e 100644 --- a/tools/testing/selftests/x86/fsgsbase.c +++ b/tools/testing/selftests/x86/fsgsbase.c | |||
@@ -35,6 +35,8 @@ | |||
35 | static volatile sig_atomic_t want_segv; | 35 | static volatile sig_atomic_t want_segv; |
36 | static volatile unsigned long segv_addr; | 36 | static volatile unsigned long segv_addr; |
37 | 37 | ||
38 | static unsigned short *shared_scratch; | ||
39 | |||
38 | static int nerrs; | 40 | static int nerrs; |
39 | 41 | ||
40 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), | 42 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), |
@@ -242,16 +244,11 @@ static void do_remote_base() | |||
242 | 244 | ||
243 | static __thread int set_thread_area_entry_number = -1; | 245 | static __thread int set_thread_area_entry_number = -1; |
244 | 246 | ||
245 | static void do_unexpected_base(void) | 247 | static unsigned short load_gs(void) |
246 | { | 248 | { |
247 | /* | 249 | /* |
248 | * The goal here is to try to arrange for GS == 0, GSBASE != | 250 | * Sets GS != 0 and GSBASE != 0 but arranges for the kernel to think |
249 | * 0, and for the the kernel the think that GSBASE == 0. | 251 | * that GSBASE == 0 (i.e. thread.gsbase == 0). |
250 | * | ||
251 | * To make the test as reliable as possible, this uses | ||
252 | * explicit descriptors. (This is not the only way. This | ||
253 | * could use ARCH_SET_GS with a low, nonzero base, but the | ||
254 | * relevant side effect of ARCH_SET_GS could change.) | ||
255 | */ | 252 | */ |
256 | 253 | ||
257 | /* Step 1: tell the kernel that we have GSBASE == 0. */ | 254 | /* Step 1: tell the kernel that we have GSBASE == 0. */ |
@@ -271,8 +268,9 @@ static void do_unexpected_base(void) | |||
271 | .useable = 0 | 268 | .useable = 0 |
272 | }; | 269 | }; |
273 | if (syscall(SYS_modify_ldt, 1, &desc, sizeof(desc)) == 0) { | 270 | if (syscall(SYS_modify_ldt, 1, &desc, sizeof(desc)) == 0) { |
274 | printf("\tother thread: using LDT slot 0\n"); | 271 | printf("\tusing LDT slot 0\n"); |
275 | asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0x7)); | 272 | asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0x7)); |
273 | return 0x7; | ||
276 | } else { | 274 | } else { |
277 | /* No modify_ldt for us (configured out, perhaps) */ | 275 | /* No modify_ldt for us (configured out, perhaps) */ |
278 | 276 | ||
@@ -294,20 +292,15 @@ static void do_unexpected_base(void) | |||
294 | 292 | ||
295 | if (ret != 0) { | 293 | if (ret != 0) { |
296 | printf("[NOTE]\tcould not create a segment -- test won't do anything\n"); | 294 | printf("[NOTE]\tcould not create a segment -- test won't do anything\n"); |
297 | return; | 295 | return 0; |
298 | } | 296 | } |
299 | printf("\tother thread: using GDT slot %d\n", desc.entry_number); | 297 | printf("\tusing GDT slot %d\n", desc.entry_number); |
300 | set_thread_area_entry_number = desc.entry_number; | 298 | set_thread_area_entry_number = desc.entry_number; |
301 | 299 | ||
302 | asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)((desc.entry_number << 3) | 0x3))); | 300 | unsigned short gs = (unsigned short)((desc.entry_number << 3) | 0x3); |
301 | asm volatile ("mov %0, %%gs" : : "rm" (gs)); | ||
302 | return gs; | ||
303 | } | 303 | } |
304 | |||
305 | /* | ||
306 | * Step 3: set the selector back to zero. On AMD chips, this will | ||
307 | * preserve GSBASE. | ||
308 | */ | ||
309 | |||
310 | asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0)); | ||
311 | } | 304 | } |
312 | 305 | ||
313 | void test_wrbase(unsigned short index, unsigned long base) | 306 | void test_wrbase(unsigned short index, unsigned long base) |
@@ -346,12 +339,19 @@ static void *threadproc(void *ctx) | |||
346 | if (ftx == 3) | 339 | if (ftx == 3) |
347 | return NULL; | 340 | return NULL; |
348 | 341 | ||
349 | if (ftx == 1) | 342 | if (ftx == 1) { |
350 | do_remote_base(); | 343 | do_remote_base(); |
351 | else if (ftx == 2) | 344 | } else if (ftx == 2) { |
352 | do_unexpected_base(); | 345 | /* |
353 | else | 346 | * On AMD chips, this causes GSBASE != 0, GS == 0, and |
347 | * thread.gsbase == 0. | ||
348 | */ | ||
349 | |||
350 | load_gs(); | ||
351 | asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0)); | ||
352 | } else { | ||
354 | errx(1, "helper thread got bad command"); | 353 | errx(1, "helper thread got bad command"); |
354 | } | ||
355 | 355 | ||
356 | ftx = 0; | 356 | ftx = 0; |
357 | syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); | 357 | syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); |
@@ -453,12 +453,7 @@ static void test_ptrace_write_gsbase(void) | |||
453 | if (child == 0) { | 453 | if (child == 0) { |
454 | printf("[RUN]\tPTRACE_POKE(), write GSBASE from ptracer\n"); | 454 | printf("[RUN]\tPTRACE_POKE(), write GSBASE from ptracer\n"); |
455 | 455 | ||
456 | /* | 456 | *shared_scratch = load_gs(); |
457 | * Use the LDT setup and fetch the GSBASE from the LDT | ||
458 | * by switching to the (nonzero) selector (again) | ||
459 | */ | ||
460 | do_unexpected_base(); | ||
461 | asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0x7)); | ||
462 | 457 | ||
463 | if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) | 458 | if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) |
464 | err(1, "PTRACE_TRACEME"); | 459 | err(1, "PTRACE_TRACEME"); |
@@ -476,7 +471,7 @@ static void test_ptrace_write_gsbase(void) | |||
476 | 471 | ||
477 | gs = ptrace(PTRACE_PEEKUSER, child, gs_offset, NULL); | 472 | gs = ptrace(PTRACE_PEEKUSER, child, gs_offset, NULL); |
478 | 473 | ||
479 | if (gs != 0x7) { | 474 | if (gs != *shared_scratch) { |
480 | nerrs++; | 475 | nerrs++; |
481 | printf("[FAIL]\tGS is not prepared with nonzero\n"); | 476 | printf("[FAIL]\tGS is not prepared with nonzero\n"); |
482 | goto END; | 477 | goto END; |
@@ -494,16 +489,24 @@ static void test_ptrace_write_gsbase(void) | |||
494 | * selector value is changed or not by the GSBASE write in | 489 | * selector value is changed or not by the GSBASE write in |
495 | * a ptracer. | 490 | * a ptracer. |
496 | */ | 491 | */ |
497 | if (gs != 0x7) { | 492 | if (gs != *shared_scratch) { |
498 | nerrs++; | 493 | nerrs++; |
499 | printf("[FAIL]\tGS changed to %lx\n", gs); | 494 | printf("[FAIL]\tGS changed to %lx\n", gs); |
495 | |||
496 | /* | ||
497 | * On older kernels, poking a nonzero value into the | ||
498 | * base would zero the selector. On newer kernels, | ||
499 | * this behavior has changed -- poking the base | ||
500 | * changes only the base and, if FSGSBASE is not | ||
501 | * available, this may have no effect. | ||
502 | */ | ||
503 | if (gs == 0) | ||
504 | printf("\tNote: this is expected behavior on older kernels.\n"); | ||
500 | } else if (have_fsgsbase && (base != 0xFF)) { | 505 | } else if (have_fsgsbase && (base != 0xFF)) { |
501 | nerrs++; | 506 | nerrs++; |
502 | printf("[FAIL]\tGSBASE changed to %lx\n", base); | 507 | printf("[FAIL]\tGSBASE changed to %lx\n", base); |
503 | } else { | 508 | } else { |
504 | printf("[OK]\tGS remained 0x7 %s"); | 509 | printf("[OK]\tGS remained 0x%hx%s", *shared_scratch, have_fsgsbase ? " and GSBASE changed to 0xFF" : ""); |
505 | if (have_fsgsbase) | ||
506 | printf("and GSBASE changed to 0xFF"); | ||
507 | printf("\n"); | 510 | printf("\n"); |
508 | } | 511 | } |
509 | } | 512 | } |
@@ -516,6 +519,9 @@ int main() | |||
516 | { | 519 | { |
517 | pthread_t thread; | 520 | pthread_t thread; |
518 | 521 | ||
522 | shared_scratch = mmap(NULL, 4096, PROT_READ | PROT_WRITE, | ||
523 | MAP_ANONYMOUS | MAP_SHARED, -1, 0); | ||
524 | |||
519 | /* Probe FSGSBASE */ | 525 | /* Probe FSGSBASE */ |
520 | sethandler(SIGILL, sigill, 0); | 526 | sethandler(SIGILL, sigill, 0); |
521 | if (sigsetjmp(jmpbuf, 1) == 0) { | 527 | if (sigsetjmp(jmpbuf, 1) == 0) { |
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 | } |