[PATCH] frv: fix signal handling
The attached patch makes FRV signal handling work properly:
(1) After do_notify_resume() has been called, the work flags must be checked
again (there may be another signal to deliver or the process might require
rescheduling for instance).
(2) After the signal frame is set up on the userspace stack, ptrace() should
be given an opportunity to single-step into the signal handler.
(3) The error state from setting up a signal frame should be passed back up
the call chain.
(4) The segfault handler shouldn't be preemptively reset in the arch if we
fail to deliver a SEGV signal: force_sig() will take care of that.
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/arch/frv/kernel/entry.S b/arch/frv/kernel/entry.S
index ad10ea5..5f65483 100644
--- a/arch/frv/kernel/entry.S
+++ b/arch/frv/kernel/entry.S
@@ -1076,7 +1076,7 @@
LEDS 0x6410
ori.p gr4,#0,gr8
call do_notify_resume
- bra __entry_return_direct
+ bra __entry_resume_userspace
# perform syscall entry tracing
__syscall_trace_entry:
diff --git a/arch/frv/kernel/signal.c b/arch/frv/kernel/signal.c
index d4ccc07..89a1cf5 100644
--- a/arch/frv/kernel/signal.c
+++ b/arch/frv/kernel/signal.c
@@ -297,7 +297,8 @@
/*
*
*/
-static void 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)
{
struct sigframe __user *frame;
int rsig;
@@ -362,26 +363,30 @@
set_fs(USER_DS);
+ /* the tracer may want to single-step inside the handler */
+ if (test_thread_flag(TIF_SINGLESTEP))
+ ptrace_notify(SIGTRAP);
+
#if DEBUG_SIG
printk("SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n",
- sig, current->comm, current->pid, frame, regs->pc, frame->pretcode);
+ sig, current->comm, current->pid, frame, regs->pc,
+ frame->pretcode);
#endif
- return;
+ return 1;
give_sigsegv:
- if (sig == SIGSEGV)
- ka->sa.sa_handler = SIG_DFL;
-
force_sig(SIGSEGV, current);
+ return 0;
+
} /* end setup_frame() */
/*****************************************************************************/
/*
*
*/
-static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
- sigset_t *set, struct pt_regs * regs)
+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;
int rsig;
@@ -457,17 +462,21 @@
set_fs(USER_DS);
+ /* the tracer may want to single-step inside the handler */
+ if (test_thread_flag(TIF_SINGLESTEP))
+ ptrace_notify(SIGTRAP);
+
#if DEBUG_SIG
printk("SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n",
- sig, current->comm, current->pid, frame, regs->pc, frame->pretcode);
+ sig, current->comm, current->pid, frame, regs->pc,
+ frame->pretcode);
#endif
- return;
+ return 1;
give_sigsegv:
- if (sig == SIGSEGV)
- ka->sa.sa_handler = SIG_DFL;
force_sig(SIGSEGV, current);
+ return 0;
} /* end setup_rt_frame() */
@@ -475,10 +484,12 @@
/*
* OK, we're invoking a handler
*/
-static void handle_signal(unsigned long sig, siginfo_t *info,
- struct k_sigaction *ka, sigset_t *oldset,
- struct pt_regs *regs)
+static int handle_signal(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 (in_syscall(regs)) {
/* If so, check system call restarting.. */
@@ -493,6 +504,7 @@
regs->gr8 = -EINTR;
break;
}
+
/* fallthrough */
case -ERESTARTNOINTR:
regs->gr8 = regs->orig_gr8;
@@ -502,16 +514,22 @@
/* 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);
+ ret = setup_frame(sig, ka, oldset, regs);
- 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) {
+ 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 ret;
+
} /* end handle_signal() */
/*****************************************************************************/
@@ -542,12 +560,10 @@
oldset = ¤t->blocked;
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
- if (signr > 0) {
- handle_signal(signr, &info, &ka, oldset, regs);
- return 1;
- }
+ if (signr > 0)
+ return handle_signal(signr, &info, &ka, oldset, regs);
- no_signal:
+no_signal:
/* Did we come from a system call? */
if (regs->syscallno >= 0) {
/* Restart the system call - no handlers present */
@@ -565,6 +581,7 @@
}
return 0;
+
} /* end do_signal() */
/*****************************************************************************/