forked from Minki/linux
67850b7bdc
of Peter Zijlstra was encountering with ptrace in his freezer rewrite I identified some cleanups to ptrace_stop that make sense on their own and move make resolving the other problems much simpler. The biggest issue is the habbit of the ptrace code to change task->__state from the tracer to suppress TASK_WAKEKILL from waking up the tracee. No other code in the kernel does that and it is straight forward to update signal_wake_up and friends to make that unnecessary. Peter's task freezer sets frozen tasks to a new state TASK_FROZEN and then it stores them by calling "wake_up_state(t, TASK_FROZEN)" relying on the fact that all stopped states except the special stop states can tolerate spurious wake up and recover their state. The state of stopped and traced tasked is changed to be stored in task->jobctl as well as in task->__state. This makes it possible for the freezer to recover tasks in these special states, as well as serving as a general cleanup. With a little more work in that direction I believe TASK_STOPPED can learn to tolerate spurious wake ups and become an ordinary stop state. The TASK_TRACED state has to remain a special state as the registers for a process are only reliably available when the process is stopped in the scheduler. Fundamentally ptrace needs acess to the saved register values of a task. There are bunch of semi-random ptrace related cleanups that were found while looking at these issues. One cleanup that deserves to be called out is from commit57b6de08b5
("ptrace: Admit ptrace_stop can generate spuriuos SIGTRAPs"). This makes a change that is technically user space visible, in the handling of what happens to a tracee when a tracer dies unexpectedly. According to our testing and our understanding of userspace nothing cares that spurious SIGTRAPs can be generated in that case. The entire discussion can be found at: https://lkml.kernel.org/r/87a6bv6dl6.fsf_-_@email.froward.int.ebiederm.org Eric W. Biederman (11): signal: Rename send_signal send_signal_locked signal: Replace __group_send_sig_info with send_signal_locked ptrace/um: Replace PT_DTRACE with TIF_SINGLESTEP ptrace/xtensa: Replace PT_SINGLESTEP with TIF_SINGLESTEP ptrace: Remove arch_ptrace_attach signal: Use lockdep_assert_held instead of assert_spin_locked ptrace: Reimplement PTRACE_KILL by always sending SIGKILL ptrace: Document that wait_task_inactive can't fail ptrace: Admit ptrace_stop can generate spuriuos SIGTRAPs ptrace: Don't change __state ptrace: Always take siglock in ptrace_resume Peter Zijlstra (1): sched,signal,ptrace: Rework TASK_TRACED, TASK_STOPPED state arch/ia64/include/asm/ptrace.h | 4 -- arch/ia64/kernel/ptrace.c | 57 ---------------- arch/um/include/asm/thread_info.h | 2 + arch/um/kernel/exec.c | 2 +- arch/um/kernel/process.c | 2 +- arch/um/kernel/ptrace.c | 8 +-- arch/um/kernel/signal.c | 4 +- arch/x86/kernel/step.c | 3 +- arch/xtensa/kernel/ptrace.c | 4 +- arch/xtensa/kernel/signal.c | 4 +- drivers/tty/tty_jobctrl.c | 4 +- include/linux/ptrace.h | 7 -- include/linux/sched.h | 10 ++- include/linux/sched/jobctl.h | 8 +++ include/linux/sched/signal.h | 20 ++++-- include/linux/signal.h | 3 +- kernel/ptrace.c | 87 ++++++++--------------- kernel/sched/core.c | 5 +- kernel/signal.c | 140 +++++++++++++++++--------------------- kernel/time/posix-cpu-timers.c | 6 +- 20 files changed, 140 insertions(+), 240 deletions(-) Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEgjlraLDcwBA2B+6cC/v6Eiajj0AFAmKaXaYACgkQC/v6Eiaj j0CgoA/+JncSQ6PY2D5Jh1apvHzmnRsFXzr3DRvtv/CVx4oIebOXRQFyVDeD5tRn TmMgB29HpBlHRDLojlmlZRGAld1HR/aPEW9j8W1D3Sy/ZFO5L8lQitv9aDHO9Ntw 4lZvlhS1M0KhATudVVBqSPixiG6CnV5SsGmixqdOyg7xcXSY6G1l2nB7Zk9I3Tat ZlmhuZ6R5Z5qsm4MEq0vUSrnsHiGxYrpk6uQOaVz8Wkv8ZFmbutt6XgxF0tsyZNn mHSmWSiZzIgBjTlaibEmxi8urYJTPj3vGBeJQVYHblFwLFi6+Oy7bDxQbWjQvaZh DsgWPScfBF4Jm0+8hhCiSYpvPp8XnZuklb4LNCeok/VFr+KfSmpJTIhn00kagQ1u vxQDqLws8YLW4qsfGydfx9uUIFCbQE/V2VDYk5J3Re3gkUNDOOR1A56hPniKv6VB 2aqGO2Fl0RdBbUa3JF+XI5Pwq5y1WrqR93EUvj+5+u5W9rZL/8WLBHBMEz6gbmfD DhwFE0y8TG2WRlWJVEDRId+5zo3di/YvasH0vJZ5HbrxhS2RE/yIGAd+kKGx/lZO qWDJC7IHvFJ7Mw5KugacyF0SHeNdloyBM7KZW6HeXmgKn9IMJBpmwib92uUkRZJx D8j/bHHqD/zsgQ39nO+c4M0MmhO/DsPLG/dnGKrRCu7v1tmEnkY= =ZUuO -----END PGP SIGNATURE----- Merge tag 'ptrace_stop-cleanup-for-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace Pull ptrace_stop cleanups from Eric Biederman: "While looking at the ptrace problems with PREEMPT_RT and the problems Peter Zijlstra was encountering with ptrace in his freezer rewrite I identified some cleanups to ptrace_stop that make sense on their own and move make resolving the other problems much simpler. The biggest issue is the habit of the ptrace code to change task->__state from the tracer to suppress TASK_WAKEKILL from waking up the tracee. No other code in the kernel does that and it is straight forward to update signal_wake_up and friends to make that unnecessary. Peter's task freezer sets frozen tasks to a new state TASK_FROZEN and then it stores them by calling "wake_up_state(t, TASK_FROZEN)" relying on the fact that all stopped states except the special stop states can tolerate spurious wake up and recover their state. The state of stopped and traced tasked is changed to be stored in task->jobctl as well as in task->__state. This makes it possible for the freezer to recover tasks in these special states, as well as serving as a general cleanup. With a little more work in that direction I believe TASK_STOPPED can learn to tolerate spurious wake ups and become an ordinary stop state. The TASK_TRACED state has to remain a special state as the registers for a process are only reliably available when the process is stopped in the scheduler. Fundamentally ptrace needs acess to the saved register values of a task. There are bunch of semi-random ptrace related cleanups that were found while looking at these issues. One cleanup that deserves to be called out is from commit57b6de08b5
("ptrace: Admit ptrace_stop can generate spuriuos SIGTRAPs"). This makes a change that is technically user space visible, in the handling of what happens to a tracee when a tracer dies unexpectedly. According to our testing and our understanding of userspace nothing cares that spurious SIGTRAPs can be generated in that case" * tag 'ptrace_stop-cleanup-for-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace: sched,signal,ptrace: Rework TASK_TRACED, TASK_STOPPED state ptrace: Always take siglock in ptrace_resume ptrace: Don't change __state ptrace: Admit ptrace_stop can generate spuriuos SIGTRAPs ptrace: Document that wait_task_inactive can't fail ptrace: Reimplement PTRACE_KILL by always sending SIGKILL signal: Use lockdep_assert_held instead of assert_spin_locked ptrace: Remove arch_ptrace_attach ptrace/xtensa: Replace PT_SINGLESTEP with TIF_SINGLESTEP ptrace/um: Replace PT_DTRACE with TIF_SINGLESTEP signal: Replace __group_send_sig_info with send_signal_locked signal: Rename send_signal send_signal_locked
515 lines
12 KiB
C
515 lines
12 KiB
C
/*
|
|
* arch/xtensa/kernel/signal.c
|
|
*
|
|
* Default platform functions.
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
*
|
|
* Copyright (C) 2005, 2006 Tensilica Inc.
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
* 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
|
|
*
|
|
* Chris Zankel <chris@zankel.net>
|
|
* Joe Taylor <joe@tensilica.com>
|
|
*/
|
|
|
|
#include <linux/signal.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/personality.h>
|
|
#include <linux/resume_user_mode.h>
|
|
#include <linux/sched/task_stack.h>
|
|
|
|
#include <asm/ucontext.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/coprocessor.h>
|
|
#include <asm/unistd.h>
|
|
|
|
extern struct task_struct *coproc_owners[];
|
|
|
|
struct rt_sigframe
|
|
{
|
|
struct siginfo info;
|
|
struct ucontext uc;
|
|
struct {
|
|
xtregs_opt_t opt;
|
|
xtregs_user_t user;
|
|
#if XTENSA_HAVE_COPROCESSORS
|
|
xtregs_coprocessor_t cp;
|
|
#endif
|
|
} xtregs;
|
|
unsigned char retcode[6];
|
|
unsigned int window[4];
|
|
};
|
|
|
|
#if defined(USER_SUPPORT_WINDOWED)
|
|
/*
|
|
* Flush register windows stored in pt_regs to stack.
|
|
* Returns 1 for errors.
|
|
*/
|
|
|
|
static int
|
|
flush_window_regs_user(struct pt_regs *regs)
|
|
{
|
|
const unsigned long ws = regs->windowstart;
|
|
const unsigned long wb = regs->windowbase;
|
|
unsigned long sp = 0;
|
|
unsigned long wm;
|
|
int err = 1;
|
|
int base;
|
|
|
|
/* Return if no other frames. */
|
|
|
|
if (regs->wmask == 1)
|
|
return 0;
|
|
|
|
/* Rotate windowmask and skip empty frames. */
|
|
|
|
wm = (ws >> wb) | (ws << (XCHAL_NUM_AREGS / 4 - wb));
|
|
base = (XCHAL_NUM_AREGS / 4) - (regs->wmask >> 4);
|
|
|
|
/* For call8 or call12 frames, we need the previous stack pointer. */
|
|
|
|
if ((regs->wmask & 2) == 0)
|
|
if (__get_user(sp, (int*)(regs->areg[base * 4 + 1] - 12)))
|
|
goto errout;
|
|
|
|
/* Spill frames to stack. */
|
|
|
|
while (base < XCHAL_NUM_AREGS / 4) {
|
|
|
|
int m = (wm >> base);
|
|
int inc = 0;
|
|
|
|
/* Save registers a4..a7 (call8) or a4...a11 (call12) */
|
|
|
|
if (m & 2) { /* call4 */
|
|
inc = 1;
|
|
|
|
} else if (m & 4) { /* call8 */
|
|
if (copy_to_user(&SPILL_SLOT_CALL8(sp, 4),
|
|
®s->areg[(base + 1) * 4], 16))
|
|
goto errout;
|
|
inc = 2;
|
|
|
|
} else if (m & 8) { /* call12 */
|
|
if (copy_to_user(&SPILL_SLOT_CALL12(sp, 4),
|
|
®s->areg[(base + 1) * 4], 32))
|
|
goto errout;
|
|
inc = 3;
|
|
}
|
|
|
|
/* Save current frame a0..a3 under next SP */
|
|
|
|
sp = regs->areg[((base + inc) * 4 + 1) % XCHAL_NUM_AREGS];
|
|
if (copy_to_user(&SPILL_SLOT(sp, 0), ®s->areg[base * 4], 16))
|
|
goto errout;
|
|
|
|
/* Get current stack pointer for next loop iteration. */
|
|
|
|
sp = regs->areg[base * 4 + 1];
|
|
base += inc;
|
|
}
|
|
|
|
regs->wmask = 1;
|
|
regs->windowstart = 1 << wb;
|
|
|
|
return 0;
|
|
|
|
errout:
|
|
return err;
|
|
}
|
|
#else
|
|
static int
|
|
flush_window_regs_user(struct pt_regs *regs)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Note: We don't copy double exception 'regs', we have to finish double exc.
|
|
* first before we return to signal handler! This dbl.exc.handler might cause
|
|
* another double exception, but I think we are fine as the situation is the
|
|
* same as if we had returned to the signal handerl and got an interrupt
|
|
* immediately...
|
|
*/
|
|
|
|
static int
|
|
setup_sigcontext(struct rt_sigframe __user *frame, struct pt_regs *regs)
|
|
{
|
|
struct sigcontext __user *sc = &frame->uc.uc_mcontext;
|
|
struct thread_info *ti = current_thread_info();
|
|
int err = 0;
|
|
|
|
#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x)
|
|
COPY(pc);
|
|
COPY(ps);
|
|
COPY(lbeg);
|
|
COPY(lend);
|
|
COPY(lcount);
|
|
COPY(sar);
|
|
#undef COPY
|
|
|
|
err |= flush_window_regs_user(regs);
|
|
err |= __copy_to_user (sc->sc_a, regs->areg, 16 * 4);
|
|
err |= __put_user(0, &sc->sc_xtregs);
|
|
|
|
if (err)
|
|
return err;
|
|
|
|
#if XTENSA_HAVE_COPROCESSORS
|
|
coprocessor_flush_release_all(ti);
|
|
err |= __copy_to_user(&frame->xtregs.cp, &ti->xtregs_cp,
|
|
sizeof (frame->xtregs.cp));
|
|
#endif
|
|
err |= __copy_to_user(&frame->xtregs.opt, ®s->xtregs_opt,
|
|
sizeof (xtregs_opt_t));
|
|
err |= __copy_to_user(&frame->xtregs.user, &ti->xtregs_user,
|
|
sizeof (xtregs_user_t));
|
|
|
|
err |= __put_user(err ? NULL : &frame->xtregs, &sc->sc_xtregs);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
restore_sigcontext(struct pt_regs *regs, struct rt_sigframe __user *frame)
|
|
{
|
|
struct sigcontext __user *sc = &frame->uc.uc_mcontext;
|
|
struct thread_info *ti = current_thread_info();
|
|
unsigned int err = 0;
|
|
unsigned long ps;
|
|
|
|
#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x)
|
|
COPY(pc);
|
|
COPY(lbeg);
|
|
COPY(lend);
|
|
COPY(lcount);
|
|
COPY(sar);
|
|
#undef COPY
|
|
|
|
/* All registers were flushed to stack. Start with a pristine frame. */
|
|
|
|
regs->wmask = 1;
|
|
regs->windowbase = 0;
|
|
regs->windowstart = 1;
|
|
|
|
regs->syscall = NO_SYSCALL; /* disable syscall checks */
|
|
|
|
/* For PS, restore only PS.CALLINC.
|
|
* Assume that all other bits are either the same as for the signal
|
|
* handler, or the user mode value doesn't matter (e.g. PS.OWB).
|
|
*/
|
|
err |= __get_user(ps, &sc->sc_ps);
|
|
regs->ps = (regs->ps & ~PS_CALLINC_MASK) | (ps & PS_CALLINC_MASK);
|
|
|
|
/* Additional corruption checks */
|
|
|
|
if ((regs->lcount > 0)
|
|
&& ((regs->lbeg > TASK_SIZE) || (regs->lend > TASK_SIZE)) )
|
|
err = 1;
|
|
|
|
err |= __copy_from_user(regs->areg, sc->sc_a, 16 * 4);
|
|
|
|
if (err)
|
|
return err;
|
|
|
|
/* The signal handler may have used coprocessors in which
|
|
* case they are still enabled. We disable them to force a
|
|
* reloading of the original task's CP state by the lazy
|
|
* context-switching mechanisms of CP exception handling.
|
|
* Also, we essentially discard any coprocessor state that the
|
|
* signal handler created. */
|
|
|
|
#if XTENSA_HAVE_COPROCESSORS
|
|
coprocessor_release_all(ti);
|
|
err |= __copy_from_user(&ti->xtregs_cp, &frame->xtregs.cp,
|
|
sizeof (frame->xtregs.cp));
|
|
#endif
|
|
err |= __copy_from_user(&ti->xtregs_user, &frame->xtregs.user,
|
|
sizeof (xtregs_user_t));
|
|
err |= __copy_from_user(®s->xtregs_opt, &frame->xtregs.opt,
|
|
sizeof (xtregs_opt_t));
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
* Do a signal return; undo the signal stack.
|
|
*/
|
|
|
|
asmlinkage long xtensa_rt_sigreturn(void)
|
|
{
|
|
struct pt_regs *regs = current_pt_regs();
|
|
struct rt_sigframe __user *frame;
|
|
sigset_t set;
|
|
int ret;
|
|
|
|
/* Always make any pending restarted system calls return -EINTR */
|
|
current->restart_block.fn = do_no_restart_syscall;
|
|
|
|
if (regs->depc > 64)
|
|
panic("rt_sigreturn in double exception!\n");
|
|
|
|
frame = (struct rt_sigframe __user *) regs->areg[1];
|
|
|
|
if (!access_ok(frame, sizeof(*frame)))
|
|
goto badframe;
|
|
|
|
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
|
|
goto badframe;
|
|
|
|
set_current_blocked(&set);
|
|
|
|
if (restore_sigcontext(regs, frame))
|
|
goto badframe;
|
|
|
|
ret = regs->areg[2];
|
|
|
|
if (restore_altstack(&frame->uc.uc_stack))
|
|
goto badframe;
|
|
|
|
return ret;
|
|
|
|
badframe:
|
|
force_sig(SIGSEGV);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Set up a signal frame.
|
|
*/
|
|
|
|
static int
|
|
gen_return_code(unsigned char *codemem)
|
|
{
|
|
int err = 0;
|
|
|
|
/*
|
|
* The 12-bit immediate is really split up within the 24-bit MOVI
|
|
* instruction. As long as the above system call numbers fit within
|
|
* 8-bits, the following code works fine. See the Xtensa ISA for
|
|
* details.
|
|
*/
|
|
|
|
#if __NR_rt_sigreturn > 255
|
|
# error Generating the MOVI instruction below breaks!
|
|
#endif
|
|
|
|
#ifdef __XTENSA_EB__ /* Big Endian version */
|
|
/* Generate instruction: MOVI a2, __NR_rt_sigreturn */
|
|
err |= __put_user(0x22, &codemem[0]);
|
|
err |= __put_user(0x0a, &codemem[1]);
|
|
err |= __put_user(__NR_rt_sigreturn, &codemem[2]);
|
|
/* Generate instruction: SYSCALL */
|
|
err |= __put_user(0x00, &codemem[3]);
|
|
err |= __put_user(0x05, &codemem[4]);
|
|
err |= __put_user(0x00, &codemem[5]);
|
|
|
|
#elif defined __XTENSA_EL__ /* Little Endian version */
|
|
/* Generate instruction: MOVI a2, __NR_rt_sigreturn */
|
|
err |= __put_user(0x22, &codemem[0]);
|
|
err |= __put_user(0xa0, &codemem[1]);
|
|
err |= __put_user(__NR_rt_sigreturn, &codemem[2]);
|
|
/* Generate instruction: SYSCALL */
|
|
err |= __put_user(0x00, &codemem[3]);
|
|
err |= __put_user(0x50, &codemem[4]);
|
|
err |= __put_user(0x00, &codemem[5]);
|
|
#else
|
|
# error Must use compiler for Xtensa processors.
|
|
#endif
|
|
|
|
/* Flush generated code out of the data cache */
|
|
|
|
if (err == 0) {
|
|
__invalidate_icache_range((unsigned long)codemem, 6UL);
|
|
__flush_invalidate_dcache_range((unsigned long)codemem, 6UL);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static int setup_frame(struct ksignal *ksig, sigset_t *set,
|
|
struct pt_regs *regs)
|
|
{
|
|
struct rt_sigframe *frame;
|
|
int err = 0, sig = ksig->sig;
|
|
unsigned long sp, ra, tp, ps;
|
|
unsigned int base;
|
|
|
|
sp = regs->areg[1];
|
|
|
|
if ((ksig->ka.sa.sa_flags & SA_ONSTACK) != 0 && sas_ss_flags(sp) == 0) {
|
|
sp = current->sas_ss_sp + current->sas_ss_size;
|
|
}
|
|
|
|
frame = (void *)((sp - sizeof(*frame)) & -16ul);
|
|
|
|
if (regs->depc > 64)
|
|
panic ("Double exception sys_sigreturn\n");
|
|
|
|
if (!access_ok(frame, sizeof(*frame))) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
|
|
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
|
|
}
|
|
|
|
/* Create the user context. */
|
|
|
|
err |= __put_user(0, &frame->uc.uc_flags);
|
|
err |= __put_user(0, &frame->uc.uc_link);
|
|
err |= __save_altstack(&frame->uc.uc_stack, regs->areg[1]);
|
|
err |= setup_sigcontext(frame, regs);
|
|
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
|
|
|
|
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
|
|
ra = (unsigned long)ksig->ka.sa.sa_restorer;
|
|
} else {
|
|
|
|
/* Create sys_rt_sigreturn syscall in stack frame */
|
|
|
|
err |= gen_return_code(frame->retcode);
|
|
|
|
if (err) {
|
|
return -EFAULT;
|
|
}
|
|
ra = (unsigned long) frame->retcode;
|
|
}
|
|
|
|
/*
|
|
* Create signal handler execution context.
|
|
* Return context not modified until this point.
|
|
*/
|
|
|
|
/* Set up registers for signal handler; preserve the threadptr */
|
|
tp = regs->threadptr;
|
|
ps = regs->ps;
|
|
start_thread(regs, (unsigned long) ksig->ka.sa.sa_handler,
|
|
(unsigned long) frame);
|
|
|
|
/* Set up a stack frame for a call4 if userspace uses windowed ABI */
|
|
if (ps & PS_WOE_MASK) {
|
|
base = 4;
|
|
regs->areg[base] =
|
|
(((unsigned long) ra) & 0x3fffffff) | 0x40000000;
|
|
ps = (ps & ~(PS_CALLINC_MASK | PS_OWB_MASK)) |
|
|
(1 << PS_CALLINC_SHIFT);
|
|
} else {
|
|
base = 0;
|
|
regs->areg[base] = (unsigned long) ra;
|
|
}
|
|
regs->areg[base + 2] = (unsigned long) sig;
|
|
regs->areg[base + 3] = (unsigned long) &frame->info;
|
|
regs->areg[base + 4] = (unsigned long) &frame->uc;
|
|
regs->threadptr = tp;
|
|
regs->ps = ps;
|
|
|
|
pr_debug("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08lx\n",
|
|
current->comm, current->pid, sig, frame, regs->pc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Note that 'init' is a special process: it doesn't get signals it doesn't
|
|
* want to handle. Thus you cannot kill init even with a SIGKILL even by
|
|
* mistake.
|
|
*
|
|
* Note that we go through the signals twice: once to check the signals that
|
|
* the kernel can handle, and then we build all the user-level signal handling
|
|
* stack-frames in one go after that.
|
|
*/
|
|
static void do_signal(struct pt_regs *regs)
|
|
{
|
|
struct ksignal ksig;
|
|
|
|
task_pt_regs(current)->icountlevel = 0;
|
|
|
|
if (get_signal(&ksig)) {
|
|
int ret;
|
|
|
|
/* Are we from a system call? */
|
|
|
|
if (regs->syscall != NO_SYSCALL) {
|
|
|
|
/* If so, check system call restarting.. */
|
|
|
|
switch (regs->areg[2]) {
|
|
case -ERESTARTNOHAND:
|
|
case -ERESTART_RESTARTBLOCK:
|
|
regs->areg[2] = -EINTR;
|
|
break;
|
|
|
|
case -ERESTARTSYS:
|
|
if (!(ksig.ka.sa.sa_flags & SA_RESTART)) {
|
|
regs->areg[2] = -EINTR;
|
|
break;
|
|
}
|
|
fallthrough;
|
|
case -ERESTARTNOINTR:
|
|
regs->areg[2] = regs->syscall;
|
|
regs->pc -= 3;
|
|
break;
|
|
|
|
default:
|
|
/* nothing to do */
|
|
if (regs->areg[2] != 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Whee! Actually deliver the signal. */
|
|
/* Set up the stack frame */
|
|
ret = setup_frame(&ksig, sigmask_to_save(), regs);
|
|
signal_setup_done(ret, &ksig, 0);
|
|
if (test_thread_flag(TIF_SINGLESTEP))
|
|
task_pt_regs(current)->icountlevel = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Did we come from a system call? */
|
|
if (regs->syscall != NO_SYSCALL) {
|
|
/* Restart the system call - no handlers present */
|
|
switch (regs->areg[2]) {
|
|
case -ERESTARTNOHAND:
|
|
case -ERESTARTSYS:
|
|
case -ERESTARTNOINTR:
|
|
regs->areg[2] = regs->syscall;
|
|
regs->pc -= 3;
|
|
break;
|
|
case -ERESTART_RESTARTBLOCK:
|
|
regs->areg[2] = __NR_restart_syscall;
|
|
regs->pc -= 3;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If there's no signal to deliver, we just restore the saved mask. */
|
|
restore_saved_sigmask();
|
|
|
|
if (test_thread_flag(TIF_SINGLESTEP))
|
|
task_pt_regs(current)->icountlevel = 1;
|
|
return;
|
|
}
|
|
|
|
void do_notify_resume(struct pt_regs *regs)
|
|
{
|
|
if (test_thread_flag(TIF_SIGPENDING) ||
|
|
test_thread_flag(TIF_NOTIFY_SIGNAL))
|
|
do_signal(regs);
|
|
|
|
if (test_thread_flag(TIF_NOTIFY_RESUME))
|
|
resume_user_mode_work(regs);
|
|
}
|