Messages in this thread Patch in this message |  | | | Date | Thu, 29 Jan 2009 09:06:00 +0100 | | From | Oleg Nesterov <> | | Subject | [PATCH 3/4] reparent_thread: fix a zombie leak if /sbin/init ignores SIGCHLD |
| |
If /sbin/init ignores SIGCHLD and we re-parent a zombie, it is leaked. reparent_thread() does do_notify_parent() which sets ->exit_signal = -1 in this case. This means that nobody except us can reap it, the detached task is not visible to do_wait().
Change reparent_thread() to return a boolean (like __pthread_detach) to indicate that the thread is dead and must be released. Also change forget_original_parent() to add the child to ptrace_dead list in this case.
The naming becomes insane, the next patch does the cleanup.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
--- 6.29-rc3/kernel/exit.c~7_FIX_INIT_IGN_CHLD 2009-01-29 06:57:09.000000000 +0100 +++ 6.29-rc3/kernel/exit.c 2009-01-29 08:03:18.000000000 +0100 @@ -804,8 +804,11 @@ static void ptrace_exit_finish(struct ta } } -static void reparent_thread(struct task_struct *p, struct task_struct *father) +/* Returns nonzero if the child should be released. */ +static int reparent_thread(struct task_struct *p, struct task_struct *father) { + int dead; + if (p->pdeath_signal) /* We already hold the tasklist_lock here. */ group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p); @@ -813,12 +816,12 @@ static void reparent_thread(struct task_ list_move_tail(&p->sibling, &p->real_parent->children); if (task_detached(p)) - return; + return 0; /* If this is a threaded reparent there is no need to * notify anyone anything has happened. */ if (same_thread_group(p->real_parent, father)) - return; + return 0; /* We don't want people slaying init. */ p->exit_signal = SIGCHLD; @@ -826,11 +829,19 @@ static void reparent_thread(struct task_ /* If we'd notified the old parent about this child's death, * also notify the new parent. */ + dead = 0; if (!p->ptrace && - p->exit_state == EXIT_ZOMBIE && thread_group_empty(p)) + p->exit_state == EXIT_ZOMBIE && thread_group_empty(p)) { do_notify_parent(p, p->exit_signal); + if (task_detached(p)) { + p->exit_state = EXIT_DEAD; + dead = 1; + } + } kill_orphaned_pgrp(p, father); + + return dead; } /* @@ -890,7 +901,8 @@ static void forget_original_parent(struc BUG_ON(p->ptrace); p->parent = p->real_parent; } - reparent_thread(p, father); + if (reparent_thread(p, father)) + list_add(&p->ptrace_entry, &ptrace_dead);; } write_unlock_irq(&tasklist_lock);
|  |