From: George Anzinger While rooting aroung in the signal code trying to understand how to fix the SIG_IGN ploy (set sig handler to SIG_IGN and flood system with high speed repeating timers) I came across what, I think, is a problem in sigaction() in that when processing a SIG_IGN request it flushes signals from 1 to SIGRTMIN and leaves the rest. Attempt to fix this. Signed-off-by: George Anzinger Cc: Roland McGrath Cc: Linus Torvalds Signed-off-by: Andrew Morton --- include/linux/signal.h | 16 ++++++++++++++++ kernel/signal.c | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff -puN include/linux/signal.h~sigaction-should-clear-all-signals-on-sig_ign-not-just include/linux/signal.h --- devel/include/linux/signal.h~sigaction-should-clear-all-signals-on-sig_ign-not-just 2005-11-16 18:30:40.000000000 -0800 +++ devel-akpm/include/linux/signal.h 2005-11-16 18:30:40.000000000 -0800 @@ -81,6 +81,22 @@ static inline int sigfindinword(unsigned #endif /* __HAVE_ARCH_SIG_BITOPS */ +static inline int sigisemptyset(sigset_t *set) +{ + extern void _NSIG_WORDS_is_unsupported_size(void); + switch (_NSIG_WORDS) { + case 4: + return (set->sig[3] | set->sig[2] | + set->sig[1] | set->sig[0]) == 0; + case 2: + return (set->sig[1] | set->sig[0]) == 0; + case 1: + return set->sig[0] == 0; + default: + _NSIG_WORDS_is_unsupported_size(); + } +} + #define sigmask(sig) (1UL << ((sig) - 1)) #ifndef __HAVE_ARCH_SIG_SETOPS diff -puN kernel/signal.c~sigaction-should-clear-all-signals-on-sig_ign-not-just kernel/signal.c --- devel/kernel/signal.c~sigaction-should-clear-all-signals-on-sig_ign-not-just 2005-11-16 18:30:40.000000000 -0800 +++ devel-akpm/kernel/signal.c 2005-11-16 18:30:40.000000000 -0800 @@ -613,6 +613,33 @@ void signal_wake_up(struct task_struct * * Returns 1 if any signals were found. * * All callers must be holding the siglock. + * + * This version takes a sigset mask and looks at all signals, + * not just those in the first mask word. + */ +static int rm_from_queue_full(sigset_t *mask, struct sigpending *s) +{ + struct sigqueue *q, *n; + sigset_t m; + + sigandsets(&m, mask, &s->signal); + if (sigisemptyset(&m)) + return 0; + + signandsets(&s->signal, &s->signal, mask); + list_for_each_entry_safe(q, n, &s->list, list) { + if (sigismember(mask, q->info.si_signo)) { + list_del_init(&q->list); + __sigqueue_free(q); + } + } + return 1; +} +/* + * Remove signals in mask from the pending set and queue. + * Returns 1 if any signals were found. + * + * All callers must be holding the siglock. */ static int rm_from_queue(unsigned long mask, struct sigpending *s) { @@ -2338,6 +2365,7 @@ int do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact) { struct k_sigaction *k; + sigset_t mask; if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig))) return -EINVAL; @@ -2385,9 +2413,11 @@ do_sigaction(int sig, const struct k_sig *k = *act; sigdelsetmask(&k->sa.sa_mask, sigmask(SIGKILL) | sigmask(SIGSTOP)); - rm_from_queue(sigmask(sig), &t->signal->shared_pending); + sigemptyset(&mask); + sigaddset(&mask, sig); + rm_from_queue_full(&mask, &t->signal->shared_pending); do { - rm_from_queue(sigmask(sig), &t->pending); + rm_from_queue_full(&mask, &t->pending); recalc_sigpending_tsk(t); t = next_thread(t); } while (t != current); _