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/signal.c | |
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/signal.c')
-rw-r--r-- | kernel/signal.c | 24 |
1 files changed, 18 insertions, 6 deletions
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 | } |