diff options
| author | Roland McGrath <roland@redhat.com> | 2007-05-23 16:57:44 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-23 23:14:12 -0400 |
| commit | 7bb44adef39ad3bda2be40bb34686bc56bd563a5 (patch) | |
| tree | 1e9eecbd03877c7ac8b00a8cb1d4ae54ebec3bb1 /kernel | |
| parent | 3c6df2a9177c010bf811c361149069e4bfd9bb11 (diff) | |
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: Roland McGrath <roland@redhat.com>
Cc: <Steve.Hawkes@motorola.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/exit.c | 7 | ||||
| -rw-r--r-- | kernel/power/process.c | 2 | ||||
| -rw-r--r-- | kernel/signal.c | 24 |
3 files changed, 21 insertions, 12 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index c6d14b8008dd..5b888c24e43e 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
| @@ -762,11 +762,8 @@ static void exit_notify(struct task_struct *tsk) | |||
| 762 | read_lock(&tasklist_lock); | 762 | read_lock(&tasklist_lock); |
| 763 | spin_lock_irq(&tsk->sighand->siglock); | 763 | spin_lock_irq(&tsk->sighand->siglock); |
| 764 | for (t = next_thread(tsk); t != tsk; t = next_thread(t)) | 764 | for (t = next_thread(tsk); t != tsk; t = next_thread(t)) |
| 765 | if (!signal_pending(t) && !(t->flags & PF_EXITING)) { | 765 | if (!signal_pending(t) && !(t->flags & PF_EXITING)) |
| 766 | recalc_sigpending_tsk(t); | 766 | recalc_sigpending_and_wake(t); |
| 767 | if (signal_pending(t)) | ||
| 768 | signal_wake_up(t, 0); | ||
| 769 | } | ||
| 770 | spin_unlock_irq(&tsk->sighand->siglock); | 767 | spin_unlock_irq(&tsk->sighand->siglock); |
| 771 | read_unlock(&tasklist_lock); | 768 | read_unlock(&tasklist_lock); |
| 772 | } | 769 | } |
diff --git a/kernel/power/process.c b/kernel/power/process.c index d31d638ab4c0..e0233d8422b9 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c | |||
| @@ -101,7 +101,7 @@ static void cancel_freezing(struct task_struct *p) | |||
| 101 | pr_debug(" clean up: %s\n", p->comm); | 101 | pr_debug(" clean up: %s\n", p->comm); |
| 102 | do_not_freeze(p); | 102 | do_not_freeze(p); |
| 103 | spin_lock_irqsave(&p->sighand->siglock, flags); | 103 | spin_lock_irqsave(&p->sighand->siglock, flags); |
| 104 | recalc_sigpending_tsk(p); | 104 | recalc_sigpending_and_wake(p); |
| 105 | spin_unlock_irqrestore(&p->sighand->siglock, flags); | 105 | spin_unlock_irqrestore(&p->sighand->siglock, flags); |
| 106 | } | 106 | } |
| 107 | } | 107 | } |
diff --git a/kernel/signal.c b/kernel/signal.c index 364fc95bf97c..acdfc0549c6f 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
| @@ -96,15 +96,27 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) | |||
| 96 | 96 | ||
| 97 | #define PENDING(p,b) has_pending_signals(&(p)->signal, (b)) | 97 | #define PENDING(p,b) has_pending_signals(&(p)->signal, (b)) |
| 98 | 98 | ||
| 99 | fastcall void recalc_sigpending_tsk(struct task_struct *t) | 99 | static int recalc_sigpending_tsk(struct task_struct *t) |
| 100 | { | 100 | { |
| 101 | if (t->signal->group_stop_count > 0 || | 101 | if (t->signal->group_stop_count > 0 || |
| 102 | (freezing(t)) || | 102 | (freezing(t)) || |
| 103 | PENDING(&t->pending, &t->blocked) || | 103 | PENDING(&t->pending, &t->blocked) || |
| 104 | PENDING(&t->signal->shared_pending, &t->blocked)) | 104 | PENDING(&t->signal->shared_pending, &t->blocked)) { |
| 105 | set_tsk_thread_flag(t, TIF_SIGPENDING); | 105 | set_tsk_thread_flag(t, TIF_SIGPENDING); |
| 106 | else | 106 | return 1; |
| 107 | clear_tsk_thread_flag(t, TIF_SIGPENDING); | 107 | } |
| 108 | clear_tsk_thread_flag(t, TIF_SIGPENDING); | ||
| 109 | return 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | /* | ||
| 113 | * After recalculating TIF_SIGPENDING, we need to make sure the task wakes up. | ||
| 114 | * This is superfluous when called on current, the wakeup is a harmless no-op. | ||
| 115 | */ | ||
| 116 | void recalc_sigpending_and_wake(struct task_struct *t) | ||
| 117 | { | ||
| 118 | if (recalc_sigpending_tsk(t)) | ||
| 119 | signal_wake_up(t, 0); | ||
| 108 | } | 120 | } |
| 109 | 121 | ||
| 110 | void recalc_sigpending(void) | 122 | void recalc_sigpending(void) |
| @@ -744,7 +756,7 @@ force_sig_info(int sig, struct siginfo *info, struct task_struct *t) | |||
| 744 | action->sa.sa_handler = SIG_DFL; | 756 | action->sa.sa_handler = SIG_DFL; |
| 745 | if (blocked) { | 757 | if (blocked) { |
| 746 | sigdelset(&t->blocked, sig); | 758 | sigdelset(&t->blocked, sig); |
| 747 | recalc_sigpending_tsk(t); | 759 | recalc_sigpending_and_wake(t); |
| 748 | } | 760 | } |
| 749 | } | 761 | } |
| 750 | ret = specific_send_sig_info(sig, info, t); | 762 | 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 | rm_from_queue_full(&mask, &t->signal->shared_pending); | 2285 | rm_from_queue_full(&mask, &t->signal->shared_pending); |
| 2274 | do { | 2286 | do { |
| 2275 | rm_from_queue_full(&mask, &t->pending); | 2287 | rm_from_queue_full(&mask, &t->pending); |
| 2276 | recalc_sigpending_tsk(t); | 2288 | recalc_sigpending_and_wake(t); |
| 2277 | t = next_thread(t); | 2289 | t = next_thread(t); |
| 2278 | } while (t != current); | 2290 | } while (t != current); |
| 2279 | } | 2291 | } |
