Commit 7bb44ade authored by Roland McGrath's avatar Roland McGrath Committed by Linus Torvalds

recalc_sigpending_tsk fixes

Steve Hawkes discovered a problem where recalc_sigpending_tsk was called in
do_sigaction but no signal_wake_up call was made, preventing later signals
from waking up blocked threads with TIF_SIGPENDING already set.

In fact, the few other calls to recalc_sigpending_tsk outside the signals
code are also subject to this problem in other race conditions.

This change makes recalc_sigpending_tsk private to the signals code.  It
changes the outside calls, as well as do_sigaction, to use the new
recalc_sigpending_and_wake instead.
Signed-off-by: default avatarRoland McGrath <roland@redhat.com>
Cc: <Steve.Hawkes@motorola.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 3c6df2a9
...@@ -1616,11 +1616,13 @@ static inline int lock_need_resched(spinlock_t *lock) ...@@ -1616,11 +1616,13 @@ static inline int lock_need_resched(spinlock_t *lock)
return 0; return 0;
} }
/* Reevaluate whether the task has signals pending delivery. /*
This is required every time the blocked sigset_t changes. * Reevaluate whether the task has signals pending delivery.
callers must hold sighand->siglock. */ * Wake the task if so.
* This is required every time the blocked sigset_t changes.
extern FASTCALL(void recalc_sigpending_tsk(struct task_struct *t)); * callers must hold sighand->siglock.
*/
extern void recalc_sigpending_and_wake(struct task_struct *t);
extern void recalc_sigpending(void); extern void recalc_sigpending(void);
extern void signal_wake_up(struct task_struct *t, int resume_stopped); extern void signal_wake_up(struct task_struct *t, int resume_stopped);
......
...@@ -762,11 +762,8 @@ static void exit_notify(struct task_struct *tsk) ...@@ -762,11 +762,8 @@ static void exit_notify(struct task_struct *tsk)
read_lock(&tasklist_lock); read_lock(&tasklist_lock);
spin_lock_irq(&tsk->sighand->siglock); spin_lock_irq(&tsk->sighand->siglock);
for (t = next_thread(tsk); t != tsk; t = next_thread(t)) for (t = next_thread(tsk); t != tsk; t = next_thread(t))
if (!signal_pending(t) && !(t->flags & PF_EXITING)) { if (!signal_pending(t) && !(t->flags & PF_EXITING))
recalc_sigpending_tsk(t); recalc_sigpending_and_wake(t);
if (signal_pending(t))
signal_wake_up(t, 0);
}
spin_unlock_irq(&tsk->sighand->siglock); spin_unlock_irq(&tsk->sighand->siglock);
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
} }
......
...@@ -101,7 +101,7 @@ static void cancel_freezing(struct task_struct *p) ...@@ -101,7 +101,7 @@ static void cancel_freezing(struct task_struct *p)
pr_debug(" clean up: %s\n", p->comm); pr_debug(" clean up: %s\n", p->comm);
do_not_freeze(p); do_not_freeze(p);
spin_lock_irqsave(&p->sighand->siglock, flags); spin_lock_irqsave(&p->sighand->siglock, flags);
recalc_sigpending_tsk(p); recalc_sigpending_and_wake(p);
spin_unlock_irqrestore(&p->sighand->siglock, flags); spin_unlock_irqrestore(&p->sighand->siglock, flags);
} }
} }
......
...@@ -96,15 +96,27 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) ...@@ -96,15 +96,27 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked)
#define PENDING(p,b) has_pending_signals(&(p)->signal, (b)) #define PENDING(p,b) has_pending_signals(&(p)->signal, (b))
fastcall void recalc_sigpending_tsk(struct task_struct *t) static int recalc_sigpending_tsk(struct task_struct *t)
{ {
if (t->signal->group_stop_count > 0 || if (t->signal->group_stop_count > 0 ||
(freezing(t)) || (freezing(t)) ||
PENDING(&t->pending, &t->blocked) || PENDING(&t->pending, &t->blocked) ||
PENDING(&t->signal->shared_pending, &t->blocked)) PENDING(&t->signal->shared_pending, &t->blocked)) {
set_tsk_thread_flag(t, TIF_SIGPENDING); set_tsk_thread_flag(t, TIF_SIGPENDING);
else return 1;
}
clear_tsk_thread_flag(t, TIF_SIGPENDING); clear_tsk_thread_flag(t, TIF_SIGPENDING);
return 0;
}
/*
* After recalculating TIF_SIGPENDING, we need to make sure the task wakes up.
* This is superfluous when called on current, the wakeup is a harmless no-op.
*/
void recalc_sigpending_and_wake(struct task_struct *t)
{
if (recalc_sigpending_tsk(t))
signal_wake_up(t, 0);
} }
void recalc_sigpending(void) void recalc_sigpending(void)
...@@ -744,7 +756,7 @@ force_sig_info(int sig, struct siginfo *info, struct task_struct *t) ...@@ -744,7 +756,7 @@ force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
action->sa.sa_handler = SIG_DFL; action->sa.sa_handler = SIG_DFL;
if (blocked) { if (blocked) {
sigdelset(&t->blocked, sig); sigdelset(&t->blocked, sig);
recalc_sigpending_tsk(t); recalc_sigpending_and_wake(t);
} }
} }
ret = specific_send_sig_info(sig, info, t); ret = specific_send_sig_info(sig, info, t);
...@@ -2273,7 +2285,7 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact) ...@@ -2273,7 +2285,7 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
rm_from_queue_full(&mask, &t->signal->shared_pending); rm_from_queue_full(&mask, &t->signal->shared_pending);
do { do {
rm_from_queue_full(&mask, &t->pending); rm_from_queue_full(&mask, &t->pending);
recalc_sigpending_tsk(t); recalc_sigpending_and_wake(t);
t = next_thread(t); t = next_thread(t);
} while (t != current); } while (t != current);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment