diff options
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/kernel/signal_32.c | 180 |
1 files changed, 101 insertions, 79 deletions
diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c index f4ec6a0..3da3ffa 100644 --- a/arch/x86/kernel/signal_32.c +++ b/arch/x86/kernel/signal_32.c @@ -4,26 +4,29 @@ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes */ +#include <linux/list.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/smp.h> +#include <linux/personality.h> +#include <linux/binfmts.h> +#include <linux/suspend.h> #include <linux/kernel.h> +#include <linux/ptrace.h> #include <linux/signal.h> +#include <linux/stddef.h> +#include <linux/unistd.h> #include <linux/errno.h> +#include <linux/sched.h> #include <linux/wait.h> -#include <linux/unistd.h> -#include <linux/stddef.h> -#include <linux/personality.h> -#include <linux/suspend.h> -#include <linux/ptrace.h> #include <linux/elf.h> -#include <linux/binfmts.h> +#include <linux/smp.h> +#include <linux/mm.h> + #include <asm/processor.h> #include <asm/ucontext.h> #include <asm/uaccess.h> #include <asm/i387.h> #include <asm/vdso.h> + #include "sigframe.h" #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) @@ -55,10 +58,11 @@ sys_sigsuspend(int history0, int history1, old_sigset_t mask) current->state = TASK_INTERRUPTIBLE; schedule(); set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } -asmlinkage int +asmlinkage int sys_sigaction(int sig, const struct old_sigaction __user *act, struct old_sigaction __user *oact) { @@ -67,10 +71,12 @@ sys_sigaction(int sig, const struct old_sigaction __user *act, if (act) { old_sigset_t mask; + if (!access_ok(VERIFY_READ, act, sizeof(*act)) || __get_user(new_ka.sa.sa_handler, &act->sa_handler) || __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); __get_user(mask, &act->sa_mask); siginitset(&new_ka.sa.sa_mask, mask); @@ -83,6 +89,7 @@ sys_sigaction(int sig, const struct old_sigaction __user *act, __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); } @@ -90,10 +97,12 @@ sys_sigaction(int sig, const struct old_sigaction __user *act, return ret; } -asmlinkage int -sys_sigaltstack(unsigned long bx) +asmlinkage int sys_sigaltstack(unsigned long bx) { - /* This is needed to make gcc realize it doesn't own the "struct pt_regs" */ + /* + * This is needed to make gcc realize it doesn't own the + * "struct pt_regs" + */ struct pt_regs *regs = (struct pt_regs *)&bx; const stack_t __user *uss = (const stack_t __user *)bx; stack_t __user *uoss = (stack_t __user *)regs->cx; @@ -129,7 +138,7 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, #define GET_SEG(seg) \ { unsigned short tmp; \ err |= __get_user(tmp, &sc->seg); \ - loadsegment(seg,tmp); } + loadsegment(seg, tmp); } GET_SEG(gs); COPY_SEG(fs); @@ -139,16 +148,19 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, COPY(dx); COPY(cx); COPY(ip); COPY_SEG_STRICT(cs); COPY_SEG_STRICT(ss); - + { unsigned int tmpflags; + err |= __get_user(tmpflags, &sc->flags); - regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS); + regs->flags = (regs->flags & ~FIX_EFLAGS) | + (tmpflags & FIX_EFLAGS); regs->orig_ax = -1; /* disable syscall checks */ } { - struct _fpstate __user * buf; + struct _fpstate __user *buf; + err |= __get_user(buf, &sc->fpstate); if (buf) { if (!access_ok(VERIFY_READ, buf, sizeof(*buf))) @@ -156,6 +168,7 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, err |= restore_i387(buf); } else { struct task_struct *me = current; + if (used_math()) { clear_fpu(me); clear_used_math(); @@ -172,15 +185,17 @@ badframe: asmlinkage unsigned long sys_sigreturn(unsigned long __unused) { - struct pt_regs *regs = (struct pt_regs *) &__unused; - struct sigframe __user *frame = (struct sigframe __user *)(regs->sp - 8); - sigset_t set; + struct sigframe __user *frame; + struct pt_regs *regs; unsigned long ax; + sigset_t set; + + regs = (struct pt_regs *) &__unused; + frame = (struct sigframe __user *)(regs->sp - 8); if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) goto badframe; - if (__get_user(set.sig[0], &frame->sc.oldmask) - || (_NSIG_WORDS > 1 + if (__get_user(set.sig[0], &frame->sc.oldmask) || (_NSIG_WORDS > 1 && __copy_from_user(&set.sig[1], &frame->extramask, sizeof(frame->extramask)))) goto badframe; @@ -190,7 +205,7 @@ asmlinkage unsigned long sys_sigreturn(unsigned long __unused) current->blocked = set; recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - + if (restore_sigcontext(regs, &frame->sc, &ax)) goto badframe; return ax; @@ -207,8 +222,9 @@ badframe: } force_sig(SIGSEGV, current); + return 0; -} +} asmlinkage int sys_rt_sigreturn(unsigned long __unused) { @@ -228,7 +244,7 @@ asmlinkage int sys_rt_sigreturn(unsigned long __unused) current->blocked = set; recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - + if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax)) goto badframe; @@ -240,12 +256,11 @@ asmlinkage int sys_rt_sigreturn(unsigned long __unused) badframe: force_sig(SIGSEGV, current); return 0; -} +} /* * Set up a signal frame. */ - static int setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate, struct pt_regs *regs, unsigned long mask) @@ -276,9 +291,9 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate, tmp = save_i387(fpstate); if (tmp < 0) - err = 1; + err = 1; else - err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate); + err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate); /* non-iBCS2 extensions.. */ err |= __put_user(mask, &sc->oldmask); @@ -291,7 +306,7 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate, * Determine which stack to use.. */ static inline void __user * -get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) +get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) { unsigned long sp; @@ -309,32 +324,37 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) if (ka->sa.sa_flags & SA_ONSTACK) { if (sas_ss_flags(sp) == 0) sp = 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) { - sp = (unsigned long) ka->sa.sa_restorer; + } else { + /* This is the legacy signal stack switching. */ + if ((regs->ss & 0xffff) != __USER_DS && + !(ka->sa.sa_flags & SA_RESTORER) && + ka->sa.sa_restorer) + sp = (unsigned long) ka->sa.sa_restorer; } sp -= frame_size; - /* Align the stack pointer according to the i386 ABI, - * i.e. so that on function entry ((sp + 4) & 15) == 0. */ + /* + * Align the stack pointer according to the i386 ABI, + * i.e. so that on function entry ((sp + 4) & 15) == 0. + */ sp = ((sp + 4) & -16ul) - 4; + return (void __user *) sp; } -/* These symbols are defined with the addresses in the vsyscall page. - See vsyscall-sigreturn.S. */ +/* + * These symbols are defined with the addresses in the vsyscall page. + * See vsyscall-sigreturn.S. + */ extern void __user __kernel_sigreturn; extern void __user __kernel_rt_sigreturn; -static int setup_frame(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs * regs) +static int +setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, + struct pt_regs *regs) { - void __user *restorer; struct sigframe __user *frame; + void __user *restorer; int err = 0; int usig; @@ -373,9 +393,9 @@ static int setup_frame(int sig, struct k_sigaction *ka, /* Set up to return from userspace. */ err |= __put_user(restorer, &frame->pretcode); - + /* - * This is popl %eax ; movl $,%eax ; int $0x80 + * This is popl %eax ; movl $__NR_sigreturn, %eax ; int $0x80 * * WE DO NOT USE IT ANY MORE! It's only left here for historical * reasons and because gdb uses it as a signature to notice @@ -389,9 +409,9 @@ static int setup_frame(int sig, struct k_sigaction *ka, goto give_sigsegv; /* Set up registers for signal handler */ - regs->sp = (unsigned long) frame; - regs->ip = (unsigned long) ka->sa.sa_handler; - regs->ax = (unsigned long) sig; + regs->sp = (unsigned long)frame; + regs->ip = (unsigned long)ka->sa.sa_handler; + regs->ax = (unsigned long)sig; regs->dx = 0; regs->cx = 0; @@ -418,10 +438,10 @@ give_sigsegv: } static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs * regs) + sigset_t *set, struct pt_regs *regs) { - void __user *restorer; struct rt_sigframe __user *frame; + void __user *restorer; int err = 0; int usig; @@ -451,7 +471,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, &frame->uc.uc_stack.ss_flags); err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate, - regs, set->sig[0]); + regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); if (err) goto give_sigsegv; @@ -461,9 +481,9 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, if (ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer; err |= __put_user(restorer, &frame->pretcode); - + /* - * This is movl $,%ax ; int $0x80 + * This is movl $__NR_rt_sigreturn, %ax ; int $0x80 * * WE DO NOT USE IT ANY MORE! It's only left here for historical * reasons and because gdb uses it as a signature to notice @@ -477,11 +497,11 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, goto give_sigsegv; /* Set up registers for signal handler */ - regs->sp = (unsigned long) frame; - regs->ip = (unsigned long) ka->sa.sa_handler; - regs->ax = (unsigned long) usig; - regs->dx = (unsigned long) &frame->info; - regs->cx = (unsigned long) &frame->uc; + regs->sp = (unsigned long)frame; + regs->ip = (unsigned long)ka->sa.sa_handler; + regs->ax = (unsigned long)usig; + regs->dx = (unsigned long)&frame->info; + regs->cx = (unsigned long)&frame->uc; regs->ds = __USER_DS; regs->es = __USER_DS; @@ -506,9 +526,8 @@ give_sigsegv: } /* - * OK, we're invoking a handler - */ - + * OK, we're invoking a handler: + */ static int handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) @@ -551,16 +570,17 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, else ret = setup_frame(sig, ka, oldset, regs); - if (ret == 0) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } + if (ret) + return ret; - return ret; + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + return 0; } /* @@ -592,7 +612,8 @@ static void do_signal(struct pt_regs *regs) signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { - /* Re-enable any watchpoints before delivering the + /* + * Re-enable any watchpoints before delivering the * signal to user space. The processor register will * have been cleared if the watchpoint triggered * inside the kernel. @@ -600,16 +621,17 @@ static void do_signal(struct pt_regs *regs) if (current->thread.debugreg7) set_debugreg(current->thread.debugreg7, 7); - /* Whee! Actually deliver the signal. */ + /* Whee! Actually deliver the signal. */ if (handle_signal(signr, &info, &ka, oldset, regs) == 0) { - /* a signal was successfully delivered; the saved + /* + * a signal was successfully delivered; the saved * sigmask will have been stored in the signal frame, * and will be restored by sigreturn, so we can simply - * clear the TIF_RESTORE_SIGMASK flag */ + * clear the TIF_RESTORE_SIGMASK flag + */ if (test_thread_flag(TIF_RESTORE_SIGMASK)) clear_thread_flag(TIF_RESTORE_SIGMASK); } - return; } @@ -645,8 +667,8 @@ static void do_signal(struct pt_regs *regs) * notification of userspace execution resumption * - triggered by the TIF_WORK_MASK flags */ -void do_notify_resume(struct pt_regs *regs, void *unused, - __u32 thread_info_flags) +void +do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) { /* Pending single-step? */ if (thread_info_flags & _TIF_SINGLESTEP) { @@ -660,6 +682,6 @@ void do_notify_resume(struct pt_regs *regs, void *unused, if (thread_info_flags & _TIF_HRTICK_RESCHED) hrtick_resched(); - + clear_thread_flag(TIF_IRET); } |