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 | } |