2005-04-16 15:20:36 -07:00
/*
* linux / arch / x86_64 / ia32 / ia32_signal . c
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*
* 1997 - 11 - 28 Modified for POSIX .1 b signals by Richard Henderson
* 2000 - 06 - 20 Pentium III FXSR , SSE support by Gareth Hughes
* 2000 - 12 - * x86 - 64 compatibility mode signal handling by Andi Kleen
*
* $ Id : ia32_signal . c , v 1.22 2002 / 07 / 29 10 : 34 : 03 ak Exp $
*/
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/smp.h>
# include <linux/smp_lock.h>
# include <linux/kernel.h>
# include <linux/signal.h>
# include <linux/errno.h>
# include <linux/wait.h>
# include <linux/ptrace.h>
# include <linux/unistd.h>
# include <linux/stddef.h>
# include <linux/personality.h>
# include <linux/compat.h>
# include <asm/ucontext.h>
# include <asm/uaccess.h>
# include <asm/i387.h>
# include <asm/ia32.h>
# include <asm/ptrace.h>
# include <asm/ia32_unistd.h>
# include <asm/user32.h>
# include <asm/sigcontext32.h>
# include <asm/fpu32.h>
# include <asm/proto.h>
# include <asm/vsyscall32.h>
# define DEBUG_SIG 0
# define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
asmlinkage int do_signal ( struct pt_regs * regs , sigset_t * oldset ) ;
void signal_fault ( struct pt_regs * regs , void __user * frame , char * where ) ;
int copy_siginfo_to_user32 ( compat_siginfo_t __user * to , siginfo_t * from )
{
int err ;
if ( ! access_ok ( VERIFY_WRITE , to , sizeof ( compat_siginfo_t ) ) )
return - EFAULT ;
/* If you change siginfo_t structure, please make sure that
this code is fixed accordingly .
It should never copy any pad contained in the structure
to avoid security leaks , but must copy the generic
3 ints plus the relevant union member . */
err = __put_user ( from - > si_signo , & to - > si_signo ) ;
err | = __put_user ( from - > si_errno , & to - > si_errno ) ;
err | = __put_user ( ( short ) from - > si_code , & to - > si_code ) ;
if ( from - > si_code < 0 ) {
err | = __put_user ( from - > si_pid , & to - > si_pid ) ;
err | = __put_user ( from - > si_uid , & to - > si_uid ) ;
err | = __put_user ( ptr_to_compat ( from - > si_ptr ) , & to - > si_ptr ) ;
} else {
/* First 32bits of unions are always present:
* si_pid = = = si_band = = = si_tid = = = si_addr ( LS half ) */
err | = __put_user ( from - > _sifields . _pad [ 0 ] , & to - > _sifields . _pad [ 0 ] ) ;
switch ( from - > si_code > > 16 ) {
case __SI_FAULT > > 16 :
break ;
case __SI_CHLD > > 16 :
err | = __put_user ( from - > si_utime , & to - > si_utime ) ;
err | = __put_user ( from - > si_stime , & to - > si_stime ) ;
err | = __put_user ( from - > si_status , & to - > si_status ) ;
/* FALL THROUGH */
default :
case __SI_KILL > > 16 :
err | = __put_user ( from - > si_uid , & to - > si_uid ) ;
break ;
case __SI_POLL > > 16 :
err | = __put_user ( from - > si_fd , & to - > si_fd ) ;
break ;
case __SI_TIMER > > 16 :
err | = __put_user ( from - > si_overrun , & to - > si_overrun ) ;
err | = __put_user ( ptr_to_compat ( from - > si_ptr ) ,
& to - > si_ptr ) ;
break ;
case __SI_RT > > 16 : /* This is not generated by the kernel as of now. */
case __SI_MESGQ > > 16 :
err | = __put_user ( from - > si_uid , & to - > si_uid ) ;
err | = __put_user ( from - > si_int , & to - > si_int ) ;
break ;
}
}
return err ;
}
int copy_siginfo_from_user32 ( siginfo_t * to , compat_siginfo_t __user * from )
{
int err ;
u32 ptr32 ;
if ( ! access_ok ( VERIFY_READ , from , sizeof ( compat_siginfo_t ) ) )
return - EFAULT ;
err = __get_user ( to - > si_signo , & from - > si_signo ) ;
err | = __get_user ( to - > si_errno , & from - > si_errno ) ;
err | = __get_user ( to - > si_code , & from - > si_code ) ;
err | = __get_user ( to - > si_pid , & from - > si_pid ) ;
err | = __get_user ( to - > si_uid , & from - > si_uid ) ;
err | = __get_user ( ptr32 , & from - > si_ptr ) ;
to - > si_ptr = compat_ptr ( ptr32 ) ;
return err ;
}
asmlinkage long
sys32_sigsuspend ( int history0 , int history1 , old_sigset_t mask ,
struct pt_regs * regs )
{
sigset_t saveset ;
mask & = _BLOCKABLE ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
saveset = current - > blocked ;
siginitset ( & current - > blocked , mask ) ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
regs - > rax = - EINTR ;
while ( 1 ) {
current - > state = TASK_INTERRUPTIBLE ;
schedule ( ) ;
if ( do_signal ( regs , & saveset ) )
return - EINTR ;
}
}
asmlinkage long
sys32_sigaltstack ( const stack_ia32_t __user * uss_ptr ,
stack_ia32_t __user * uoss_ptr ,
struct pt_regs * regs )
{
stack_t uss , uoss ;
int ret ;
mm_segment_t seg ;
if ( uss_ptr ) {
u32 ptr ;
memset ( & uss , 0 , sizeof ( stack_t ) ) ;
if ( ! access_ok ( VERIFY_READ , uss_ptr , sizeof ( stack_ia32_t ) ) | |
__get_user ( ptr , & uss_ptr - > ss_sp ) | |
__get_user ( uss . ss_flags , & uss_ptr - > ss_flags ) | |
__get_user ( uss . ss_size , & uss_ptr - > ss_size ) )
return - EFAULT ;
uss . ss_sp = compat_ptr ( ptr ) ;
}
seg = get_fs ( ) ;
set_fs ( KERNEL_DS ) ;
ret = do_sigaltstack ( uss_ptr ? & uss : NULL , & uoss , regs - > rsp ) ;
set_fs ( seg ) ;
if ( ret > = 0 & & uoss_ptr ) {
if ( ! access_ok ( VERIFY_WRITE , uoss_ptr , sizeof ( stack_ia32_t ) ) | |
__put_user ( ptr_to_compat ( uoss . ss_sp ) , & uoss_ptr - > ss_sp ) | |
__put_user ( uoss . ss_flags , & uoss_ptr - > ss_flags ) | |
__put_user ( uoss . ss_size , & uoss_ptr - > ss_size ) )
ret = - EFAULT ;
}
return ret ;
}
/*
* Do a signal return ; undo the signal stack .
*/
struct sigframe
{
u32 pretcode ;
int sig ;
struct sigcontext_ia32 sc ;
struct _fpstate_ia32 fpstate ;
unsigned int extramask [ _COMPAT_NSIG_WORDS - 1 ] ;
char retcode [ 8 ] ;
} ;
struct rt_sigframe
{
u32 pretcode ;
int sig ;
u32 pinfo ;
u32 puc ;
compat_siginfo_t info ;
struct ucontext_ia32 uc ;
struct _fpstate_ia32 fpstate ;
char retcode [ 8 ] ;
} ;
static int
ia32_restore_sigcontext ( struct pt_regs * regs , struct sigcontext_ia32 __user * sc , unsigned int * peax )
{
unsigned int err = 0 ;
/* Always make any pending restarted system calls return -EINTR */
current_thread_info ( ) - > restart_block . fn = do_no_restart_syscall ;
# if DEBUG_SIG
printk ( " SIG restore_sigcontext: sc=%p err(%x) eip(%x) cs(%x) flg(%x) \n " ,
sc , sc - > err , sc - > eip , sc - > cs , sc - > eflags ) ;
# endif
# define COPY(x) { \
unsigned int reg ; \
err | = __get_user ( reg , & sc - > e # # x ) ; \
regs - > r # # x = reg ; \
}
# define RELOAD_SEG(seg,mask) \
{ unsigned int cur ; \
unsigned short pre ; \
err | = __get_user ( pre , & sc - > seg ) ; \
asm volatile ( " movl %% " # seg " ,%0 " : " =r " ( cur ) ) ; \
pre | = mask ; \
if ( pre ! = cur ) loadsegment ( seg , pre ) ; }
/* Reload fs and gs if they have changed in the signal handler.
This does not handle long fs / gs base changes in the handler , but
does not clobber them at least in the normal case . */
{
unsigned gs , oldgs ;
err | = __get_user ( gs , & sc - > gs ) ;
gs | = 3 ;
asm ( " movl %%gs,%0 " : " =r " ( oldgs ) ) ;
if ( gs ! = oldgs )
load_gs_index ( gs ) ;
}
RELOAD_SEG ( fs , 3 ) ;
RELOAD_SEG ( ds , 3 ) ;
RELOAD_SEG ( es , 3 ) ;
COPY ( di ) ; COPY ( si ) ; COPY ( bp ) ; COPY ( sp ) ; COPY ( bx ) ;
COPY ( dx ) ; COPY ( cx ) ; COPY ( ip ) ;
/* Don't touch extended registers */
err | = __get_user ( regs - > cs , & sc - > cs ) ;
regs - > cs | = 3 ;
err | = __get_user ( regs - > ss , & sc - > ss ) ;
regs - > ss | = 3 ;
{
unsigned int tmpflags ;
err | = __get_user ( tmpflags , & sc - > eflags ) ;
regs - > eflags = ( regs - > eflags & ~ 0x40DD5 ) | ( tmpflags & 0x40DD5 ) ;
regs - > orig_rax = - 1 ; /* disable syscall checks */
}
{
u32 tmp ;
struct _fpstate_ia32 __user * buf ;
err | = __get_user ( tmp , & sc - > fpstate ) ;
buf = compat_ptr ( tmp ) ;
if ( buf ) {
if ( ! access_ok ( VERIFY_READ , buf , sizeof ( * buf ) ) )
goto badframe ;
err | = restore_i387_ia32 ( current , buf , 0 ) ;
} else {
struct task_struct * me = current ;
if ( used_math ( ) ) {
clear_fpu ( me ) ;
clear_used_math ( ) ;
}
}
}
{
u32 tmp ;
err | = __get_user ( tmp , & sc - > eax ) ;
* peax = tmp ;
}
return err ;
badframe :
return 1 ;
}
asmlinkage long sys32_sigreturn ( struct pt_regs * regs )
{
struct sigframe __user * frame = ( struct sigframe __user * ) ( regs - > rsp - 8 ) ;
sigset_t set ;
unsigned int eax ;
if ( ! access_ok ( VERIFY_READ , frame , sizeof ( * frame ) ) )
goto badframe ;
if ( __get_user ( set . sig [ 0 ] , & frame - > sc . oldmask )
| | ( _COMPAT_NSIG_WORDS > 1
& & __copy_from_user ( ( ( ( char * ) & set . sig ) + 4 ) , & frame - > extramask ,
sizeof ( frame - > extramask ) ) ) )
goto badframe ;
sigdelsetmask ( & set , ~ _BLOCKABLE ) ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
current - > blocked = set ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
if ( ia32_restore_sigcontext ( regs , & frame - > sc , & eax ) )
goto badframe ;
return eax ;
badframe :
signal_fault ( regs , frame , " 32bit sigreturn " ) ;
return 0 ;
}
asmlinkage long sys32_rt_sigreturn ( struct pt_regs * regs )
{
struct rt_sigframe __user * frame ;
sigset_t set ;
unsigned int eax ;
struct pt_regs tregs ;
frame = ( struct rt_sigframe __user * ) ( regs - > rsp - 4 ) ;
if ( ! access_ok ( VERIFY_READ , frame , sizeof ( * frame ) ) )
goto badframe ;
if ( __copy_from_user ( & set , & frame - > uc . uc_sigmask , sizeof ( set ) ) )
goto badframe ;
sigdelsetmask ( & set , ~ _BLOCKABLE ) ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
current - > blocked = set ;
recalc_sigpending ( ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
if ( ia32_restore_sigcontext ( regs , & frame - > uc . uc_mcontext , & eax ) )
goto badframe ;
tregs = * regs ;
if ( sys32_sigaltstack ( & frame - > uc . uc_stack , NULL , & tregs ) = = - EFAULT )
goto badframe ;
return eax ;
badframe :
signal_fault ( regs , frame , " 32bit rt sigreturn " ) ;
return 0 ;
}
/*
* Set up a signal frame .
*/
static int
ia32_setup_sigcontext ( struct sigcontext_ia32 __user * sc , struct _fpstate_ia32 __user * fpstate ,
struct pt_regs * regs , unsigned int mask )
{
int tmp , err = 0 ;
u32 eflags ;
tmp = 0 ;
__asm__ ( " movl %%gs,%0 " : " =r " ( tmp ) : " 0 " ( tmp ) ) ;
err | = __put_user ( tmp , ( unsigned int __user * ) & sc - > gs ) ;
__asm__ ( " movl %%fs,%0 " : " =r " ( tmp ) : " 0 " ( tmp ) ) ;
err | = __put_user ( tmp , ( unsigned int __user * ) & sc - > fs ) ;
__asm__ ( " movl %%ds,%0 " : " =r " ( tmp ) : " 0 " ( tmp ) ) ;
err | = __put_user ( tmp , ( unsigned int __user * ) & sc - > ds ) ;
__asm__ ( " movl %%es,%0 " : " =r " ( tmp ) : " 0 " ( tmp ) ) ;
err | = __put_user ( tmp , ( unsigned int __user * ) & sc - > es ) ;
err | = __put_user ( ( u32 ) regs - > rdi , & sc - > edi ) ;
err | = __put_user ( ( u32 ) regs - > rsi , & sc - > esi ) ;
err | = __put_user ( ( u32 ) regs - > rbp , & sc - > ebp ) ;
err | = __put_user ( ( u32 ) regs - > rsp , & sc - > esp ) ;
err | = __put_user ( ( u32 ) regs - > rbx , & sc - > ebx ) ;
err | = __put_user ( ( u32 ) regs - > rdx , & sc - > edx ) ;
err | = __put_user ( ( u32 ) regs - > rcx , & sc - > ecx ) ;
err | = __put_user ( ( u32 ) regs - > rax , & sc - > eax ) ;
err | = __put_user ( ( u32 ) regs - > cs , & sc - > cs ) ;
err | = __put_user ( ( u32 ) regs - > ss , & sc - > ss ) ;
err | = __put_user ( current - > thread . trap_no , & sc - > trapno ) ;
err | = __put_user ( current - > thread . error_code , & sc - > err ) ;
err | = __put_user ( ( u32 ) regs - > rip , & sc - > eip ) ;
eflags = regs - > eflags ;
if ( current - > ptrace & PT_PTRACED )
eflags & = ~ TF_MASK ;
err | = __put_user ( ( u32 ) eflags , & sc - > eflags ) ;
err | = __put_user ( ( u32 ) regs - > rsp , & sc - > esp_at_signal ) ;
tmp = save_i387_ia32 ( current , fpstate , regs , 0 ) ;
if ( tmp < 0 )
err = - EFAULT ;
else {
clear_used_math ( ) ;
stts ( ) ;
err | = __put_user ( ptr_to_compat ( tmp ? fpstate : NULL ) ,
& sc - > fpstate ) ;
}
/* non-iBCS2 extensions.. */
err | = __put_user ( mask , & sc - > oldmask ) ;
err | = __put_user ( current - > thread . cr2 , & sc - > cr2 ) ;
return err ;
}
/*
* Determine which stack to use . .
*/
static void __user *
get_sigframe ( struct k_sigaction * ka , struct pt_regs * regs , size_t frame_size )
{
unsigned long rsp ;
/* Default to using normal stack */
rsp = regs - > rsp ;
/* This is the X/Open sanctioned signal stack switching. */
if ( ka - > sa . sa_flags & SA_ONSTACK ) {
if ( sas_ss_flags ( rsp ) = = 0 )
rsp = current - > sas_ss_sp + current - > sas_ss_size ;
}
/* This is the legacy signal stack switching. */
else if ( ( regs - > ss & 0xffff ) ! = __USER_DS & &
! ( ka - > sa . sa_flags & SA_RESTORER ) & &
ka - > sa . sa_restorer ) {
rsp = ( unsigned long ) ka - > sa . sa_restorer ;
}
return ( void __user * ) ( ( rsp - frame_size ) & - 8UL ) ;
}
2005-06-23 00:08:37 -07:00
int ia32_setup_frame ( int sig , struct k_sigaction * ka ,
compat_sigset_t * set , struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
struct sigframe __user * frame ;
int err = 0 ;
frame = get_sigframe ( ka , regs , sizeof ( * frame ) ) ;
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( * frame ) ) )
goto give_sigsegv ;
{
struct exec_domain * ed = current_thread_info ( ) - > exec_domain ;
err | = __put_user ( ( ed
& & ed - > signal_invmap
& & sig < 32
? ed - > signal_invmap [ sig ]
: sig ) ,
& frame - > sig ) ;
}
if ( err )
goto give_sigsegv ;
err | = ia32_setup_sigcontext ( & frame - > sc , & frame - > fpstate , regs ,
set - > sig [ 0 ] ) ;
if ( err )
goto give_sigsegv ;
if ( _COMPAT_NSIG_WORDS > 1 ) {
err | = __copy_to_user ( frame - > extramask , & set - > sig [ 1 ] ,
sizeof ( frame - > extramask ) ) ;
}
if ( err )
goto give_sigsegv ;
/* Return stub is in 32bit vsyscall page */
{
void __user * restorer = VSYSCALL32_SIGRETURN ;
if ( ka - > sa . sa_flags & SA_RESTORER )
restorer = ka - > sa . sa_restorer ;
err | = __put_user ( ptr_to_compat ( restorer ) , & frame - > pretcode ) ;
}
/* These are actually not used anymore, but left because some
gdb versions depend on them as a marker . */
{
/* copy_to_user optimizes that into a single 8 byte store */
static const struct {
u16 poplmovl ;
u32 val ;
u16 int80 ;
u16 pad ;
} __attribute__ ( ( packed ) ) code = {
0xb858 , /* popl %eax ; movl $...,%eax */
__NR_ia32_sigreturn ,
0x80cd , /* int $0x80 */
0 ,
} ;
err | = __copy_to_user ( frame - > retcode , & code , 8 ) ;
}
if ( err )
goto give_sigsegv ;
/* Set up registers for signal handler */
regs - > rsp = ( unsigned long ) frame ;
regs - > rip = ( unsigned long ) ka - > sa . sa_handler ;
asm volatile ( " movl %0,%%ds " : : " r " ( __USER32_DS ) ) ;
asm volatile ( " movl %0,%%es " : : " r " ( __USER32_DS ) ) ;
regs - > cs = __USER32_CS ;
regs - > ss = __USER32_DS ;
set_fs ( USER_DS ) ;
if ( regs - > eflags & TF_MASK ) {
if ( current - > ptrace & PT_PTRACED ) {
ptrace_notify ( SIGTRAP ) ;
} else {
regs - > eflags & = ~ TF_MASK ;
}
}
# if DEBUG_SIG
printk ( " SIG deliver (%s:%d): sp=%p pc=%p ra=%p \n " ,
current - > comm , current - > pid , frame , regs - > rip , frame - > pretcode ) ;
# endif
2005-06-23 00:08:37 -07:00
return 1 ;
2005-04-16 15:20:36 -07:00
give_sigsegv :
force_sigsegv ( sig , current ) ;
2005-06-23 00:08:37 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2005-06-23 00:08:37 -07:00
int ia32_setup_rt_frame ( int sig , struct k_sigaction * ka , siginfo_t * info ,
compat_sigset_t * set , struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
struct rt_sigframe __user * frame ;
int err = 0 ;
frame = get_sigframe ( ka , regs , sizeof ( * frame ) ) ;
if ( ! access_ok ( VERIFY_WRITE , frame , sizeof ( * frame ) ) )
goto give_sigsegv ;
{
struct exec_domain * ed = current_thread_info ( ) - > exec_domain ;
err | = __put_user ( ( ed
& & ed - > signal_invmap
& & sig < 32
? ed - > signal_invmap [ sig ]
: sig ) ,
& frame - > sig ) ;
}
err | = __put_user ( ptr_to_compat ( & frame - > info ) , & frame - > pinfo ) ;
err | = __put_user ( ptr_to_compat ( & frame - > uc ) , & frame - > puc ) ;
err | = copy_siginfo_to_user32 ( & frame - > info , info ) ;
if ( err )
goto give_sigsegv ;
/* Create the ucontext. */
err | = __put_user ( 0 , & frame - > uc . uc_flags ) ;
err | = __put_user ( 0 , & frame - > uc . uc_link ) ;
err | = __put_user ( current - > sas_ss_sp , & frame - > uc . uc_stack . ss_sp ) ;
err | = __put_user ( sas_ss_flags ( regs - > rsp ) ,
& frame - > uc . uc_stack . ss_flags ) ;
err | = __put_user ( current - > sas_ss_size , & frame - > uc . uc_stack . ss_size ) ;
err | = ia32_setup_sigcontext ( & frame - > uc . uc_mcontext , & frame - > fpstate ,
regs , set - > sig [ 0 ] ) ;
err | = __copy_to_user ( & frame - > uc . uc_sigmask , set , sizeof ( * set ) ) ;
if ( err )
goto give_sigsegv ;
{
void __user * restorer = VSYSCALL32_RTSIGRETURN ;
if ( ka - > sa . sa_flags & SA_RESTORER )
restorer = ka - > sa . sa_restorer ;
err | = __put_user ( ptr_to_compat ( restorer ) , & frame - > pretcode ) ;
}
/* This is movl $,%eax ; int $0x80 */
/* Not actually used anymore, but left because some gdb versions
need it . */
{
/* __copy_to_user optimizes that into a single 8 byte store */
static const struct {
u8 movl ;
u32 val ;
u16 int80 ;
u16 pad ;
u8 pad2 ;
} __attribute__ ( ( packed ) ) code = {
0xb8 ,
__NR_ia32_rt_sigreturn ,
0x80cd ,
0 ,
} ;
err | = __copy_to_user ( frame - > retcode , & code , 8 ) ;
}
if ( err )
goto give_sigsegv ;
/* Set up registers for signal handler */
regs - > rsp = ( unsigned long ) frame ;
regs - > rip = ( unsigned long ) ka - > sa . sa_handler ;
asm volatile ( " movl %0,%%ds " : : " r " ( __USER32_DS ) ) ;
asm volatile ( " movl %0,%%es " : : " r " ( __USER32_DS ) ) ;
regs - > cs = __USER32_CS ;
regs - > ss = __USER32_DS ;
set_fs ( USER_DS ) ;
if ( regs - > eflags & TF_MASK ) {
if ( current - > ptrace & PT_PTRACED ) {
ptrace_notify ( SIGTRAP ) ;
} else {
regs - > eflags & = ~ TF_MASK ;
}
}
# if DEBUG_SIG
printk ( " SIG deliver (%s:%d): sp=%p pc=%p ra=%p \n " ,
current - > comm , current - > pid , frame , regs - > rip , frame - > pretcode ) ;
# endif
2005-06-23 00:08:37 -07:00
return 1 ;
2005-04-16 15:20:36 -07:00
give_sigsegv :
force_sigsegv ( sig , current ) ;
2005-06-23 00:08:37 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}