mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 13:44:15 +08:00
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>
This commit is contained in:
parent
3c6df2a917
commit
7bb44adef3
@ -1616,11 +1616,13 @@ static inline int lock_need_resched(spinlock_t *lock)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reevaluate whether the task has signals pending delivery.
|
/*
|
||||||
This is required every time the blocked sigset_t changes.
|
* Reevaluate whether the task has signals pending delivery.
|
||||||
callers must hold sighand->siglock. */
|
* Wake the task if so.
|
||||||
|
* This is required every time the blocked sigset_t changes.
|
||||||
extern FASTCALL(void recalc_sigpending_tsk(struct task_struct *t));
|
* callers must hold sighand->siglock.
|
||||||
|
*/
|
||||||
|
extern void recalc_sigpending_and_wake(struct task_struct *t);
|
||||||
extern void recalc_sigpending(void);
|
extern void recalc_sigpending(void);
|
||||||
|
|
||||||
extern void signal_wake_up(struct task_struct *t, int resume_stopped);
|
extern void signal_wake_up(struct task_struct *t, int resume_stopped);
|
||||||
|
@ -762,11 +762,8 @@ static void exit_notify(struct task_struct *tsk)
|
|||||||
read_lock(&tasklist_lock);
|
read_lock(&tasklist_lock);
|
||||||
spin_lock_irq(&tsk->sighand->siglock);
|
spin_lock_irq(&tsk->sighand->siglock);
|
||||||
for (t = next_thread(tsk); t != tsk; t = next_thread(t))
|
for (t = next_thread(tsk); t != tsk; t = next_thread(t))
|
||||||
if (!signal_pending(t) && !(t->flags & PF_EXITING)) {
|
if (!signal_pending(t) && !(t->flags & PF_EXITING))
|
||||||
recalc_sigpending_tsk(t);
|
recalc_sigpending_and_wake(t);
|
||||||
if (signal_pending(t))
|
|
||||||
signal_wake_up(t, 0);
|
|
||||||
}
|
|
||||||
spin_unlock_irq(&tsk->sighand->siglock);
|
spin_unlock_irq(&tsk->sighand->siglock);
|
||||||
read_unlock(&tasklist_lock);
|
read_unlock(&tasklist_lock);
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ static void cancel_freezing(struct task_struct *p)
|
|||||||
pr_debug(" clean up: %s\n", p->comm);
|
pr_debug(" clean up: %s\n", p->comm);
|
||||||
do_not_freeze(p);
|
do_not_freeze(p);
|
||||||
spin_lock_irqsave(&p->sighand->siglock, flags);
|
spin_lock_irqsave(&p->sighand->siglock, flags);
|
||||||
recalc_sigpending_tsk(p);
|
recalc_sigpending_and_wake(p);
|
||||||
spin_unlock_irqrestore(&p->sighand->siglock, flags);
|
spin_unlock_irqrestore(&p->sighand->siglock, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,15 +96,27 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked)
|
|||||||
|
|
||||||
#define PENDING(p,b) has_pending_signals(&(p)->signal, (b))
|
#define PENDING(p,b) has_pending_signals(&(p)->signal, (b))
|
||||||
|
|
||||||
fastcall void recalc_sigpending_tsk(struct task_struct *t)
|
static int recalc_sigpending_tsk(struct task_struct *t)
|
||||||
{
|
{
|
||||||
if (t->signal->group_stop_count > 0 ||
|
if (t->signal->group_stop_count > 0 ||
|
||||||
(freezing(t)) ||
|
(freezing(t)) ||
|
||||||
PENDING(&t->pending, &t->blocked) ||
|
PENDING(&t->pending, &t->blocked) ||
|
||||||
PENDING(&t->signal->shared_pending, &t->blocked))
|
PENDING(&t->signal->shared_pending, &t->blocked)) {
|
||||||
set_tsk_thread_flag(t, TIF_SIGPENDING);
|
set_tsk_thread_flag(t, TIF_SIGPENDING);
|
||||||
else
|
return 1;
|
||||||
clear_tsk_thread_flag(t, TIF_SIGPENDING);
|
}
|
||||||
|
clear_tsk_thread_flag(t, TIF_SIGPENDING);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* After recalculating TIF_SIGPENDING, we need to make sure the task wakes up.
|
||||||
|
* This is superfluous when called on current, the wakeup is a harmless no-op.
|
||||||
|
*/
|
||||||
|
void recalc_sigpending_and_wake(struct task_struct *t)
|
||||||
|
{
|
||||||
|
if (recalc_sigpending_tsk(t))
|
||||||
|
signal_wake_up(t, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void recalc_sigpending(void)
|
void recalc_sigpending(void)
|
||||||
@ -744,7 +756,7 @@ force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
|
|||||||
action->sa.sa_handler = SIG_DFL;
|
action->sa.sa_handler = SIG_DFL;
|
||||||
if (blocked) {
|
if (blocked) {
|
||||||
sigdelset(&t->blocked, sig);
|
sigdelset(&t->blocked, sig);
|
||||||
recalc_sigpending_tsk(t);
|
recalc_sigpending_and_wake(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = specific_send_sig_info(sig, info, t);
|
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)
|
|||||||
rm_from_queue_full(&mask, &t->signal->shared_pending);
|
rm_from_queue_full(&mask, &t->signal->shared_pending);
|
||||||
do {
|
do {
|
||||||
rm_from_queue_full(&mask, &t->pending);
|
rm_from_queue_full(&mask, &t->pending);
|
||||||
recalc_sigpending_tsk(t);
|
recalc_sigpending_and_wake(t);
|
||||||
t = next_thread(t);
|
t = next_thread(t);
|
||||||
} while (t != current);
|
} while (t != current);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user