forked from Minki/linux
[PATCH] send_sigqueue: simplify and fix the race
send_sigqueue() checks PF_EXITING, then locks p->sighand->siglock. This is unsafe: 'p' can exit in between and set ->sighand = NULL. The race is theoretical, the window is tiny and irqs are disabled by the caller, so I don't think we need the fix for -stable tree. Convert send_sigqueue() to use lock_task_sighand() helper. Also, delete 'p->flags & PF_EXITING' re-check, it is unneeded and the comment is wrong. Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
a1d5e21e3e
commit
547679087b
@ -1309,12 +1309,10 @@ void sigqueue_free(struct sigqueue *q)
|
|||||||
__sigqueue_free(q);
|
__sigqueue_free(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int send_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
|
||||||
send_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
|
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct sighand_struct *sh;
|
|
||||||
|
|
||||||
BUG_ON(!(q->flags & SIGQUEUE_PREALLOC));
|
BUG_ON(!(q->flags & SIGQUEUE_PREALLOC));
|
||||||
|
|
||||||
@ -1328,48 +1326,17 @@ send_sigqueue(int sig, struct sigqueue *q, struct task_struct *p)
|
|||||||
*/
|
*/
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
|
|
||||||
if (unlikely(p->flags & PF_EXITING)) {
|
if (!likely(lock_task_sighand(p, &flags))) {
|
||||||
ret = -1;
|
ret = -1;
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
retry:
|
|
||||||
sh = rcu_dereference(p->sighand);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&sh->siglock, flags);
|
|
||||||
if (p->sighand != sh) {
|
|
||||||
/* We raced with exec() in a multithreaded process... */
|
|
||||||
spin_unlock_irqrestore(&sh->siglock, flags);
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We do the check here again to handle the following scenario:
|
|
||||||
*
|
|
||||||
* CPU 0 CPU 1
|
|
||||||
* send_sigqueue
|
|
||||||
* check PF_EXITING
|
|
||||||
* interrupt exit code running
|
|
||||||
* __exit_signal
|
|
||||||
* lock sighand->siglock
|
|
||||||
* unlock sighand->siglock
|
|
||||||
* lock sh->siglock
|
|
||||||
* add(tsk->pending) flush_sigqueue(tsk->pending)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (unlikely(p->flags & PF_EXITING)) {
|
|
||||||
ret = -1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(!list_empty(&q->list))) {
|
if (unlikely(!list_empty(&q->list))) {
|
||||||
/*
|
/*
|
||||||
* If an SI_TIMER entry is already queue just increment
|
* If an SI_TIMER entry is already queue just increment
|
||||||
* the overrun count.
|
* the overrun count.
|
||||||
*/
|
*/
|
||||||
if (q->info.si_code != SI_TIMER)
|
BUG_ON(q->info.si_code != SI_TIMER);
|
||||||
BUG();
|
|
||||||
q->info.si_overrun++;
|
q->info.si_overrun++;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -1385,7 +1352,7 @@ retry:
|
|||||||
signal_wake_up(p, sig == SIGKILL);
|
signal_wake_up(p, sig == SIGKILL);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
spin_unlock_irqrestore(&sh->siglock, flags);
|
unlock_task_sighand(p, &flags);
|
||||||
out_err:
|
out_err:
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user