selftests/x86: Skip fsgsbase segment setup when int $0x80 is unavailable
Subsystems (3)
PATCH 1/1 selftests/x86: Skip fsgsbase segment setup when int $0x80 is unavailable
The fsgsbase test's load_gs() helper needs a nonzero-based FS/GS segment, i.e. a selector whose hidden base differs from the kernel's saved thread base, to exercise the selector/base state tracking. 64-bit userspace can only create such a segment via modify_ldt() or, failing that, via the 32-bit set_thread_area syscall over int $0x80.
On kernels built with CONFIG_MODIFY_LDT_SYSCALL=n, modify_ldt() fails and the test falls back to int $0x80. If the kernel is also built with CONFIG_IA32_EMULATION=n, no IDT gate is installed for vector 0x80, so executing int $0x80 raises a #GP fault and the test dies with SIGSEGV instead of reporting results:
traps: fsgsbase_64[5460] general protection fault ip:4012a1 sp:7f38eb124de0 error:402 in fsgsbase_64[12a1,400000+2000]
With both options disabled, pure 64-bit userspace cannot install a nonzero-based FS/GS segment at all, so this scenario is untestable. Probe for a working int $0x80 at startup and skip the affected subtests when neither mechanism is available instead of crashing.
While at it, replace the magic syscall numbers in the int $0x80 inline asm with named __NR_ia32_getpid / __NR_ia32_set_thread_area constants. These deliberately use the ia32 syscall table numbering, which differs from the x86-64 numbering exported by <sys/syscall.h> on 64-bit builds.
--- tools/testing/selftests/x86/fsgsbase.c | 51 +++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-)
tools/testing/selftests/x86/fsgsbase.c +49 -2
@@ -218,6 +218,40 @@ static void do_remote_base()static __threadint set_thread_area_entry_number =-1 ; +/* +* int $0x80 dispatches through the ia32 syscall table, whose numbers +* differ from the x86-64 table exposed by <sys/syscall.h> on an +* x86_64 build. Define the ia32 numbers we need explicitly. +*/ +#define __NR_ia32_getpid 20 +#define __NR_ia32_set_thread_area 243 + +static bool have_int80; + +static void sigsegv_int80 (int sig,siginfo_t *si,void *ctx_void) +{ +siglongjmp (jmpbuf,1 ); +} + +static bool probe_int80 (void ) +{ +/* +* Check whether int $0x80 is available. Kernels built without +* CONFIG_IA32_EMULATION do not install an IDT entry for vector +* 0x80, so executing int $0x80 causes a #GP fault. +*/ +sethandler (SIGSEGV , sigsegv_int80,0 ); +if (sigsetjmp (jmpbuf,1 ) ==0 ) { +long ret; +/* getpid -- harmless if it works */ + asmvolatile ("int $0x80" :"=a" (ret) :"a" (__NR_ia32_getpid)); +clearhandler (SIGSEGV ); +return true; + } +clearhandler (SIGSEGV ); +return false; +} +static unsigned short load_gs (void ) {/*
@@ -245,7 +279,7 @@ static unsigned short load_gs(void)printf ("\tusing LDT slot 0\n" ); asmvolatile ("mov %0, %%gs" : :"rm" ((unsigned short )0x7 ));return 0x7 ; - }else { + }else if (have_int80) {/* No modify_ldt for us (configured out, perhaps) */ struct user_desc *low_desc = mmap(
@@ -260,7 +294,7 @@ static unsigned short load_gs(void)long ret; asmvolatile ("int $0x80" :"=a" (ret),"+m" (*low_desc) - :"a" (243 ),"b" (low_desc) + :"a" (__NR_ia32_set_thread_area),"b" (low_desc) :"r8" ,"r9" ,"r10" ,"r11" );memcpy (&desc, low_desc,sizeof (desc));munmap (low_desc,sizeof (desc));
@@ -275,6 +309,9 @@ static unsigned short load_gs(void)unsigned short gs = (unsigned short )((desc.entry_number <<3 ) |0x3 ); asmvolatile ("mov %0, %%gs" : :"rm" (gs));return gs; + }else { +printf ("[NOTE]\tno way to create a nonzero-based segment\n" ); +return 0 ; } }
@@ -516,6 +553,11 @@ static void test_ptrace_write_gsbase(void) gs =ptrace (PTRACE_PEEKUSER , child, gs_offset,NULL ); +if (*shared_scratch ==0 ) { +printf ("[SKIP]\tCould not create a nonzero GS selector\n" ); + goto END; + } +if (gs != *shared_scratch) { nerrs++;printf ("[FAIL]\tGS is not prepared with nonzero\n" );
@@ -587,6 +629,11 @@ int main() }clearhandler (SIGILL ); +/* Probe int $0x80 (32-bit syscall entry) */ + have_int80 =probe_int80 (); +if (!have_int80) +printf ("\tint $0x80 is unavailable (CONFIG_IA32_EMULATION=n?)\n" ); +sethandler (SIGSEGV , sigsegv,0 );check_gs_value (0 );
Signature · 1 line
2.50.1
PATCH v2 1/1 selftests/x86: Skip fsgsbase segment setup when int $0x80 is unavailable
The fsgsbase{_restore} tests' load_gs() helper needs a nonzero-based FS/GS segment, i.e. a selector whose hidden base differs from the kernel's saved thread base, to exercise the selector/base state tracking. 64-bit userspace can only create such a segment via modify_ldt() or, failing that, via the 32-bit set_thread_area syscall over int $0x80.
On kernels built with CONFIG_MODIFY_LDT_SYSCALL=n, modify_ldt() fails and the test falls back to int $0x80. If the kernel is also built with CONFIG_IA32_EMULATION=n, no IDT gate is installed for vector 0x80, so executing int $0x80 raises a #GP fault and the test dies with SIGSEGV instead of reporting results:
traps: fsgsbase_64[5460] general protection fault ip:4012a1 sp:7f38eb124de0 error:402 in fsgsbase_64[12a1,400000+2000]
With both options disabled, pure 64-bit userspace cannot install a nonzero-based FS/GS segment at all, so this scenario is untestable. Probe for a working int $0x80 at startup and skip the affected subtests when neither mechanism is available instead of crashing.
While at it, replace the magic syscall numbers in the int $0x80 inline asm with named __NR_ia32_getpid / __NR_ia32_set_thread_area constants. These deliberately use the ia32 syscall table numbering, which differs from the x86-64 numbering exported by <sys/syscall.h> on 64-bit builds.
---
v2
- fsgsbase_restore needs that segment treatment as well
--- tools/testing/selftests/x86/fsgsbase.c | 51 ++++++++++++- .../testing/selftests/x86/fsgsbase_restore.c | 72 ++++++++++++++++++- 2 files changed, 119 insertions(+), 4 deletions(-)
tools/testing/selftests/x86/fsgsbase.c +49 -2
@@ -218,6 +218,40 @@ static void do_remote_base()static __threadint set_thread_area_entry_number =-1 ; +/* +* int $0x80 dispatches through the ia32 syscall table, whose numbers +* differ from the x86-64 table exposed by <sys/syscall.h> on an +* x86_64 build. Define the ia32 numbers we need explicitly. +*/ +#define __NR_ia32_getpid 20 +#define __NR_ia32_set_thread_area 243 + +static bool have_int80; + +static void sigsegv_int80 (int sig,siginfo_t *si,void *ctx_void) +{ +siglongjmp (jmpbuf,1 ); +} + +static bool probe_int80 (void ) +{ +/* +* Check whether int $0x80 is available. Kernels built without +* CONFIG_IA32_EMULATION do not install an IDT entry for vector +* 0x80, so executing int $0x80 causes a #GP fault. +*/ +sethandler (SIGSEGV , sigsegv_int80,0 ); +if (sigsetjmp (jmpbuf,1 ) ==0 ) { +long ret; +/* getpid -- harmless if it works */ + asmvolatile ("int $0x80" :"=a" (ret) :"a" (__NR_ia32_getpid)); +clearhandler (SIGSEGV ); +return true; + } +clearhandler (SIGSEGV ); +return false; +} +static unsigned short load_gs (void ) {/*
@@ -245,7 +279,7 @@ static unsigned short load_gs(void)printf ("\tusing LDT slot 0\n" ); asmvolatile ("mov %0, %%gs" : :"rm" ((unsigned short )0x7 ));return 0x7 ; - }else { + }else if (have_int80) {/* No modify_ldt for us (configured out, perhaps) */ struct user_desc *low_desc = mmap(
@@ -260,7 +294,7 @@ static unsigned short load_gs(void)long ret; asmvolatile ("int $0x80" :"=a" (ret),"+m" (*low_desc) - :"a" (243 ),"b" (low_desc) + :"a" (__NR_ia32_set_thread_area),"b" (low_desc) :"r8" ,"r9" ,"r10" ,"r11" );memcpy (&desc, low_desc,sizeof (desc));munmap (low_desc,sizeof (desc));
@@ -275,6 +309,9 @@ static unsigned short load_gs(void)unsigned short gs = (unsigned short )((desc.entry_number <<3 ) |0x3 ); asmvolatile ("mov %0, %%gs" : :"rm" (gs));return gs; + }else { +printf ("[NOTE]\tno way to create a nonzero-based segment\n" ); +return 0 ; } }
@@ -516,6 +553,11 @@ static void test_ptrace_write_gsbase(void) gs =ptrace (PTRACE_PEEKUSER , child, gs_offset,NULL ); +if (*shared_scratch ==0 ) { +printf ("[SKIP]\tCould not create a nonzero GS selector\n" ); + goto END; + } +if (gs != *shared_scratch) { nerrs++;printf ("[FAIL]\tGS is not prepared with nonzero\n" );
@@ -587,6 +629,11 @@ int main() }clearhandler (SIGILL ); +/* Probe int $0x80 (32-bit syscall entry) */ + have_int80 =probe_int80 (); +if (!have_int80) +printf ("\tint $0x80 is unavailable (CONFIG_IA32_EMULATION=n?)\n" ); +sethandler (SIGSEGV , sigsegv,0 );check_gs_value (0 );
tools/testing/selftests/x86/fsgsbase_restore.c +70 -2
99 lines not on this page · raw message
Signature · 1 line
2.50.1