diff options
| author | Ingo Molnar <mingo@elte.hu> | 2002-10-02 23:32:38 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@penguin.transmeta.com> | 2002-10-02 23:32:38 -0700 |
| commit | 794aa320b79d2cb8643ecb6058f0f3fadd51955d (patch) | |
| tree | 9a104738c8c07e1af2797ee7b2d0b48a64e8c3aa | |
| parent | 6a20c6fee5ba95d7244762a1873a1754d006ec7b (diff) | |
[PATCH] sigfix-2.5.40-D6
This fixes all known signal semantics problems.
sigwait() is really evil - i had to re-introduce ->real_blocked. When a
signal has no handler defined then the actual action taken by the kernel
depends on whether the sigwait()-ing thread was blocking the signal
originally or not. If the signal was blocked => specific delivery to the
thread, if the signal was not blocked => kill-all.
fortunately this meant that PF_SIGWAIT could be killed - the real_blocked
field contains all the necessery information to do the right decision at
signal-sending time.
i've also cleaned up and made the shared-pending code more robust: now
there's a single central dequeue_signal() function that handles all the
details. Plus upon unqueueing a shared-pending signal we now re-queue the
signal to the current thread, which this time around is not going to end
up in the shared-pending queue. This change handles the following case
correctly: a signal was blocked in every signal, then one thread unblocks
it and gets the signal delivered - but there's no handler for the signal
=> the correct action is to do a kill-all.
i removed the unused shared_unblocked field as well, reported by Oleg
Nesterov.
now we pass both signal-tst1 and signal-tst2, so i'm confident that we got
most of the details right.
| -rw-r--r-- | include/linux/sched.h | 4 | ||||
| -rw-r--r-- | kernel/signal.c | 53 |
2 files changed, 34 insertions, 23 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index 59dcfad4667e..89c4ead4cf4b 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -378,7 +378,7 @@ struct task_struct { /* signal handlers */ struct signal_struct *sig; - sigset_t blocked, real_blocked, shared_unblocked; + sigset_t blocked, real_blocked; struct sigpending pending; unsigned long sas_ss_sp; @@ -530,7 +530,7 @@ extern void proc_caches_init(void); extern void flush_signals(struct task_struct *); extern void flush_signal_handlers(struct task_struct *); extern void sig_exit(int, int, struct siginfo *); -extern int dequeue_signal(struct sigpending *pending, sigset_t *mask, siginfo_t *info); +extern int dequeue_signal(sigset_t *mask, siginfo_t *info); extern void block_all_signals(int (*notifier)(void *priv), void *priv, sigset_t *mask); extern void unblock_all_signals(void); diff --git a/kernel/signal.c b/kernel/signal.c index c7701f42a932..b037b12ce04b 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -405,14 +405,8 @@ found_another: return 0; } -/* - * Dequeue a signal and return the element to the caller, which is - * expected to free it. - * - * All callers have to hold the siglock. - */ - -int dequeue_signal(struct sigpending *pending, sigset_t *mask, siginfo_t *info) +static int __dequeue_signal(struct sigpending *pending, sigset_t *mask, + siginfo_t *info) { int sig = 0; @@ -438,6 +432,27 @@ int dequeue_signal(struct sigpending *pending, sigset_t *mask, siginfo_t *info) return sig; } +/* + * Dequeue a signal and return the element to the caller, which is + * expected to free it. + * + * All callers have to hold the siglock. + */ +int dequeue_signal(sigset_t *mask, siginfo_t *info) +{ + /* + * Here we handle shared pending signals. To implement the full + * semantics we need to unqueue and resend them. It will likely + * get into our own pending queue. + */ + if (current->sig->shared_pending.head) { + int signr = __dequeue_signal(¤t->sig->shared_pending, mask, info); + if (signr) + __send_sig_info(signr, info, current); + } + return __dequeue_signal(¤t->pending, mask, info); +} + static int rm_from_queue(int sig, struct sigpending *s) { struct sigqueue *q, **pp; @@ -843,8 +858,7 @@ struct task_struct * find_unblocked_thread(struct task_struct *p, int signr) struct pid *pid; for_each_task_pid(p->tgid, PIDTYPE_TGID, tmp, l, pid) - if (!sigismember(&tmp->blocked, signr) && - !sigismember(&tmp->real_blocked, signr)) + if (!sigismember(&tmp->blocked, signr)) return tmp; return NULL; } @@ -887,6 +901,10 @@ __send_sig_info(int sig, struct siginfo *info, struct task_struct *p) ret = specific_send_sig_info(sig, info, p, 1); goto out_unlock; } + if (sigismember(&t->real_blocked,sig)) { + ret = specific_send_sig_info(sig, info, t, 0); + goto out_unlock; + } if (sig_kernel_broadcast(sig) || sig_kernel_coredump(sig)) { ret = __broadcast_thread_group(p, sig); goto out_unlock; @@ -1169,10 +1187,7 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs) struct k_sigaction *ka; spin_lock_irq(¤t->sig->siglock); - if (current->sig->shared_pending.head) - signr = dequeue_signal(¤t->sig->shared_pending, mask, info); - if (!signr) - signr = dequeue_signal(¤t->pending, mask, info); + signr = dequeue_signal(mask, info); spin_unlock_irq(¤t->sig->siglock); if (!signr) @@ -1268,7 +1283,7 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs) #endif EXPORT_SYMBOL(recalc_sigpending); -EXPORT_SYMBOL(dequeue_signal); +EXPORT_SYMBOL_GPL(dequeue_signal); EXPORT_SYMBOL(flush_signals); EXPORT_SYMBOL(force_sig); EXPORT_SYMBOL(force_sig_info); @@ -1469,9 +1484,7 @@ sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, } spin_lock_irq(¤t->sig->siglock); - sig = dequeue_signal(¤t->sig->shared_pending, &these, &info); - if (!sig) - sig = dequeue_signal(¤t->pending, &these, &info); + sig = dequeue_signal(&these, &info); if (!sig) { timeout = MAX_SCHEDULE_TIMEOUT; if (uts) @@ -1491,9 +1504,7 @@ sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, timeout = schedule_timeout(timeout); spin_lock_irq(¤t->sig->siglock); - sig = dequeue_signal(¤t->sig->shared_pending, &these, &info); - if (!sig) - sig = dequeue_signal(¤t->pending, &these, &info); + sig = dequeue_signal(&these, &info); current->blocked = current->real_blocked; siginitset(¤t->real_blocked, 0); recalc_sigpending(); |
