|
@@ -32,8 +32,8 @@
|
|
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
|
|
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
|
|
|
|
|
|
asmlinkage void ret_from_sys_call(void);
|
|
asmlinkage void ret_from_sys_call(void);
|
|
-static int do_signal(sigset_t *, struct pt_regs *, struct switch_stack *,
|
|
|
|
- unsigned long, unsigned long);
|
|
|
|
|
|
+static void do_signal(struct pt_regs *, struct switch_stack *,
|
|
|
|
+ unsigned long, unsigned long);
|
|
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -146,11 +146,9 @@ sys_rt_sigaction(int sig, const struct sigaction __user *act,
|
|
asmlinkage int
|
|
asmlinkage int
|
|
do_sigsuspend(old_sigset_t mask, struct pt_regs *regs, struct switch_stack *sw)
|
|
do_sigsuspend(old_sigset_t mask, struct pt_regs *regs, struct switch_stack *sw)
|
|
{
|
|
{
|
|
- sigset_t oldset;
|
|
|
|
-
|
|
|
|
mask &= _BLOCKABLE;
|
|
mask &= _BLOCKABLE;
|
|
spin_lock_irq(¤t->sighand->siglock);
|
|
spin_lock_irq(¤t->sighand->siglock);
|
|
- oldset = current->blocked;
|
|
|
|
|
|
+ current->saved_sigmask = current->blocked;
|
|
siginitset(¤t->blocked, mask);
|
|
siginitset(¤t->blocked, mask);
|
|
recalc_sigpending();
|
|
recalc_sigpending();
|
|
spin_unlock_irq(¤t->sighand->siglock);
|
|
spin_unlock_irq(¤t->sighand->siglock);
|
|
@@ -160,19 +158,17 @@ do_sigsuspend(old_sigset_t mask, struct pt_regs *regs, struct switch_stack *sw)
|
|
regs->r0 = EINTR;
|
|
regs->r0 = EINTR;
|
|
regs->r19 = 1;
|
|
regs->r19 = 1;
|
|
|
|
|
|
- while (1) {
|
|
|
|
- current->state = TASK_INTERRUPTIBLE;
|
|
|
|
- schedule();
|
|
|
|
- if (do_signal(&oldset, regs, sw, 0, 0))
|
|
|
|
- return -EINTR;
|
|
|
|
- }
|
|
|
|
|
|
+ current->state = TASK_INTERRUPTIBLE;
|
|
|
|
+ schedule();
|
|
|
|
+ set_thread_flag(TIF_RESTORE_SIGMASK);
|
|
|
|
+ return -ERESTARTNOHAND;
|
|
}
|
|
}
|
|
|
|
|
|
asmlinkage int
|
|
asmlinkage int
|
|
do_rt_sigsuspend(sigset_t __user *uset, size_t sigsetsize,
|
|
do_rt_sigsuspend(sigset_t __user *uset, size_t sigsetsize,
|
|
struct pt_regs *regs, struct switch_stack *sw)
|
|
struct pt_regs *regs, struct switch_stack *sw)
|
|
{
|
|
{
|
|
- sigset_t oldset, set;
|
|
|
|
|
|
+ sigset_t set;
|
|
|
|
|
|
/* XXX: Don't preclude handling different sized sigset_t's. */
|
|
/* XXX: Don't preclude handling different sized sigset_t's. */
|
|
if (sigsetsize != sizeof(sigset_t))
|
|
if (sigsetsize != sizeof(sigset_t))
|
|
@@ -182,7 +178,7 @@ do_rt_sigsuspend(sigset_t __user *uset, size_t sigsetsize,
|
|
|
|
|
|
sigdelsetmask(&set, ~_BLOCKABLE);
|
|
sigdelsetmask(&set, ~_BLOCKABLE);
|
|
spin_lock_irq(¤t->sighand->siglock);
|
|
spin_lock_irq(¤t->sighand->siglock);
|
|
- oldset = current->blocked;
|
|
|
|
|
|
+ current->saved_sigmask = current->blocked;
|
|
current->blocked = set;
|
|
current->blocked = set;
|
|
recalc_sigpending();
|
|
recalc_sigpending();
|
|
spin_unlock_irq(¤t->sighand->siglock);
|
|
spin_unlock_irq(¤t->sighand->siglock);
|
|
@@ -192,12 +188,10 @@ do_rt_sigsuspend(sigset_t __user *uset, size_t sigsetsize,
|
|
regs->r0 = EINTR;
|
|
regs->r0 = EINTR;
|
|
regs->r19 = 1;
|
|
regs->r19 = 1;
|
|
|
|
|
|
- while (1) {
|
|
|
|
- current->state = TASK_INTERRUPTIBLE;
|
|
|
|
- schedule();
|
|
|
|
- if (do_signal(&oldset, regs, sw, 0, 0))
|
|
|
|
- return -EINTR;
|
|
|
|
- }
|
|
|
|
|
|
+ current->state = TASK_INTERRUPTIBLE;
|
|
|
|
+ schedule();
|
|
|
|
+ set_thread_flag(TIF_RESTORE_SIGMASK);
|
|
|
|
+ return -ERESTARTNOHAND;
|
|
}
|
|
}
|
|
|
|
|
|
asmlinkage int
|
|
asmlinkage int
|
|
@@ -436,7 +430,7 @@ setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
-static void
|
|
|
|
|
|
+static int
|
|
setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
|
|
setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
|
|
struct pt_regs *regs, struct switch_stack * sw)
|
|
struct pt_regs *regs, struct switch_stack * sw)
|
|
{
|
|
{
|
|
@@ -481,13 +475,14 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
|
|
current->comm, current->pid, frame, regs->pc, regs->r26);
|
|
current->comm, current->pid, frame, regs->pc, regs->r26);
|
|
#endif
|
|
#endif
|
|
|
|
|
|
- return;
|
|
|
|
|
|
+ return 0;
|
|
|
|
|
|
give_sigsegv:
|
|
give_sigsegv:
|
|
force_sigsegv(sig, current);
|
|
force_sigsegv(sig, current);
|
|
|
|
+ return -EFAULT;
|
|
}
|
|
}
|
|
|
|
|
|
-static void
|
|
|
|
|
|
+static int
|
|
setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|
setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|
sigset_t *set, struct pt_regs *regs, struct switch_stack * sw)
|
|
sigset_t *set, struct pt_regs *regs, struct switch_stack * sw)
|
|
{
|
|
{
|
|
@@ -543,34 +538,38 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|
current->comm, current->pid, frame, regs->pc, regs->r26);
|
|
current->comm, current->pid, frame, regs->pc, regs->r26);
|
|
#endif
|
|
#endif
|
|
|
|
|
|
- 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 sig, struct k_sigaction *ka, siginfo_t *info,
|
|
handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|
sigset_t *oldset, struct pt_regs * regs, struct switch_stack *sw)
|
|
sigset_t *oldset, struct pt_regs * regs, struct switch_stack *sw)
|
|
{
|
|
{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
if (ka->sa.sa_flags & SA_SIGINFO)
|
|
if (ka->sa.sa_flags & SA_SIGINFO)
|
|
- setup_rt_frame(sig, ka, info, oldset, regs, sw);
|
|
|
|
|
|
+ ret = setup_rt_frame(sig, ka, info, oldset, regs, sw);
|
|
else
|
|
else
|
|
- setup_frame(sig, ka, oldset, regs, sw);
|
|
|
|
|
|
+ ret = setup_frame(sig, ka, oldset, regs, sw);
|
|
|
|
|
|
- if (ka->sa.sa_flags & SA_RESETHAND)
|
|
|
|
- ka->sa.sa_handler = SIG_DFL;
|
|
|
|
|
|
+ if (ret == 0) {
|
|
|
|
+ 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);
|
|
|
|
+ }
|
|
|
|
|
|
- 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;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void
|
|
static inline void
|
|
@@ -611,30 +610,42 @@ syscall_restart(unsigned long r0, unsigned long r19,
|
|
* restart. "r0" is also used as an indicator whether we can restart at
|
|
* restart. "r0" is also used as an indicator whether we can restart at
|
|
* all (if we get here from anything but a syscall return, it will be 0)
|
|
* all (if we get here from anything but a syscall return, it will be 0)
|
|
*/
|
|
*/
|
|
-static int
|
|
|
|
-do_signal(sigset_t *oldset, struct pt_regs * regs, struct switch_stack * sw,
|
|
|
|
|
|
+static void
|
|
|
|
+do_signal(struct pt_regs * regs, struct switch_stack * sw,
|
|
unsigned long r0, unsigned long r19)
|
|
unsigned long r0, unsigned long r19)
|
|
{
|
|
{
|
|
siginfo_t info;
|
|
siginfo_t info;
|
|
int signr;
|
|
int signr;
|
|
unsigned long single_stepping = ptrace_cancel_bpt(current);
|
|
unsigned long single_stepping = ptrace_cancel_bpt(current);
|
|
struct k_sigaction ka;
|
|
struct k_sigaction ka;
|
|
|
|
+ sigset_t *oldset;
|
|
|
|
|
|
- if (!oldset)
|
|
|
|
|
|
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
|
|
|
|
+ oldset = ¤t->saved_sigmask;
|
|
|
|
+ else
|
|
oldset = ¤t->blocked;
|
|
oldset = ¤t->blocked;
|
|
|
|
|
|
/* This lets the debugger run, ... */
|
|
/* This lets the debugger run, ... */
|
|
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
|
|
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
|
|
|
|
+
|
|
/* ... so re-check the single stepping. */
|
|
/* ... so re-check the single stepping. */
|
|
single_stepping |= ptrace_cancel_bpt(current);
|
|
single_stepping |= ptrace_cancel_bpt(current);
|
|
|
|
|
|
if (signr > 0) {
|
|
if (signr > 0) {
|
|
/* Whee! Actually deliver the signal. */
|
|
/* Whee! Actually deliver the signal. */
|
|
- if (r0) syscall_restart(r0, r19, regs, &ka);
|
|
|
|
- handle_signal(signr, &ka, &info, oldset, regs, sw);
|
|
|
|
|
|
+ if (r0)
|
|
|
|
+ syscall_restart(r0, r19, regs, &ka);
|
|
|
|
+ if (handle_signal(signr, &ka, &info, oldset, regs, sw) == 0) {
|
|
|
|
+ /* A signal was successfully delivered, and the
|
|
|
|
+ saved sigmask was stored on the signal frame,
|
|
|
|
+ and will be restored by sigreturn. So we can
|
|
|
|
+ simply clear the restore sigmask flag. */
|
|
|
|
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
|
|
|
|
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
|
|
|
|
+ }
|
|
if (single_stepping)
|
|
if (single_stepping)
|
|
ptrace_set_bpt(current); /* re-set bpt */
|
|
ptrace_set_bpt(current); /* re-set bpt */
|
|
- return 1;
|
|
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
|
|
if (r0) {
|
|
if (r0) {
|
|
@@ -654,17 +665,22 @@ do_signal(sigset_t *oldset, struct pt_regs * regs, struct switch_stack * sw,
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /* If there's no signal to deliver, we just restore the saved mask. */
|
|
|
|
+ if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
|
|
|
|
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
|
|
|
|
+ sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL);
|
|
|
|
+ }
|
|
|
|
+
|
|
if (single_stepping)
|
|
if (single_stepping)
|
|
ptrace_set_bpt(current); /* re-set breakpoint */
|
|
ptrace_set_bpt(current); /* re-set breakpoint */
|
|
-
|
|
|
|
- return 0;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
void
|
|
-do_notify_resume(sigset_t *oldset, struct pt_regs *regs,
|
|
|
|
- struct switch_stack *sw, unsigned long r0,
|
|
|
|
- unsigned long r19, unsigned long thread_info_flags)
|
|
|
|
|
|
+do_notify_resume(struct pt_regs *regs, struct switch_stack *sw,
|
|
|
|
+ unsigned long thread_info_flags,
|
|
|
|
+ unsigned long r0, unsigned long r19)
|
|
{
|
|
{
|
|
- if (thread_info_flags & _TIF_SIGPENDING)
|
|
|
|
- do_signal(oldset, regs, sw, r0, r19);
|
|
|
|
|
|
+ if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
|
|
|
|
+ do_signal(regs, sw, r0, r19);
|
|
}
|
|
}
|