Commit 10ab825bdef8df510f99c703a5a2d9b13a4e31a5
Committed by
Linus Torvalds
1 parent
5de18d1697
Exists in
master
and in
39 other branches
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>
Showing 4 changed files with 15 additions and 15 deletions Side-by-side Diff
include/linux/sched.h
... | ... | @@ -1317,6 +1317,7 @@ |
1317 | 1317 | |
1318 | 1318 | extern void proc_caches_init(void); |
1319 | 1319 | extern void flush_signals(struct task_struct *); |
1320 | +extern void ignore_signals(struct task_struct *); | |
1320 | 1321 | extern void flush_signal_handlers(struct task_struct *, int force_default); |
1321 | 1322 | extern int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info); |
1322 | 1323 |
kernel/exit.c
... | ... | @@ -347,7 +347,7 @@ |
347 | 347 | return -EINVAL; |
348 | 348 | |
349 | 349 | spin_lock_irq(¤t->sighand->siglock); |
350 | - sigaddset(¤t->blocked, sig); | |
350 | + current->sighand->action[(sig)-1].sa.sa_handler = SIG_IGN; | |
351 | 351 | recalc_sigpending(); |
352 | 352 | spin_unlock_irq(¤t->sighand->siglock); |
353 | 353 | return 0; |
kernel/kthread.c
... | ... | @@ -215,24 +215,13 @@ |
215 | 215 | static __init void kthreadd_setup(void) |
216 | 216 | { |
217 | 217 | struct task_struct *tsk = current; |
218 | - struct k_sigaction sa; | |
219 | - sigset_t blocked; | |
220 | 218 | |
221 | 219 | set_task_comm(tsk, "kthreadd"); |
222 | 220 | |
223 | - /* Block and flush all signals */ | |
224 | - sigfillset(&blocked); | |
225 | - sigprocmask(SIG_BLOCK, &blocked, NULL); | |
226 | - flush_signals(tsk); | |
221 | + ignore_signals(tsk); | |
227 | 222 | |
228 | - /* SIG_IGN makes children autoreap: see do_notify_parent(). */ | |
229 | - sa.sa.sa_handler = SIG_IGN; | |
230 | - sa.sa.sa_flags = 0; | |
231 | - siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); | |
232 | - do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0); | |
233 | - | |
234 | - set_user_nice(current, -5); | |
235 | - set_cpus_allowed(current, CPU_MASK_ALL); | |
223 | + set_user_nice(tsk, -5); | |
224 | + set_cpus_allowed(tsk, CPU_MASK_ALL); | |
236 | 225 | } |
237 | 226 | |
238 | 227 | int kthreadd(void *unused) |
kernel/signal.c
... | ... | @@ -209,6 +209,16 @@ |
209 | 209 | spin_unlock_irqrestore(&t->sighand->siglock, flags); |
210 | 210 | } |
211 | 211 | |
212 | +void ignore_signals(struct task_struct *t) | |
213 | +{ | |
214 | + int i; | |
215 | + | |
216 | + for (i = 0; i < _NSIG; ++i) | |
217 | + t->sighand->action[i].sa.sa_handler = SIG_IGN; | |
218 | + | |
219 | + flush_signals(t); | |
220 | +} | |
221 | + | |
212 | 222 | /* |
213 | 223 | * Flush all handlers for a task. |
214 | 224 | */ |