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 Original line Diff line number Diff line
@@ -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,83 +40,29 @@
 */
 */
#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);

	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;
	current->state = TASK_INTERRUPTIBLE;
	schedule();
	schedule();
		if (do_signal(0, &saveset, regs))
	set_thread_flag(TIF_RESTORE_SIGMASK);
			/* We will get here twice: once to call the signal
	return -ERESTARTNOHAND;
			   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 
int sys_sigaction(int sig, const struct old_sigaction __user *act,
sys_sigaction(int sig, const struct old_sigaction __user *act,
	struct old_sigaction *oact)
	struct old_sigaction *oact)
{
{
	struct k_sigaction new_ka, old_ka;
	struct k_sigaction new_ka, old_ka;
@@ -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());
}
}
@@ -300,8 +245,8 @@ asmlinkage int sys_rt_sigreturn(long r10, long r11, long r12, long r13,
 * 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,7 +269,8 @@ 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)
@@ -352,7 +298,7 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
 * 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;
@@ -401,13 +347,14 @@ 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;
@@ -444,7 +391,8 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
		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,
			(short __user *)(frame->retcode+2));
		err |= __put_user(0xe93d, (short __user *)(frame->retcode+4));
		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 */
	/* 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;
			regs->r10 = -EINTR;
			break;
			break;

		case -ERESTARTSYS:
		case -ERESTARTSYS:
				/* ERESTARTSYS means to restart the syscall if there is no
			/* ERESTARTSYS means to restart the syscall if
				   handler or the handler was registered with SA_RESTART */
			 * there is no handler or the handler was
			 * registered with SA_RESTART */
			if (!(ka->sa.sa_flags & SA_RESTART)) {
			if (!(ka->sa.sa_flags & SA_RESTART)) {
				regs->r10 = -EINTR;
				regs->r10 = -EINTR;
				break;
				break;
			}
			}
		/* fallthrough */
		/* fallthrough */
		case -ERESTARTNOINTR:
		case -ERESTARTNOINTR:
				/* ERESTARTNOINTR means that the syscall should be called again
			/* ERESTARTNOINTR means that the syscall should
				   after the signal handler returns. */
			 * be called again after the signal handler returns. */
			RESTART_CRIS_SYS(regs);
			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)
		ka->sa.sa_handler = SIG_DFL;


	if (ret == 0) {
		spin_lock_irq(&current->sighand->siglock);
		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))
		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;
}


/*
/*
 * Note that 'init' is a special process: it doesn't get signals it doesn't
 * 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.
 * 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? */
@@ -574,5 +541,11 @@ int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs)
			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);
	}
}
}