Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a4858d4d authored by Jesper Nilsson's avatar Jesper Nilsson Committed by Linus Torvalds
Browse files

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: default avatarJesper Nilsson <jesper.nilsson@axis.com>
Cc: Mikael Starvik <mikael.starvik@axis.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 3ea0345b
Loading
Loading
Loading
Loading
+112 −139
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@
 *
 *  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)
 *
@@ -40,83 +40,29 @@
 */
#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
 * dummy arguments to be able to reach the regs argument.  (Note that this
 * arrangement relies on old_sigset_t occupying one register.)
 */
int
sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof, 
int sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof,
	long srp, struct pt_regs *regs)
{
	sigset_t saveset;

	mask &= _BLOCKABLE;
	spin_lock_irq(&current->sighand->siglock);
	saveset = current->blocked;
	current->saved_sigmask = current->blocked;
	siginitset(&current->blocked, mask);
	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;
	}
}

/* Define dummy arguments to be able to reach the regs argument.  (Note that
 * this arrangement relies on size_t occupying one register.)
 */
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;
	}
	set_thread_flag(TIF_RESTORE_SIGMASK);
	return -ERESTARTNOHAND;
}

int 
sys_sigaction(int sig, const struct old_sigaction __user *act,
int sys_sigaction(int sig, const struct old_sigaction __user *act,
	struct old_sigaction *oact)
{
	struct k_sigaction new_ka, old_ka;
@@ -147,8 +93,7 @@ sys_sigaction(int sig, const struct old_sigaction __user *act,
	return ret;
}

int
sys_sigaltstack(const stack_t *uss, stack_t __user *uoss)
int sys_sigaltstack(const stack_t *uss, stack_t __user *uoss)
{
	return do_sigaltstack(uss, uoss, rdusp());
}
@@ -300,8 +245,8 @@ badframe:
 * Set up a signal frame.
 */

static int
setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, unsigned long mask)
static int setup_sigcontext(struct sigcontext __user *sc,
	struct pt_regs *regs, unsigned long mask)
{
	int err = 0;
	unsigned long usp = rdusp();
@@ -324,7 +269,8 @@ setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, unsigned lo
	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 *
get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size)
@@ -352,7 +298,7 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
 * 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)
{
	struct sigframe __user *frame;
@@ -401,13 +347,14 @@ static void setup_frame(int sig, struct k_sigaction *ka,

	wrusp((unsigned long)frame);

	return;
	return 0;

give_sigsegv:
	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)
{
	struct rt_sigframe __user *frame;
@@ -444,7 +391,8 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
		return_ip = (unsigned long)&frame->retcode;
		/* This is movu.w __NR_rt_sigreturn, r9; break 13; */
		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,
			(short __user *)(frame->retcode+2));
		err |= __put_user(0xe93d, (short __user *)(frame->retcode+4));
	}

@@ -455,74 +403,82 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,

	/* Set up registers for signal handler */

	regs->irp = (unsigned long) ka->sa.sa_handler;  /* what we enter NOW   */
	regs->srp = return_ip;                          /* what we enter LATER */
	regs->r10 = sig;                                /* first argument is signo */
        regs->r11 = (unsigned long) &frame->info;       /* second argument is (siginfo_t *) */
        regs->r12 = 0;                                  /* third argument is unused */

	/* actually move the usp to reflect the stacked frame */

	/* What we enter NOW   */
	regs->irp = (unsigned long) ka->sa.sa_handler;
	/* What we enter LATER */
	regs->srp = return_ip;
	/* First argument is signo */
	regs->r10 = sig;
	/* 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);

	return;
	return 0;

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

/*
 * OK, we're invoking a handler
 */

static inline void
handle_signal(int canrestart, unsigned long sig,
static inline int handle_signal(int canrestart, unsigned long sig,
	siginfo_t *info, struct k_sigaction *ka,
	sigset_t *oldset, struct pt_regs *regs)
{
	int ret;

	/* Are we from a system call? */
	if (canrestart) {
		/* If so, check system call restarting.. */
		switch (regs->r10) {
		case -ERESTART_RESTARTBLOCK:
		case -ERESTARTNOHAND:
				/* ERESTARTNOHAND means that the syscall should only be
				   restarted if there was no handler for the signal, and since
				   we only get here if there is a handler, we don't restart */
			/* ERESTARTNOHAND means that the syscall should
			 * only be restarted if there was no handler for
			 * 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 */
			/* 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;
				break;
			}
		/* fallthrough */
		case -ERESTARTNOINTR:
				/* ERESTARTNOINTR means that the syscall should be called again
				   after the signal handler returns. */
			/* ERESTARTNOINTR means that the syscall should
			 * be called again after the signal handler returns. */
			RESTART_CRIS_SYS(regs);
		}
	}

	/* Set up the stack frame */
	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
		setup_frame(sig, ka, oldset, regs);

	if (ka->sa.sa_flags & SA_ONESHOT)
		ka->sa.sa_handler = SIG_DFL;
		ret = setup_frame(sig, ka, oldset, regs);

	if (ret == 0) {
		spin_lock_irq(&current->sighand->siglock);
	sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
		sigorsets(&current->blocked, &current->blocked,
			&ka->sa.sa_mask);
		if (!(ka->sa.sa_flags & SA_NODEFER))
			sigaddset(&current->blocked, sig);
		recalc_sigpending();
		spin_unlock_irq(&current->sighand->siglock);
	}
	return ret;
}

/*
 * Note that 'init' is a special process: it doesn't get signals it doesn't
@@ -536,11 +492,12 @@ handle_signal(int canrestart, unsigned long sig,
 * 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;
	int signr;
        struct k_sigaction ka;
	sigset_t *oldset;

	/*
	 * 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 (!user_mode(regs))
		return 1;
		return;

	if (!oldset)
	if (test_thread_flag(TIF_RESTORE_SIGMASK))
		oldset = &current->saved_sigmask;
	else
		oldset = &current->blocked;

	signr = get_signal_to_deliver(&info, &ka, regs, NULL);
	if (signr > 0) {
		/* Whee!  Actually deliver the signal.  */
		handle_signal(canrestart, signr, &info, &ka, oldset, regs);
		return 1;
		if (handle_signal(canrestart, signr, &info, &ka,
				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? */
@@ -574,5 +541,11 @@ int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs)
			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);
	}
}