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.
Unified · Split
| @@ -1616,11 +1616,13 @@ static inline int lock_need_resched(spinlock_t *lock) · 1615 unmodified lines | ||
| 1616 | 1616 | return 0; |
| 1617 | 1617 | } |
| 1618 | 1618 | |
| 1619 | −/* Reevaluate whether the task has signals pending delivery. | |
| 1620 | − This is required every time the blocked sigset_t changes. | |
| 1621 | − callers must hold sighand->siglock. */ | |
| 1622 | − | |
| 1623 | −extern FASTCALL(void recalc_sigpending_tsk(struct task_struct *t)); | |
| 1619 | +/* | |
| 1620 | + * Reevaluate whether the task has signals pending delivery. | |
| 1621 | + * Wake the task if so. | |
| 1622 | + * This is required every time the blocked sigset_t changes. | |
| 1623 | + * callers must hold sighand->siglock. | |
| 1624 | + */ | |
| 1625 | +extern void recalc_sigpending_and_wake(struct task_struct *t); | |
| 1624 | 1626 | extern void recalc_sigpending(void); |
| 1625 | 1627 | |
| 1626 | 1628 | extern void signal_wake_up(struct task_struct *t, int resume_stopped); |
| @@ -762,11 +762,8 @@ static void exit_notify(struct task_struct *tsk) · 761 unmodified lines | ||
| 762 | 762 | read_lock(&tasklist_lock); |
| 763 | 763 | spin_lock_irq(&tsk->sighand->siglock); |
| 764 | 764 | for (t = next_thread(tsk); t != tsk; t = next_thread(t)) |
| 765 | − if (!signal_pending(t) && !(t->flags & PF_EXITING)) { | |
| 766 | − recalc_sigpending_tsk(t); | |
| 767 | − if (signal_pending(t)) | |
| 768 | − signal_wake_up(t, 0); | |
| 769 | − } | |
| 765 | + if (!signal_pending(t) && !(t->flags & PF_EXITING)) | |
| 766 | + recalc_sigpending_and_wake(t); | |
| 770 | 767 | spin_unlock_irq(&tsk->sighand->siglock); |
| 771 | 768 | read_unlock(&tasklist_lock); |
| 772 | 769 | } |
| @@ -101,7 +101,7 @@ static void cancel_freezing(struct task_struct *p) · 100 unmodified lines | ||
| 101 | 101 | pr_debug(" clean up: %s\n", p->comm); |
| 102 | 102 | do_not_freeze(p); |
| 103 | 103 | spin_lock_irqsave(&p->sighand->siglock, flags); |
| 104 | − recalc_sigpending_tsk(p); | |
| 104 | + recalc_sigpending_and_wake(p); | |
| 105 | 105 | spin_unlock_irqrestore(&p->sighand->siglock, flags); |
| 106 | 106 | } |
| 107 | 107 | } |
| @@ -96,15 +96,27 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) · 95 unmodified lines | ||
| 96 | 96 | |
| 97 | 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 | 101 | if (t->signal->group_stop_count > 0 || |
| 102 | 102 | (freezing(t)) || |
| 103 | 103 | PENDING(&t->pending, &t->blocked) || |
| 104 | − PENDING(&t->signal->shared_pending, &t->blocked)) | |
| 104 | + PENDING(&t->signal->shared_pending, &t->blocked)) { | |
| 105 | 105 | set_tsk_thread_flag(t, TIF_SIGPENDING); |
| 106 | − else | |
| 107 | − clear_tsk_thread_flag(t, TIF_SIGPENDING); | |
| 106 | + return 1; | |
| 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 | 122 | void recalc_sigpending(void) |
| @@ -744,7 +756,7 @@ force_sig_info(int sig, struct siginfo *info, struct task_struct *t) · 633 unmodified lines | ||
| 744 | 756 | action->sa.sa_handler = SIG_DFL; |
| 745 | 757 | if (blocked) { |
| 746 | 758 | sigdelset(&t->blocked, sig); |
| 747 | − recalc_sigpending_tsk(t); | |
| 759 | + recalc_sigpending_and_wake(t); | |
| 748 | 760 | } |
| 749 | 761 | } |
| 750 | 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) · 1522 unmodified lines | ||
| 2273 | 2285 | rm_from_queue_full(&mask, &t->signal->shared_pending); |
| 2274 | 2286 | do { |
| 2275 | 2287 | rm_from_queue_full(&mask, &t->pending); |
| 2276 | − recalc_sigpending_tsk(t); | |
| 2288 | + recalc_sigpending_and_wake(t); | |
| 2277 | 2289 | t = next_thread(t); |
| 2278 | 2290 | } while (t != current); |
| 2279 | 2291 | } |