aboutsummaryrefslogblamecommitdiffstats
path: root/arch/x86/ia32/ia32_signal.c
blob: 6ea19c25f90d46ce209c646382443c5a8ae0e46e (plain) (tree)
1
2
3
4
5
6
7
8







                                                                       




                        








                              
                          



























































































                                                                                         
                                                               
 

                                                  
                                                  



                                                    



                                             


























































































































































































































                                                                                                    























                                                                
                                                          










































                                                                              




                                                                  

 

                                                                 








                                                            
                                            
















                                                                       




                                                           



























                                                                             




                                   






                                                           


                                             

             
                                                           


                                                                                
                 


                                    
                       

 

                                                                         






































































                                                                              




                                                 




                                                 






                                                           
                                 

                                             

             
                                                           


                                                                                
                 


                                    
                       
 
/*
 *  linux/arch/x86_64/ia32/ia32_signal.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 *  1997-11-28  Modified for POSIX.1b 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
 */

#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.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 <linux/binfmts.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)
{
	mask &= _BLOCKABLE;
	spin_lock_irq(&current->sighand->siglock);
	current->saved_sigmask = current->blocked;
	siginitset(&current->blocked, mask);
	recalc_sigpending();
	spin_unlock_irq(&current->sighand->siglock);

	current->state = TASK_INTERRUPTIBLE;
	schedule();
	set_thread_flag(TIF_RESTORE_SIGMASK);
	return -ERESTARTNOHAND;
}

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;

	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);
	err |= __put_user((u32)regs->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;
	}

	rsp -= frame_size;
	/* Align the stack pointer according to the i386 ABI,
	 * i.e. so that on function entry ((sp + 4) & 15) == 0. */
	rsp = ((rsp + 4) & -16ul) - 4;
	return (void __user *) rsp;
}

int ia32_setup_frame(int sig, struct k_sigaction *ka,
		     compat_sigset_t *set, struct pt_regs * regs)
{
	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;

	err |= __put_user(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;
		if (current->binfmt->hasvdso)
			restorer = VSYSCALL32_SIGRETURN;
		else
			restorer = (void *)&frame->retcode;
		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;

	/* Make -mregparm=3 work */
	regs->rax = sig;
	regs->rdx = 0;
	regs->rcx = 0;

	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);
	regs->eflags &= ~TF_MASK;
	if (test_thread_flag(TIF_SINGLESTEP))
		ptrace_notify(SIGTRAP);

#if DEBUG_SIG
	printk("SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
		current->comm, current->pid, frame, regs->rip, frame->pretcode);
#endif

	return 0;

give_sigsegv:
	force_sigsegv(sig, current);
	return -EFAULT;
}

int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
			compat_sigset_t *set, struct pt_regs * regs)
{
	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;

	/* Make -mregparm=3 work */
	regs->rax = sig;
	regs->rdx = (unsigned long) &frame->info;
	regs->rcx = (unsigned long) &frame->uc;

	/* Make -mregparm=3 work */
	regs->rax = sig;
	regs->rdx = (unsigned long) &frame->info;
	regs->rcx = (unsigned long) &frame->uc;

	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);
	regs->eflags &= ~TF_MASK;
	if (test_thread_flag(TIF_SINGLESTEP))
		ptrace_notify(SIGTRAP);

#if DEBUG_SIG
	printk("SIG deliver (%s:%d): sp=%p pc=%lx ra=%u\n",
		current->comm, current->pid, frame, regs->rip, frame->pretcode);
#endif

	return 0;

give_sigsegv:
	force_sigsegv(sig, current);
	return -EFAULT;
}