diff options
author | Ingo Molnar <mingo@kernel.org> | 2019-06-24 19:26:39 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2019-06-24 19:26:39 +0200 |
commit | 9e6e87e62acce30bc9f26e0732817022f70088b6 (patch) | |
tree | 291a46d57f0937126ea338ea00cb7d8eb46e8a36 /tools | |
parent | b9271f0c65e12efd9b6319f57a378aac1be98b22 (diff) | |
parent | 203dffacf592317e54480704f569a09f8b7ca380 (diff) | |
download | linux-9e6e87e62acce30bc9f26e0732817022f70088b6.tar.bz2 |
Merge branch 'x86/cpu' into perf/core, to pick up dependent patches
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/testing/selftests/x86/fsgsbase.c | 179 |
1 files changed, 176 insertions, 3 deletions
diff --git a/tools/testing/selftests/x86/fsgsbase.c b/tools/testing/selftests/x86/fsgsbase.c index af85bd4752a5..21fd4f94b5b0 100644 --- a/tools/testing/selftests/x86/fsgsbase.c +++ b/tools/testing/selftests/x86/fsgsbase.c @@ -23,6 +23,10 @@ #include <pthread.h> #include <asm/ldt.h> #include <sys/mman.h> +#include <stddef.h> +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <setjmp.h> #ifndef __x86_64__ # error This test is 64-bit only @@ -71,6 +75,43 @@ static void sigsegv(int sig, siginfo_t *si, void *ctx_void) } +static jmp_buf jmpbuf; + +static void sigill(int sig, siginfo_t *si, void *ctx_void) +{ + siglongjmp(jmpbuf, 1); +} + +static bool have_fsgsbase; + +static inline unsigned long rdgsbase(void) +{ + unsigned long gsbase; + + asm volatile("rdgsbase %0" : "=r" (gsbase) :: "memory"); + + return gsbase; +} + +static inline unsigned long rdfsbase(void) +{ + unsigned long fsbase; + + asm volatile("rdfsbase %0" : "=r" (fsbase) :: "memory"); + + return fsbase; +} + +static inline void wrgsbase(unsigned long gsbase) +{ + asm volatile("wrgsbase %0" :: "r" (gsbase) : "memory"); +} + +static inline void wrfsbase(unsigned long fsbase) +{ + asm volatile("wrfsbase %0" :: "r" (fsbase) : "memory"); +} + enum which_base { FS, GS }; static unsigned long read_base(enum which_base which) @@ -199,14 +240,16 @@ static void do_remote_base() to_set, hard_zero ? " and clear gs" : "", sel); } -void do_unexpected_base(void) +static __thread int set_thread_area_entry_number = -1; + +static void do_unexpected_base(void) { /* * The goal here is to try to arrange for GS == 0, GSBASE != * 0, and for the the kernel the think that GSBASE == 0. * * To make the test as reliable as possible, this uses - * explicit descriptorss. (This is not the only way. This + * explicit descriptors. (This is not the only way. This * could use ARCH_SET_GS with a low, nonzero base, but the * relevant side effect of ARCH_SET_GS could change.) */ @@ -239,7 +282,7 @@ void do_unexpected_base(void) MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0); memcpy(low_desc, &desc, sizeof(desc)); - low_desc->entry_number = -1; + low_desc->entry_number = set_thread_area_entry_number; /* 32-bit set_thread_area */ long ret; @@ -254,6 +297,8 @@ void do_unexpected_base(void) return; } printf("\tother thread: using GDT slot %d\n", desc.entry_number); + set_thread_area_entry_number = desc.entry_number; + asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)((desc.entry_number << 3) | 0x3))); } @@ -265,6 +310,34 @@ void do_unexpected_base(void) asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0)); } +void test_wrbase(unsigned short index, unsigned long base) +{ + unsigned short newindex; + unsigned long newbase; + + printf("[RUN]\tGS = 0x%hx, GSBASE = 0x%lx\n", index, base); + + asm volatile ("mov %0, %%gs" : : "rm" (index)); + wrgsbase(base); + + remote_base = 0; + ftx = 1; + syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); + while (ftx != 0) + syscall(SYS_futex, &ftx, FUTEX_WAIT, 1, NULL, NULL, 0); + + asm volatile ("mov %%gs, %0" : "=rm" (newindex)); + newbase = rdgsbase(); + + if (newindex == index && newbase == base) { + printf("[OK]\tIndex and base were preserved\n"); + } else { + printf("[FAIL]\tAfter switch, GS = 0x%hx and GSBASE = 0x%lx\n", + newindex, newbase); + nerrs++; + } +} + static void *threadproc(void *ctx) { while (1) { @@ -367,10 +440,93 @@ static void test_unexpected_base(void) } } +#define USER_REGS_OFFSET(r) offsetof(struct user_regs_struct, r) + +static void test_ptrace_write_gsbase(void) +{ + int status; + pid_t child = fork(); + + if (child < 0) + err(1, "fork"); + + if (child == 0) { + printf("[RUN]\tPTRACE_POKE(), write GSBASE from ptracer\n"); + + /* + * Use the LDT setup and fetch the GSBASE from the LDT + * by switching to the (nonzero) selector (again) + */ + do_unexpected_base(); + asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0x7)); + + if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) + err(1, "PTRACE_TRACEME"); + + raise(SIGTRAP); + _exit(0); + } + + wait(&status); + + if (WSTOPSIG(status) == SIGTRAP) { + unsigned long gs, base; + unsigned long gs_offset = USER_REGS_OFFSET(gs); + unsigned long base_offset = USER_REGS_OFFSET(gs_base); + + gs = ptrace(PTRACE_PEEKUSER, child, gs_offset, NULL); + + if (gs != 0x7) { + nerrs++; + printf("[FAIL]\tGS is not prepared with nonzero\n"); + goto END; + } + + if (ptrace(PTRACE_POKEUSER, child, base_offset, 0xFF) != 0) + err(1, "PTRACE_POKEUSER"); + + gs = ptrace(PTRACE_PEEKUSER, child, gs_offset, NULL); + base = ptrace(PTRACE_PEEKUSER, child, base_offset, NULL); + + /* + * In a non-FSGSBASE system, the nonzero selector will load + * GSBASE (again). But what is tested here is whether the + * selector value is changed or not by the GSBASE write in + * a ptracer. + */ + if (gs != 0x7) { + nerrs++; + printf("[FAIL]\tGS changed to %lx\n", gs); + } else if (have_fsgsbase && (base != 0xFF)) { + nerrs++; + printf("[FAIL]\tGSBASE changed to %lx\n", base); + } else { + printf("[OK]\tGS remained 0x7 %s"); + if (have_fsgsbase) + printf("and GSBASE changed to 0xFF"); + printf("\n"); + } + } + +END: + ptrace(PTRACE_CONT, child, NULL, NULL); +} + int main() { pthread_t thread; + /* Probe FSGSBASE */ + sethandler(SIGILL, sigill, 0); + if (sigsetjmp(jmpbuf, 1) == 0) { + rdfsbase(); + have_fsgsbase = true; + printf("\tFSGSBASE instructions are enabled\n"); + } else { + printf("\tFSGSBASE instructions are disabled\n"); + } + clearhandler(SIGILL); + sethandler(SIGSEGV, sigsegv, 0); check_gs_value(0); @@ -417,11 +573,28 @@ int main() test_unexpected_base(); + if (have_fsgsbase) { + unsigned short ss; + + asm volatile ("mov %%ss, %0" : "=rm" (ss)); + + test_wrbase(0, 0); + test_wrbase(0, 1); + test_wrbase(0, 0x200000000); + test_wrbase(0, 0xffffffffffffffff); + test_wrbase(ss, 0); + test_wrbase(ss, 1); + test_wrbase(ss, 0x200000000); + test_wrbase(ss, 0xffffffffffffffff); + } + ftx = 3; /* Kill the thread. */ syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); if (pthread_join(thread, NULL) != 0) err(1, "pthread_join"); + test_ptrace_write_gsbase(); + return nerrs == 0 ? 0 : 1; } |