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 | |
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>
-rw-r--r-- | include/linux/sched.h | 12 | ||||
-rw-r--r-- | kernel/exit.c | 7 | ||||
-rw-r--r-- | kernel/power/process.c | 2 | ||||
-rw-r--r-- | kernel/signal.c | 24 |
4 files changed, 28 insertions, 17 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index 870b75e348ab..d58e74b98367 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -1616,11 +1616,13 @@ static inline int lock_need_resched(spinlock_t *lock) | |||
1616 | return 0; | 1616 | return 0; |
1617 | } | 1617 | } |
1618 | 1618 | ||
1619 | /* Reevaluate whether the task has signals pending delivery. | 1619 | /* |
1620 | This is required every time the blocked sigset_t changes. | 1620 | * Reevaluate whether the task has signals pending delivery. |
1621 | callers must hold sighand->siglock. */ | 1621 | * Wake the task if so. |
1622 | 1622 | * This is required every time the blocked sigset_t changes. | |
1623 | extern FASTCALL(void recalc_sigpending_tsk(struct task_struct *t)); | 1623 | * callers must hold sighand->siglock. |
1624 | */ | ||
1625 | extern void recalc_sigpending_and_wake(struct task_struct *t); | ||
1624 | extern void recalc_sigpending(void); | 1626 | extern void recalc_sigpending(void); |
1625 | 1627 | ||
1626 | extern void signal_wake_up(struct task_struct *t, int resume_stopped); | 1628 | extern void signal_wake_up(struct task_struct *t, int resume_stopped); |
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 | } |