@ -21,6 +21,9 @@
# include <pthread.h>
# include <sched.h>
# include <linux/futex.h>
# include <sys/mman.h>
# include <asm/prctl.h>
# include <sys/prctl.h>
# define AR_ACCESSED (1<<8)
@ -44,6 +47,12 @@
static int nerrs ;
/* Points to an array of 1024 ints, each holding its own index. */
static const unsigned int * counter_page ;
static struct user_desc * low_user_desc ;
static struct user_desc * low_user_desc_clear ; /* Use to delete GDT entry */
static int gdt_entry_num ;
static void check_invalid_segment ( uint16_t index , int ldt )
{
uint32_t has_limit = 0 , has_ar = 0 , limit , ar ;
@ -561,16 +570,257 @@ static void do_exec_test(void)
}
}
static void setup_counter_page ( void )
{
unsigned int * page = mmap ( NULL , 4096 , PROT_READ | PROT_WRITE ,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT , - 1 , 0 ) ;
if ( page = = MAP_FAILED )
err ( 1 , " mmap " ) ;
for ( int i = 0 ; i < 1024 ; i + + )
page [ i ] = i ;
counter_page = page ;
}
static int invoke_set_thread_area ( void )
{
int ret ;
asm volatile ( " int $0x80 "
: " =a " ( ret ) , " +m " ( low_user_desc ) :
" a " ( 243 ) , " b " ( low_user_desc )
: " flags " ) ;
return ret ;
}
static void setup_low_user_desc ( void )
{
low_user_desc = mmap ( NULL , 2 * sizeof ( struct user_desc ) ,
PROT_READ | PROT_WRITE ,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT , - 1 , 0 ) ;
if ( low_user_desc = = MAP_FAILED )
err ( 1 , " mmap " ) ;
low_user_desc - > entry_number = - 1 ;
low_user_desc - > base_addr = ( unsigned long ) & counter_page [ 1 ] ;
low_user_desc - > limit = 0xfffff ;
low_user_desc - > seg_32bit = 1 ;
low_user_desc - > contents = 0 ; /* Data, grow-up*/
low_user_desc - > read_exec_only = 0 ;
low_user_desc - > limit_in_pages = 1 ;
low_user_desc - > seg_not_present = 0 ;
low_user_desc - > useable = 0 ;
if ( invoke_set_thread_area ( ) = = 0 ) {
gdt_entry_num = low_user_desc - > entry_number ;
printf ( " [NOTE] \t set_thread_area is available; will use GDT index %d \n " , gdt_entry_num ) ;
} else {
printf ( " [NOTE] \t set_thread_area is unavailable \n " ) ;
}
low_user_desc_clear = low_user_desc + 1 ;
low_user_desc_clear - > entry_number = gdt_entry_num ;
low_user_desc_clear - > read_exec_only = 1 ;
low_user_desc_clear - > seg_not_present = 1 ;
}
static void test_gdt_invalidation ( void )
{
if ( ! gdt_entry_num )
return ; /* 64-bit only system -- we can't use set_thread_area */
unsigned short prev_sel ;
unsigned short sel ;
unsigned int eax ;
const char * result ;
# ifdef __x86_64__
unsigned long saved_base ;
unsigned long new_base ;
# endif
/* Test DS */
invoke_set_thread_area ( ) ;
eax = 243 ;
sel = ( gdt_entry_num < < 3 ) | 3 ;
asm volatile ( " movw %%ds, %[prev_sel] \n \t "
" movw %[sel], %%ds \n \t "
# ifdef __i386__
" pushl %%ebx \n \t "
# endif
" movl %[arg1], %%ebx \n \t "
" int $0x80 \n \t " /* Should invalidate ds */
# ifdef __i386__
" popl %%ebx \n \t "
# endif
" movw %%ds, %[sel] \n \t "
" movw %[prev_sel], %%ds "
: [ prev_sel ] " =&r " ( prev_sel ) , [ sel ] " +r " ( sel ) ,
" +a " ( eax )
: " m " ( low_user_desc_clear ) ,
[ arg1 ] " r " ( ( unsigned int ) ( unsigned long ) low_user_desc_clear )
: " flags " ) ;
if ( sel ! = 0 ) {
result = " FAIL " ;
nerrs + + ;
} else {
result = " OK " ;
}
printf ( " [%s] \t Invalidate DS with set_thread_area: new DS = 0x%hx \n " ,
result , sel ) ;
/* Test ES */
invoke_set_thread_area ( ) ;
eax = 243 ;
sel = ( gdt_entry_num < < 3 ) | 3 ;
asm volatile ( " movw %%es, %[prev_sel] \n \t "
" movw %[sel], %%es \n \t "
# ifdef __i386__
" pushl %%ebx \n \t "
# endif
" movl %[arg1], %%ebx \n \t "
" int $0x80 \n \t " /* Should invalidate es */
# ifdef __i386__
" popl %%ebx \n \t "
# endif
" movw %%es, %[sel] \n \t "
" movw %[prev_sel], %%es "
: [ prev_sel ] " =&r " ( prev_sel ) , [ sel ] " +r " ( sel ) ,
" +a " ( eax )
: " m " ( low_user_desc_clear ) ,
[ arg1 ] " r " ( ( unsigned int ) ( unsigned long ) low_user_desc_clear )
: " flags " ) ;
if ( sel ! = 0 ) {
result = " FAIL " ;
nerrs + + ;
} else {
result = " OK " ;
}
printf ( " [%s] \t Invalidate ES with set_thread_area: new ES = 0x%hx \n " ,
result , sel ) ;
/* Test FS */
invoke_set_thread_area ( ) ;
eax = 243 ;
sel = ( gdt_entry_num < < 3 ) | 3 ;
# ifdef __x86_64__
syscall ( SYS_arch_prctl , ARCH_GET_FS , & saved_base ) ;
# endif
asm volatile ( " movw %%fs, %[prev_sel] \n \t "
" movw %[sel], %%fs \n \t "
# ifdef __i386__
" pushl %%ebx \n \t "
# endif
" movl %[arg1], %%ebx \n \t "
" int $0x80 \n \t " /* Should invalidate fs */
# ifdef __i386__
" popl %%ebx \n \t "
# endif
" movw %%fs, %[sel] \n \t "
: [ prev_sel ] " =&r " ( prev_sel ) , [ sel ] " +r " ( sel ) ,
" +a " ( eax )
: " m " ( low_user_desc_clear ) ,
[ arg1 ] " r " ( ( unsigned int ) ( unsigned long ) low_user_desc_clear )
: " flags " ) ;
# ifdef __x86_64__
syscall ( SYS_arch_prctl , ARCH_GET_FS , & new_base ) ;
# endif
/* Restore FS/BASE for glibc */
asm volatile ( " movw %[prev_sel], %%fs " : : [ prev_sel ] " rm " ( prev_sel ) ) ;
# ifdef __x86_64__
if ( saved_base )
syscall ( SYS_arch_prctl , ARCH_SET_FS , saved_base ) ;
# endif
if ( sel ! = 0 ) {
result = " FAIL " ;
nerrs + + ;
} else {
result = " OK " ;
}
printf ( " [%s] \t Invalidate FS with set_thread_area: new FS = 0x%hx \n " ,
result , sel ) ;
# ifdef __x86_64__
if ( sel = = 0 & & new_base ! = 0 ) {
nerrs + + ;
printf ( " [FAIL] \t New FSBASE was 0x%lx \n " , new_base ) ;
} else {
printf ( " [OK] \t New FSBASE was zero \n " ) ;
}
# endif
/* Test GS */
invoke_set_thread_area ( ) ;
eax = 243 ;
sel = ( gdt_entry_num < < 3 ) | 3 ;
# ifdef __x86_64__
syscall ( SYS_arch_prctl , ARCH_GET_GS , & saved_base ) ;
# endif
asm volatile ( " movw %%gs, %[prev_sel] \n \t "
" movw %[sel], %%gs \n \t "
# ifdef __i386__
" pushl %%ebx \n \t "
# endif
" movl %[arg1], %%ebx \n \t "
" int $0x80 \n \t " /* Should invalidate gs */
# ifdef __i386__
" popl %%ebx \n \t "
# endif
" movw %%gs, %[sel] \n \t "
: [ prev_sel ] " =&r " ( prev_sel ) , [ sel ] " +r " ( sel ) ,
" +a " ( eax )
: " m " ( low_user_desc_clear ) ,
[ arg1 ] " r " ( ( unsigned int ) ( unsigned long ) low_user_desc_clear )
: " flags " ) ;
# ifdef __x86_64__
syscall ( SYS_arch_prctl , ARCH_GET_GS , & new_base ) ;
# endif
/* Restore GS/BASE for glibc */
asm volatile ( " movw %[prev_sel], %%gs " : : [ prev_sel ] " rm " ( prev_sel ) ) ;
# ifdef __x86_64__
if ( saved_base )
syscall ( SYS_arch_prctl , ARCH_SET_GS , saved_base ) ;
# endif
if ( sel ! = 0 ) {
result = " FAIL " ;
nerrs + + ;
} else {
result = " OK " ;
}
printf ( " [%s] \t Invalidate GS with set_thread_area: new GS = 0x%hx \n " ,
result , sel ) ;
# ifdef __x86_64__
if ( sel = = 0 & & new_base ! = 0 ) {
nerrs + + ;
printf ( " [FAIL] \t New GSBASE was 0x%lx \n " , new_base ) ;
} else {
printf ( " [OK] \t New GSBASE was zero \n " ) ;
}
# endif
}
int main ( int argc , char * * argv )
{
if ( argc = = 1 & & ! strcmp ( argv [ 0 ] , " ldt_gdt_test_exec " ) )
return finish_exec_test ( ) ;
setup_counter_page ( ) ;
setup_low_user_desc ( ) ;
do_simple_tests ( ) ;
do_multicpu_tests ( ) ;
do_exec_test ( ) ;
test_gdt_invalidation ( ) ;
return nerrs ? 1 : 0 ;
}