[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>
This commit is contained in:
parent
7ee1dd3fee
commit
8efc0ab50e
2 changed files with 46 additions and 29 deletions
|
@ -1076,7 +1076,7 @@ __entry_work_notifysig:
|
||||||
LEDS 0x6410
|
LEDS 0x6410
|
||||||
ori.p gr4,#0,gr8
|
ori.p gr4,#0,gr8
|
||||||
call do_notify_resume
|
call do_notify_resume
|
||||||
bra __entry_return_direct
|
bra __entry_resume_userspace
|
||||||
|
|
||||||
# perform syscall entry tracing
|
# perform syscall entry tracing
|
||||||
__syscall_trace_entry:
|
__syscall_trace_entry:
|
||||||
|
|
|
@ -297,7 +297,8 @@ static inline void __user *get_sigframe(struct k_sigaction *ka,
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
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;
|
struct sigframe __user *frame;
|
||||||
int rsig;
|
int rsig;
|
||||||
|
@ -362,25 +363,29 @@ static void setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct p
|
||||||
|
|
||||||
set_fs(USER_DS);
|
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
|
#if DEBUG_SIG
|
||||||
printk("SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n",
|
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
|
#endif
|
||||||
|
|
||||||
return;
|
return 1;
|
||||||
|
|
||||||
give_sigsegv:
|
give_sigsegv:
|
||||||
if (sig == SIGSEGV)
|
|
||||||
ka->sa.sa_handler = SIG_DFL;
|
|
||||||
|
|
||||||
force_sig(SIGSEGV, current);
|
force_sig(SIGSEGV, current);
|
||||||
|
return 0;
|
||||||
|
|
||||||
} /* end setup_frame() */
|
} /* end setup_frame() */
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
|
@ -457,17 +462,21 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
||||||
|
|
||||||
set_fs(USER_DS);
|
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
|
#if DEBUG_SIG
|
||||||
printk("SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n",
|
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
|
#endif
|
||||||
|
|
||||||
return;
|
return 1;
|
||||||
|
|
||||||
give_sigsegv:
|
give_sigsegv:
|
||||||
if (sig == SIGSEGV)
|
|
||||||
ka->sa.sa_handler = SIG_DFL;
|
|
||||||
force_sig(SIGSEGV, current);
|
force_sig(SIGSEGV, current);
|
||||||
|
return 0;
|
||||||
|
|
||||||
} /* end setup_rt_frame() */
|
} /* end setup_rt_frame() */
|
||||||
|
|
||||||
|
@ -475,10 +484,12 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
||||||
/*
|
/*
|
||||||
* OK, we're invoking a handler
|
* OK, we're invoking a handler
|
||||||
*/
|
*/
|
||||||
static void handle_signal(unsigned long sig, siginfo_t *info,
|
static int handle_signal(unsigned long sig, siginfo_t *info,
|
||||||
struct k_sigaction *ka, sigset_t *oldset,
|
struct k_sigaction *ka, sigset_t *oldset,
|
||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* Are we from a system call? */
|
/* Are we from a system call? */
|
||||||
if (in_syscall(regs)) {
|
if (in_syscall(regs)) {
|
||||||
/* If so, check system call restarting.. */
|
/* If so, check system call restarting.. */
|
||||||
|
@ -493,6 +504,7 @@ static void handle_signal(unsigned long sig, siginfo_t *info,
|
||||||
regs->gr8 = -EINTR;
|
regs->gr8 = -EINTR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
case -ERESTARTNOINTR:
|
case -ERESTARTNOINTR:
|
||||||
regs->gr8 = regs->orig_gr8;
|
regs->gr8 = regs->orig_gr8;
|
||||||
|
@ -502,16 +514,22 @@ static void handle_signal(unsigned long sig, siginfo_t *info,
|
||||||
|
|
||||||
/* 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 (ret) {
|
||||||
spin_lock_irq(¤t->sighand->siglock);
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask);
|
sigorsets(¤t->blocked, ¤t->blocked,
|
||||||
|
&ka->sa.sa_mask);
|
||||||
if (!(ka->sa.sa_flags & SA_NODEFER))
|
if (!(ka->sa.sa_flags & SA_NODEFER))
|
||||||
sigaddset(¤t->blocked, sig);
|
sigaddset(¤t->blocked, sig);
|
||||||
recalc_sigpending();
|
recalc_sigpending();
|
||||||
spin_unlock_irq(¤t->sighand->siglock);
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
} /* end handle_signal() */
|
} /* end handle_signal() */
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -542,12 +560,10 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset)
|
||||||
oldset = ¤t->blocked;
|
oldset = ¤t->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)
|
||||||
handle_signal(signr, &info, &ka, oldset, regs);
|
return handle_signal(signr, &info, &ka, oldset, regs);
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
no_signal:
|
no_signal:
|
||||||
/* Did we come from a system call? */
|
/* Did we come from a system call? */
|
||||||
if (regs->syscallno >= 0) {
|
if (regs->syscallno >= 0) {
|
||||||
/* Restart the system call - no handlers present */
|
/* Restart the system call - no handlers present */
|
||||||
|
@ -565,6 +581,7 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset)
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
} /* end do_signal() */
|
} /* end do_signal() */
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
Loading…
Reference in a new issue