selftests/x86/fsgsbase: Test RD/WRGSBASE
This validates that GS and GSBASE are independently preserved across context switches. [ chang: Use FSGSBASE instructions directly instead of .byte ] Signed-off-by: Andy Lutomirski <luto@kernel.org> Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Andi Kleen <ak@linux.intel.com> Cc: Ravi Shankar <ravi.v.shankar@intel.com> Cc: H. Peter Anvin <hpa@zytor.com> Link: https://lkml.kernel.org/r/1557309753-24073-15-git-send-email-chang.seok.bae@intel.com
This commit is contained in:
parent
5bf0cab60e
commit
9ad75a0922
|
@ -26,6 +26,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <sys/ptrace.h>
|
#include <sys/ptrace.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
|
||||||
#ifndef __x86_64__
|
#ifndef __x86_64__
|
||||||
# error This test is 64-bit only
|
# error This test is 64-bit only
|
||||||
|
@ -74,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 };
|
enum which_base { FS, GS };
|
||||||
|
|
||||||
static unsigned long read_base(enum which_base which)
|
static unsigned long read_base(enum which_base which)
|
||||||
|
@ -202,14 +240,16 @@ static void do_remote_base()
|
||||||
to_set, hard_zero ? " and clear gs" : "", sel);
|
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 !=
|
* The goal here is to try to arrange for GS == 0, GSBASE !=
|
||||||
* 0, and for the the kernel the think that GSBASE == 0.
|
* 0, and for the the kernel the think that GSBASE == 0.
|
||||||
*
|
*
|
||||||
* To make the test as reliable as possible, this uses
|
* 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
|
* could use ARCH_SET_GS with a low, nonzero base, but the
|
||||||
* relevant side effect of ARCH_SET_GS could change.)
|
* relevant side effect of ARCH_SET_GS could change.)
|
||||||
*/
|
*/
|
||||||
|
@ -242,7 +282,7 @@ void do_unexpected_base(void)
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
|
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
|
||||||
memcpy(low_desc, &desc, sizeof(desc));
|
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 */
|
/* 32-bit set_thread_area */
|
||||||
long ret;
|
long ret;
|
||||||
|
@ -257,6 +297,8 @@ void do_unexpected_base(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
printf("\tother thread: using GDT slot %d\n", desc.entry_number);
|
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)));
|
asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)((desc.entry_number << 3) | 0x3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,6 +310,34 @@ void do_unexpected_base(void)
|
||||||
asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0));
|
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)
|
static void *threadproc(void *ctx)
|
||||||
{
|
{
|
||||||
while (1) {
|
while (1) {
|
||||||
|
@ -439,6 +509,17 @@ int main()
|
||||||
{
|
{
|
||||||
pthread_t thread;
|
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);
|
sethandler(SIGSEGV, sigsegv, 0);
|
||||||
|
|
||||||
check_gs_value(0);
|
check_gs_value(0);
|
||||||
|
@ -485,6 +566,21 @@ int main()
|
||||||
|
|
||||||
test_unexpected_base();
|
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. */
|
ftx = 3; /* Kill the thread. */
|
||||||
syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
|
syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue