Messages in this thread Patch in this message | | | Date | Tue, 22 Apr 2008 19:38:20 +0400 | From | Oleg Nesterov <> | Subject | [PATCH 1/2] posix timers: (BUG 10460) discard the pending signal when the timer is destroyed |
| |
Currently sigqueue_free() removes sigqueue from list, but doesn't cancel the pending signal. This is indeed strange, the task should either receive the "full" signal along with siginfo_t, or it shouldn't see the signal at all.
Change sigqueue_free(q) to do sigdelset() if q is queued. There is a little complication. Looking at "struct sigqueue*" we can't figure out where it is pending, in ->pending or ->shared_pending. So this patch adds a new flag, SIGQUEUE_SHARED_PENDING, which is set/cleared by send_sigqueue().
Alternatively, we can add a new parameter to sigqueue_free(), and the caller can check SIGEV_THREAD_ID. But I think this is not as robust as a flag which is "automatically" correct.
This patch should also fix the problem pointed out by Austin Clements, see
http://bugzilla.kernel.org/show_bug.cgi?id=10460
Currently, when the task execs it could be killed by the fatal signal sent by the posix timer, because exec flushes the signal handlers.
Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>
--- 25/include/linux/signal.h~1_PT_CLEAR_PENDING 2008-03-04 21:43:25.000000000 +0300 +++ 25/include/linux/signal.h 2008-03-04 21:43:25.000000000 +0300 @@ -20,6 +20,7 @@ struct sigqueue { /* flags values. */ #define SIGQUEUE_PREALLOC 1 +#define SIGQUEUE_SHARED_PENDING 2 /* meaningful only if queued */ struct sigpending { struct list_head list; --- 25/kernel/signal.c~1_PT_CLEAR_PENDING 2008-03-21 18:10:30.000000000 +0300 +++ 25/kernel/signal.c 2008-04-22 16:40:45.000000000 +0400 @@ -1233,6 +1233,28 @@ struct sigqueue *sigqueue_alloc(void) return(q); } +static void sigqueue_cancel(struct sigqueue *q) +{ + struct sigpending *pending; + int sig; + + list_del_init(&q->list); + + pending = (q->flags & SIGQUEUE_SHARED_PENDING) + ? ¤t->signal->shared_pending + : ¤t->pending; + + sig = q->info.si_signo; + if (sig >= SIGRTMIN) { + list_for_each_entry(q, &pending->list, list) + if (q->info.si_signo == sig) + return; + } + + sigdelset(&pending->signal, sig); + recalc_sigpending(); +} + void sigqueue_free(struct sigqueue *q) { unsigned long flags; @@ -1246,7 +1268,7 @@ void sigqueue_free(struct sigqueue *q) */ spin_lock_irqsave(lock, flags); if (!list_empty(&q->list)) - list_del_init(&q->list); + sigqueue_cancel(q); spin_unlock_irqrestore(lock, flags); q->flags &= ~SIGQUEUE_PREALLOC; @@ -1282,7 +1304,13 @@ int send_sigqueue(struct sigqueue *q, st } signalfd_notify(t, sig); - pending = group ? &t->signal->shared_pending : &t->pending; + if (group) { + q->flags |= SIGQUEUE_SHARED_PENDING; + pending = &t->signal->shared_pending; + } else { + q->flags &= ~SIGQUEUE_SHARED_PENDING; + pending = &t->pending; + } list_add_tail(&q->list, &pending->list); sigaddset(&pending->signal, sig); complete_signal(sig, t, group);
| |