2019-05-29 07:18:02 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2015-07-03 12:44:18 -07:00
/*
* syscall_arg_fault . c - tests faults 32 - bit fast syscall stack args
* Copyright ( c ) 2015 Andrew Lutomirski
*/
# define _GNU_SOURCE
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <sys/signal.h>
# include <sys/ucontext.h>
# include <err.h>
# include <setjmp.h>
# include <errno.h>
2020-06-26 10:21:16 -07:00
# include "helpers.h"
2019-07-01 20:43:19 -07:00
2015-07-03 12:44:18 -07:00
static void sethandler ( int sig , void ( * handler ) ( int , siginfo_t * , void * ) ,
int flags )
{
struct sigaction sa ;
memset ( & sa , 0 , sizeof ( sa ) ) ;
sa . sa_sigaction = handler ;
sa . sa_flags = SA_SIGINFO | flags ;
sigemptyset ( & sa . sa_mask ) ;
if ( sigaction ( sig , & sa , 0 ) )
err ( 1 , " sigaction " ) ;
}
static volatile sig_atomic_t sig_traps ;
static sigjmp_buf jmpbuf ;
static volatile sig_atomic_t n_errs ;
2019-07-01 20:43:19 -07:00
# ifdef __x86_64__
# define REG_AX REG_RAX
# define REG_IP REG_RIP
# else
# define REG_AX REG_EAX
# define REG_IP REG_EIP
# endif
selftests/x86: Support Atom for syscall_arg_fault test
Atom-based CPUs trigger stack fault when invoke 32-bit SYSENTER instruction
with invalid register values. So we also need SIGBUS handling in this case.
Following is assembly when the fault exception happens.
(gdb) disassemble $eip
Dump of assembler code for function __kernel_vsyscall:
0xf7fd8fe0 <+0>: push %ecx
0xf7fd8fe1 <+1>: push %edx
0xf7fd8fe2 <+2>: push %ebp
0xf7fd8fe3 <+3>: mov %esp,%ebp
0xf7fd8fe5 <+5>: sysenter
0xf7fd8fe7 <+7>: int $0x80
=> 0xf7fd8fe9 <+9>: pop %ebp
0xf7fd8fea <+10>: pop %edx
0xf7fd8feb <+11>: pop %ecx
0xf7fd8fec <+12>: ret
End of assembler dump.
According to Intel SDM, this could also be a Stack Segment Fault(#SS, 12),
except a normal Page Fault(#PF, 14). Especially, in section 6.9 of Vol.3A,
both stack and page faults are within the 10th(lowest priority) class, and
as it said, "exceptions within each class are implementation-dependent and
may vary from processor to processor". It's expected for processors like
Intel Atom to trigger stack fault(SIGBUS), while we get page fault(SIGSEGV)
from common Core processors.
Signed-off-by: Tong Bo <bo.tong@intel.com>
Acked-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
2019-04-19 15:10:55 +08:00
static void sigsegv_or_sigbus ( int sig , siginfo_t * info , void * ctx_void )
2015-07-03 12:44:18 -07:00
{
ucontext_t * ctx = ( ucontext_t * ) ctx_void ;
2019-07-01 20:43:19 -07:00
long ax = ( long ) ctx - > uc_mcontext . gregs [ REG_AX ] ;
2015-07-03 12:44:18 -07:00
2019-07-01 20:43:19 -07:00
if ( ax ! = - EFAULT & & ax ! = - ENOSYS ) {
printf ( " [FAIL] \t AX had the wrong value: 0x%lx \n " ,
( unsigned long ) ax ) ;
2020-05-27 16:02:36 -07:00
printf ( " \t IP = 0x%lx \n " , ( unsigned long ) ctx - > uc_mcontext . gregs [ REG_IP ] ) ;
2015-07-03 12:44:18 -07:00
n_errs + + ;
} else {
printf ( " [OK] \t Seems okay \n " ) ;
}
siglongjmp ( jmpbuf , 1 ) ;
}
2019-07-01 20:43:19 -07:00
static volatile sig_atomic_t sigtrap_consecutive_syscalls ;
static void sigtrap ( int sig , siginfo_t * info , void * ctx_void )
{
/*
* KVM has some bugs that can cause us to stop making progress .
* detect them and complain , but don ' t infinite loop or fail the
* test .
*/
ucontext_t * ctx = ( ucontext_t * ) ctx_void ;
unsigned short * ip = ( unsigned short * ) ctx - > uc_mcontext . gregs [ REG_IP ] ;
if ( * ip = = 0x340f | | * ip = = 0x050f ) {
/* The trap was on SYSCALL or SYSENTER */
sigtrap_consecutive_syscalls + + ;
if ( sigtrap_consecutive_syscalls > 3 ) {
printf ( " [WARN] \t Got stuck single-stepping -- you probably have a KVM bug \n " ) ;
siglongjmp ( jmpbuf , 1 ) ;
}
} else {
sigtrap_consecutive_syscalls = 0 ;
}
}
2015-07-03 12:44:18 -07:00
static void sigill ( int sig , siginfo_t * info , void * ctx_void )
{
2019-07-01 20:43:19 -07:00
ucontext_t * ctx = ( ucontext_t * ) ctx_void ;
unsigned short * ip = ( unsigned short * ) ctx - > uc_mcontext . gregs [ REG_IP ] ;
if ( * ip = = 0x0b0f ) {
/* one of the ud2 instructions faulted */
printf ( " [OK] \t SYSCALL returned normally \n " ) ;
} else {
printf ( " [SKIP] \t Illegal instruction \n " ) ;
}
2015-07-03 12:44:18 -07:00
siglongjmp ( jmpbuf , 1 ) ;
}
int main ( )
{
stack_t stack = {
2021-08-24 10:43:58 +08:00
/* Our sigaltstack scratch space. */
. ss_sp = malloc ( sizeof ( char ) * SIGSTKSZ ) ,
2015-07-03 12:44:18 -07:00
. ss_size = SIGSTKSZ ,
} ;
if ( sigaltstack ( & stack , NULL ) ! = 0 )
err ( 1 , " sigaltstack " ) ;
selftests/x86: Support Atom for syscall_arg_fault test
Atom-based CPUs trigger stack fault when invoke 32-bit SYSENTER instruction
with invalid register values. So we also need SIGBUS handling in this case.
Following is assembly when the fault exception happens.
(gdb) disassemble $eip
Dump of assembler code for function __kernel_vsyscall:
0xf7fd8fe0 <+0>: push %ecx
0xf7fd8fe1 <+1>: push %edx
0xf7fd8fe2 <+2>: push %ebp
0xf7fd8fe3 <+3>: mov %esp,%ebp
0xf7fd8fe5 <+5>: sysenter
0xf7fd8fe7 <+7>: int $0x80
=> 0xf7fd8fe9 <+9>: pop %ebp
0xf7fd8fea <+10>: pop %edx
0xf7fd8feb <+11>: pop %ecx
0xf7fd8fec <+12>: ret
End of assembler dump.
According to Intel SDM, this could also be a Stack Segment Fault(#SS, 12),
except a normal Page Fault(#PF, 14). Especially, in section 6.9 of Vol.3A,
both stack and page faults are within the 10th(lowest priority) class, and
as it said, "exceptions within each class are implementation-dependent and
may vary from processor to processor". It's expected for processors like
Intel Atom to trigger stack fault(SIGBUS), while we get page fault(SIGSEGV)
from common Core processors.
Signed-off-by: Tong Bo <bo.tong@intel.com>
Acked-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
2019-04-19 15:10:55 +08:00
sethandler ( SIGSEGV , sigsegv_or_sigbus , SA_ONSTACK ) ;
/*
* The actual exception can vary . On Atom CPUs , we get # SS
* instead of # PF when the vDSO fails to access the stack when
* ESP is too close to 2 ^ 32 , and # SS causes SIGBUS .
*/
sethandler ( SIGBUS , sigsegv_or_sigbus , SA_ONSTACK ) ;
2015-07-03 12:44:18 -07:00
sethandler ( SIGILL , sigill , SA_ONSTACK ) ;
/*
* Exercise another nasty special case . The 32 - bit SYSCALL
* and SYSENTER instructions ( even in compat mode ) each
* clobber one register . A Linux system call has a syscall
* number and six arguments , and the user stack pointer
* needs to live in some register on return . That means
* that we need eight registers , but SYSCALL and SYSENTER
* only preserve seven registers . As a result , one argument
* ends up on the stack . The stack is user memory , which
* means that the kernel can fail to read it .
*
* The 32 - bit fast system calls don ' t have a defined ABI :
* we ' re supposed to invoke them through the vDSO . So we ' ll
* fudge it : we set all regs to invalid pointer values and
* invoke the entry instruction . The return will fail no
* matter what , and we completely lose our program state ,
* but we can fix it up with a signal handler .
*/
printf ( " [RUN] \t SYSENTER with invalid state \n " ) ;
if ( sigsetjmp ( jmpbuf , 1 ) = = 0 ) {
asm volatile (
" movl $-1, %%eax \n \t "
" movl $-1, %%ebx \n \t "
" movl $-1, %%ecx \n \t "
" movl $-1, %%edx \n \t "
" movl $-1, %%esi \n \t "
" movl $-1, %%edi \n \t "
" movl $-1, %%ebp \n \t "
" movl $-1, %%esp \n \t "
" sysenter "
: : : " memory " , " flags " ) ;
}
printf ( " [RUN] \t SYSCALL with invalid state \n " ) ;
if ( sigsetjmp ( jmpbuf , 1 ) = = 0 ) {
asm volatile (
" movl $-1, %%eax \n \t "
" movl $-1, %%ebx \n \t "
" movl $-1, %%ecx \n \t "
" movl $-1, %%edx \n \t "
" movl $-1, %%esi \n \t "
" movl $-1, %%edi \n \t "
" movl $-1, %%ebp \n \t "
" movl $-1, %%esp \n \t "
" syscall \n \t "
2019-07-01 20:43:19 -07:00
" ud2 " /* make sure we recover cleanly */
: : : " memory " , " flags " ) ;
}
printf ( " [RUN] \t SYSENTER with TF and invalid state \n " ) ;
sethandler ( SIGTRAP , sigtrap , SA_ONSTACK ) ;
if ( sigsetjmp ( jmpbuf , 1 ) = = 0 ) {
sigtrap_consecutive_syscalls = 0 ;
set_eflags ( get_eflags ( ) | X86_EFLAGS_TF ) ;
asm volatile (
" movl $-1, %%eax \n \t "
" movl $-1, %%ebx \n \t "
" movl $-1, %%ecx \n \t "
" movl $-1, %%edx \n \t "
" movl $-1, %%esi \n \t "
" movl $-1, %%edi \n \t "
" movl $-1, %%ebp \n \t "
" movl $-1, %%esp \n \t "
" sysenter "
: : : " memory " , " flags " ) ;
}
set_eflags ( get_eflags ( ) & ~ X86_EFLAGS_TF ) ;
printf ( " [RUN] \t SYSCALL with TF and invalid state \n " ) ;
if ( sigsetjmp ( jmpbuf , 1 ) = = 0 ) {
sigtrap_consecutive_syscalls = 0 ;
set_eflags ( get_eflags ( ) | X86_EFLAGS_TF ) ;
asm volatile (
" movl $-1, %%eax \n \t "
" movl $-1, %%ebx \n \t "
" movl $-1, %%ecx \n \t "
" movl $-1, %%edx \n \t "
" movl $-1, %%esi \n \t "
" movl $-1, %%edi \n \t "
" movl $-1, %%ebp \n \t "
" movl $-1, %%esp \n \t "
" syscall \n \t "
" ud2 " /* make sure we recover cleanly */
2015-07-03 12:44:18 -07:00
: : : " memory " , " flags " ) ;
}
2019-07-01 20:43:19 -07:00
set_eflags ( get_eflags ( ) & ~ X86_EFLAGS_TF ) ;
2015-07-03 12:44:18 -07:00
2020-05-27 16:02:36 -07:00
# ifdef __x86_64__
printf ( " [RUN] \t SYSENTER with TF, invalid state, and GSBASE < 0 \n " ) ;
if ( sigsetjmp ( jmpbuf , 1 ) = = 0 ) {
sigtrap_consecutive_syscalls = 0 ;
asm volatile ( " wrgsbase %%rax \n \t "
: : " a " ( 0xffffffffffff0000UL ) ) ;
set_eflags ( get_eflags ( ) | X86_EFLAGS_TF ) ;
asm volatile (
" movl $-1, %%eax \n \t "
" movl $-1, %%ebx \n \t "
" movl $-1, %%ecx \n \t "
" movl $-1, %%edx \n \t "
" movl $-1, %%esi \n \t "
" movl $-1, %%edi \n \t "
" movl $-1, %%ebp \n \t "
" movl $-1, %%esp \n \t "
" sysenter "
: : : " memory " , " flags " ) ;
}
set_eflags ( get_eflags ( ) & ~ X86_EFLAGS_TF ) ;
# endif
2021-08-24 10:43:58 +08:00
free ( stack . ss_sp ) ;
2015-07-03 12:44:18 -07:00
return 0 ;
}