CRIS v10: correct do_signal to fix oops and clean up signal handling in general

This fixes a kernel panic on boot due to do_signal not being compatible
with it's callers.

- do_signal now returns void, and does not have the previous signal set
  as a parameter.
- Remove sys_rt_sigsuspend, we can use the common one instead.
- Change sys_sigsuspend to be more like x86, don't call do_signal here.
- handle_signal, setup_frame and setup_rt_frame now return -EFAULT
  if we've delivered a segfault, which is used by callers to perform
  necessary cleanup.
- Break long lines, correct whitespace and formatting errors.

Signed-off-by: Jesper Nilsson <jesper.nilsson@axis.com>
Cc: Mikael Starvik <mikael.starvik@axis.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Jesper Nilsson 2008-01-14 00:55:22 -08:00 committed by Linus Torvalds
parent 3ea0345be3
commit a4858d4dab

View file

@ -7,7 +7,7 @@
* *
* Ideas also taken from arch/arm. * Ideas also taken from arch/arm.
* *
* Copyright (C) 2000, 2001 Axis Communications AB * Copyright (C) 2000-2007 Axis Communications AB
* *
* Authors: Bjorn Wesen (bjornw@axis.com) * Authors: Bjorn Wesen (bjornw@axis.com)
* *
@ -40,84 +40,30 @@
*/ */
#define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->irp -= 2; #define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->irp -= 2;
int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs); void do_signal(int canrestart, struct pt_regs *regs);
/* /*
* Atomically swap in the new signal mask, and wait for a signal. Define * Atomically swap in the new signal mask, and wait for a signal. Define
* dummy arguments to be able to reach the regs argument. (Note that this * dummy arguments to be able to reach the regs argument. (Note that this
* arrangement relies on old_sigset_t occupying one register.) * arrangement relies on old_sigset_t occupying one register.)
*/ */
int int sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof,
sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof, long srp, struct pt_regs *regs)
long srp, struct pt_regs *regs)
{ {
sigset_t saveset;
mask &= _BLOCKABLE; mask &= _BLOCKABLE;
spin_lock_irq(&current->sighand->siglock); spin_lock_irq(&current->sighand->siglock);
saveset = current->blocked; current->saved_sigmask = current->blocked;
siginitset(&current->blocked, mask); siginitset(&current->blocked, mask);
recalc_sigpending(); recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock); spin_unlock_irq(&current->sighand->siglock);
current->state = TASK_INTERRUPTIBLE;
regs->r10 = -EINTR; schedule();
while (1) { set_thread_flag(TIF_RESTORE_SIGMASK);
current->state = TASK_INTERRUPTIBLE; return -ERESTARTNOHAND;
schedule();
if (do_signal(0, &saveset, regs))
/* We will get here twice: once to call the signal
handler, then again to return from the
sigsuspend system call. When calling the
signal handler, R10 holds the signal number as
set through do_signal. The sigsuspend call
will return with the restored value set above;
always -EINTR. */
return regs->r10;
}
} }
/* Define dummy arguments to be able to reach the regs argument. (Note that int sys_sigaction(int sig, const struct old_sigaction __user *act,
* this arrangement relies on size_t occupying one register.) struct old_sigaction *oact)
*/
int
sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13,
long mof, long srp, struct pt_regs *regs)
{
sigset_t saveset, newset;
/* XXX: Don't preclude handling different sized sigset_t's. */
if (sigsetsize != sizeof(sigset_t))
return -EINVAL;
if (copy_from_user(&newset, unewset, sizeof(newset)))
return -EFAULT;
sigdelsetmask(&newset, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
saveset = current->blocked;
current->blocked = newset;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
regs->r10 = -EINTR;
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
if (do_signal(0, &saveset, regs))
/* We will get here twice: once to call the signal
handler, then again to return from the
sigsuspend system call. When calling the
signal handler, R10 holds the signal number as
set through do_signal. The sigsuspend call
will return with the restored value set above;
always -EINTR. */
return regs->r10;
}
}
int
sys_sigaction(int sig, const struct old_sigaction __user *act,
struct old_sigaction *oact)
{ {
struct k_sigaction new_ka, old_ka; struct k_sigaction new_ka, old_ka;
int ret; int ret;
@ -147,8 +93,7 @@ sys_sigaction(int sig, const struct old_sigaction __user *act,
return ret; return ret;
} }
int int sys_sigaltstack(const stack_t *uss, stack_t __user *uoss)
sys_sigaltstack(const stack_t *uss, stack_t __user *uoss)
{ {
return do_sigaltstack(uss, uoss, rdusp()); return do_sigaltstack(uss, uoss, rdusp());
} }
@ -205,7 +150,7 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
/* TODO: the other ports use regs->orig_XX to disable syscall checks /* TODO: the other ports use regs->orig_XX to disable syscall checks
* after this completes, but we don't use that mechanism. maybe we can * after this completes, but we don't use that mechanism. maybe we can
* use it now ? * use it now ?
*/ */
return err; return err;
@ -216,7 +161,7 @@ badframe:
/* Define dummy arguments to be able to reach the regs argument. */ /* Define dummy arguments to be able to reach the regs argument. */
asmlinkage int sys_sigreturn(long r10, long r11, long r12, long r13, long mof, asmlinkage int sys_sigreturn(long r10, long r11, long r12, long r13, long mof,
long srp, struct pt_regs *regs) long srp, struct pt_regs *regs)
{ {
struct sigframe __user *frame = (struct sigframe *)rdusp(); struct sigframe __user *frame = (struct sigframe *)rdusp();
@ -243,7 +188,7 @@ asmlinkage int sys_sigreturn(long r10, long r11, long r12, long r13, long mof,
current->blocked = set; current->blocked = set;
recalc_sigpending(); recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock); spin_unlock_irq(&current->sighand->siglock);
if (restore_sigcontext(regs, &frame->sc)) if (restore_sigcontext(regs, &frame->sc))
goto badframe; goto badframe;
@ -254,11 +199,11 @@ asmlinkage int sys_sigreturn(long r10, long r11, long r12, long r13, long mof,
badframe: badframe:
force_sig(SIGSEGV, current); force_sig(SIGSEGV, current);
return 0; return 0;
} }
/* Define dummy arguments to be able to reach the regs argument. */ /* Define dummy arguments to be able to reach the regs argument. */
asmlinkage int sys_rt_sigreturn(long r10, long r11, long r12, long r13, asmlinkage int sys_rt_sigreturn(long r10, long r11, long r12, long r13,
long mof, long srp, struct pt_regs *regs) long mof, long srp, struct pt_regs *regs)
{ {
struct rt_sigframe __user *frame = (struct rt_sigframe *)rdusp(); struct rt_sigframe __user *frame = (struct rt_sigframe *)rdusp();
@ -282,7 +227,7 @@ asmlinkage int sys_rt_sigreturn(long r10, long r11, long r12, long r13,
current->blocked = set; current->blocked = set;
recalc_sigpending(); recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock); spin_unlock_irq(&current->sighand->siglock);
if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
goto badframe; goto badframe;
@ -294,14 +239,14 @@ asmlinkage int sys_rt_sigreturn(long r10, long r11, long r12, long r13,
badframe: badframe:
force_sig(SIGSEGV, current); force_sig(SIGSEGV, current);
return 0; return 0;
} }
/* /*
* Set up a signal frame. * Set up a signal frame.
*/ */
static int static int setup_sigcontext(struct sigcontext __user *sc,
setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, unsigned long mask) struct pt_regs *regs, unsigned long mask)
{ {
int err = 0; int err = 0;
unsigned long usp = rdusp(); unsigned long usp = rdusp();
@ -324,10 +269,11 @@ setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, unsigned lo
return err; return err;
} }
/* figure out where we want to put the new signal frame - usually on the stack */ /* Figure out where we want to put the new signal frame
* - usually on the stack. */
static inline void __user * 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 = rdusp(); unsigned long sp = rdusp();
@ -345,15 +291,15 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
} }
/* grab and setup a signal frame. /* grab and setup a signal frame.
* *
* basically we stack a lot of state info, and arrange for the * basically we stack a lot of state info, and arrange for the
* user-mode program to return to the kernel using either a * user-mode program to return to the kernel using either a
* trampoline which performs the syscall sigreturn, or a provided * trampoline which performs the syscall sigreturn, or a provided
* user-mode trampoline. * user-mode trampoline.
*/ */
static void setup_frame(int sig, struct k_sigaction *ka, static int setup_frame(int sig, struct k_sigaction *ka,
sigset_t *set, struct pt_regs * regs) sigset_t *set, struct pt_regs *regs)
{ {
struct sigframe __user *frame; struct sigframe __user *frame;
unsigned long return_ip; unsigned long return_ip;
@ -401,14 +347,15 @@ static void setup_frame(int sig, struct k_sigaction *ka,
wrusp((unsigned long)frame); wrusp((unsigned long)frame);
return; return 0;
give_sigsegv: give_sigsegv:
force_sigsegv(sig, current); force_sigsegv(sig, current);
return -EFAULT;
} }
static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, 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)
{ {
struct rt_sigframe __user *frame; struct rt_sigframe __user *frame;
unsigned long return_ip; unsigned long return_ip;
@ -443,9 +390,10 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
/* trampoline - the desired return ip is the retcode itself */ /* trampoline - the desired return ip is the retcode itself */
return_ip = (unsigned long)&frame->retcode; return_ip = (unsigned long)&frame->retcode;
/* This is movu.w __NR_rt_sigreturn, r9; break 13; */ /* This is movu.w __NR_rt_sigreturn, r9; break 13; */
err |= __put_user(0x9c5f, (short __user*)(frame->retcode+0)); err |= __put_user(0x9c5f, (short __user *)(frame->retcode+0));
err |= __put_user(__NR_rt_sigreturn, (short __user*)(frame->retcode+2)); err |= __put_user(__NR_rt_sigreturn,
err |= __put_user(0xe93d, (short __user*)(frame->retcode+4)); (short __user *)(frame->retcode+2));
err |= __put_user(0xe93d, (short __user *)(frame->retcode+4));
} }
if (err) if (err)
@ -455,73 +403,81 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
/* Set up registers for signal handler */ /* Set up registers for signal handler */
regs->irp = (unsigned long) ka->sa.sa_handler; /* what we enter NOW */ /* What we enter NOW */
regs->srp = return_ip; /* what we enter LATER */ regs->irp = (unsigned long) ka->sa.sa_handler;
regs->r10 = sig; /* first argument is signo */ /* What we enter LATER */
regs->r11 = (unsigned long) &frame->info; /* second argument is (siginfo_t *) */ regs->srp = return_ip;
regs->r12 = 0; /* third argument is unused */ /* First argument is signo */
regs->r10 = sig;
/* actually move the usp to reflect the stacked frame */ /* Second argument is (siginfo_t *) */
regs->r11 = (unsigned long)&frame->info;
/* Third argument is unused */
regs->r12 = 0;
/* Actually move the usp to reflect the stacked frame */
wrusp((unsigned long)frame); wrusp((unsigned long)frame);
return; return 0;
give_sigsegv: give_sigsegv:
force_sigsegv(sig, current); force_sigsegv(sig, current);
return -EFAULT;
} }
/* /*
* OK, we're invoking a handler * OK, we're invoking a handler
*/ */
static inline void static inline int handle_signal(int canrestart, unsigned long sig,
handle_signal(int canrestart, unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
siginfo_t *info, struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs)
sigset_t *oldset, struct pt_regs * regs)
{ {
int ret;
/* Are we from a system call? */ /* Are we from a system call? */
if (canrestart) { if (canrestart) {
/* If so, check system call restarting.. */ /* If so, check system call restarting.. */
switch (regs->r10) { switch (regs->r10) {
case -ERESTART_RESTARTBLOCK: case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND: case -ERESTARTNOHAND:
/* ERESTARTNOHAND means that the syscall should only be /* ERESTARTNOHAND means that the syscall should
restarted if there was no handler for the signal, and since * only be restarted if there was no handler for
we only get here if there is a handler, we don't restart */ * the signal, and since we only get here if there
* is a handler, we don't restart */
regs->r10 = -EINTR;
break;
case -ERESTARTSYS:
/* ERESTARTSYS means to restart the syscall if
* there is no handler or the handler was
* registered with SA_RESTART */
if (!(ka->sa.sa_flags & SA_RESTART)) {
regs->r10 = -EINTR; regs->r10 = -EINTR;
break; break;
}
case -ERESTARTSYS: /* fallthrough */
/* ERESTARTSYS means to restart the syscall if there is no case -ERESTARTNOINTR:
handler or the handler was registered with SA_RESTART */ /* ERESTARTNOINTR means that the syscall should
if (!(ka->sa.sa_flags & SA_RESTART)) { * be called again after the signal handler returns. */
regs->r10 = -EINTR; RESTART_CRIS_SYS(regs);
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
/* ERESTARTNOINTR means that the syscall should be called again
after the signal handler returns. */
RESTART_CRIS_SYS(regs);
} }
} }
/* Set up the stack frame */ /* Set up the stack frame */
if (ka->sa.sa_flags & SA_SIGINFO) if (ka->sa.sa_flags & SA_SIGINFO)
setup_rt_frame(sig, ka, info, oldset, regs); ret = setup_rt_frame(sig, ka, info, oldset, regs);
else else
setup_frame(sig, ka, oldset, regs); ret = setup_frame(sig, ka, oldset, regs);
if (ka->sa.sa_flags & SA_ONESHOT) if (ret == 0) {
ka->sa.sa_handler = SIG_DFL; spin_lock_irq(&current->sighand->siglock);
sigorsets(&current->blocked, &current->blocked,
spin_lock_irq(&current->sighand->siglock); &ka->sa.sa_mask);
sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask); if (!(ka->sa.sa_flags & SA_NODEFER))
if (!(ka->sa.sa_flags & SA_NODEFER)) sigaddset(&current->blocked, sig);
sigaddset(&current->blocked,sig); recalc_sigpending();
recalc_sigpending(); spin_unlock_irq(&current->sighand->siglock);
spin_unlock_irq(&current->sighand->siglock); }
return ret;
} }
/* /*
@ -536,11 +492,12 @@ handle_signal(int canrestart, unsigned long sig,
* mode below. * mode below.
*/ */
int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) void do_signal(int canrestart, struct pt_regs *regs)
{ {
siginfo_t info; siginfo_t info;
int signr; int signr;
struct k_sigaction ka; struct k_sigaction ka;
sigset_t *oldset;
/* /*
* We want the common case to go fast, which * We want the common case to go fast, which
@ -549,16 +506,26 @@ int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs)
* if so. * if so.
*/ */
if (!user_mode(regs)) if (!user_mode(regs))
return 1; return;
if (!oldset) if (test_thread_flag(TIF_RESTORE_SIGMASK))
oldset = &current->saved_sigmask;
else
oldset = &current->blocked; oldset = &current->blocked;
signr = get_signal_to_deliver(&info, &ka, regs, NULL); signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) { if (signr > 0) {
/* Whee! Actually deliver the signal. */ /* Whee! Actually deliver the signal. */
handle_signal(canrestart, signr, &info, &ka, oldset, regs); if (handle_signal(canrestart, signr, &info, &ka,
return 1; oldset, regs)) {
/* 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 */
if (test_thread_flag(TIF_RESTORE_SIGMASK))
clear_thread_flag(TIF_RESTORE_SIGMASK);
}
return;
} }
/* Did we come from a system call? */ /* Did we come from a system call? */
@ -569,10 +536,16 @@ int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs)
regs->r10 == -ERESTARTNOINTR) { regs->r10 == -ERESTARTNOINTR) {
RESTART_CRIS_SYS(regs); RESTART_CRIS_SYS(regs);
} }
if (regs->r10 == -ERESTART_RESTARTBLOCK){ if (regs->r10 == -ERESTART_RESTARTBLOCK) {
regs->r10 = __NR_restart_syscall; regs->r10 = __NR_restart_syscall;
regs->irp -= 2; regs->irp -= 2;
} }
} }
return 0;
/* if there's no signal to deliver, we just put the saved sigmask
* back */
if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
clear_thread_flag(TIF_RESTORE_SIGMASK);
sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
}
} }