change kernel threads to ignore signals instead of blocking them

Currently kernel threads use sigprocmask(SIG_BLOCK) to protect against
signals.  This doesn't prevent the signal delivery, this only blocks
signal_wake_up().  Every "killall -33 kthreadd" means a "struct siginfo"
leak.

Change kthreadd_setup() to set all handlers to SIG_IGN instead of blocking
them (make a new helper ignore_signals() for that).  If the kernel thread
needs some signal, it should use allow_signal() anyway, and in that case it
should not use CLONE_SIGHAND.

Note that we can't change daemonize() (should die!) in the same way,
because it can be used along with CLONE_SIGHAND.  This means that
allow_signal() still should unblock the signal to work correctly with
daemonize()ed threads.

However, disallow_signal() doesn't block the signal any longer but ignores
it.

NOTE: with or without this patch the kernel threads are not protected from
handle_stop_signal(), this seems harmless, but not good.

Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Oleg Nesterov 2007-05-09 02:34:37 -07:00 committed by Linus Torvalds
parent 5de18d1697
commit 10ab825bde
4 changed files with 15 additions and 15 deletions

View file

@ -1317,6 +1317,7 @@ extern int in_egroup_p(gid_t);
extern void proc_caches_init(void); extern void proc_caches_init(void);
extern void flush_signals(struct task_struct *); extern void flush_signals(struct task_struct *);
extern void ignore_signals(struct task_struct *);
extern void flush_signal_handlers(struct task_struct *, int force_default); extern void flush_signal_handlers(struct task_struct *, int force_default);
extern int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info); extern int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info);

View file

@ -347,7 +347,7 @@ int disallow_signal(int sig)
return -EINVAL; return -EINVAL;
spin_lock_irq(&current->sighand->siglock); spin_lock_irq(&current->sighand->siglock);
sigaddset(&current->blocked, sig); current->sighand->action[(sig)-1].sa.sa_handler = SIG_IGN;
recalc_sigpending(); recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock); spin_unlock_irq(&current->sighand->siglock);
return 0; return 0;

View file

@ -215,24 +215,13 @@ EXPORT_SYMBOL(kthread_stop);
static __init void kthreadd_setup(void) static __init void kthreadd_setup(void)
{ {
struct task_struct *tsk = current; struct task_struct *tsk = current;
struct k_sigaction sa;
sigset_t blocked;
set_task_comm(tsk, "kthreadd"); set_task_comm(tsk, "kthreadd");
/* Block and flush all signals */ ignore_signals(tsk);
sigfillset(&blocked);
sigprocmask(SIG_BLOCK, &blocked, NULL);
flush_signals(tsk);
/* SIG_IGN makes children autoreap: see do_notify_parent(). */ set_user_nice(tsk, -5);
sa.sa.sa_handler = SIG_IGN; set_cpus_allowed(tsk, CPU_MASK_ALL);
sa.sa.sa_flags = 0;
siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD));
do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0);
set_user_nice(current, -5);
set_cpus_allowed(current, CPU_MASK_ALL);
} }
int kthreadd(void *unused) int kthreadd(void *unused)

View file

@ -209,6 +209,16 @@ void flush_signals(struct task_struct *t)
spin_unlock_irqrestore(&t->sighand->siglock, flags); spin_unlock_irqrestore(&t->sighand->siglock, flags);
} }
void ignore_signals(struct task_struct *t)
{
int i;
for (i = 0; i < _NSIG; ++i)
t->sighand->action[i].sa.sa_handler = SIG_IGN;
flush_signals(t);
}
/* /*
* Flush all handlers for a task. * Flush all handlers for a task.
*/ */