aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/exit.c
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2008-04-09 02:12:30 -0400
committerRoland McGrath <roland@redhat.com>2008-07-16 21:02:34 -0400
commit666f164f4fbfa78bd00fb4b74788b42a39842c64 (patch)
tree27c6a77edbce3da0860b5bd05cf3a7a842140c2c /kernel/exit.c
parent14dd0b81414a58caf0296dbeace016bb0a5d11ab (diff)
fix dangling zombie when new parent ignores children
This fixes an arcane bug that we think was a regression introduced by commit b2b2cbc4b2a2f389442549399a993a8306420baf. When a parent ignores SIGCHLD (or uses SA_NOCLDWAIT), its children would self-reap but they don't if it's using ptrace on them. When the parent thread later exits and ceases to ptrace a child but leaves other live threads in the parent's thread group, any zombie children are left dangling. The fix makes them self-reap then, as they would have done earlier if ptrace had not been in use. Signed-off-by: Roland McGrath <roland@redhat.com>
Diffstat (limited to 'kernel/exit.c')
-rw-r--r--kernel/exit.c26
1 files changed, 26 insertions, 0 deletions
diff --git a/kernel/exit.c b/kernel/exit.c
index a2af6cac823c..93d2711b9381 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -703,6 +703,23 @@ static void exit_mm(struct task_struct * tsk)
703} 703}
704 704
705/* 705/*
706 * Return nonzero if @parent's children should reap themselves.
707 *
708 * Called with write_lock_irq(&tasklist_lock) held.
709 */
710static int ignoring_children(struct task_struct *parent)
711{
712 int ret;
713 struct sighand_struct *psig = parent->sighand;
714 unsigned long flags;
715 spin_lock_irqsave(&psig->siglock, flags);
716 ret = (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN ||
717 (psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT));
718 spin_unlock_irqrestore(&psig->siglock, flags);
719 return ret;
720}
721
722/*
706 * Detach all tasks we were using ptrace on. 723 * Detach all tasks we were using ptrace on.
707 * Any that need to be release_task'd are put on the @dead list. 724 * Any that need to be release_task'd are put on the @dead list.
708 * 725 *
@@ -711,6 +728,7 @@ static void exit_mm(struct task_struct * tsk)
711static void ptrace_exit(struct task_struct *parent, struct list_head *dead) 728static void ptrace_exit(struct task_struct *parent, struct list_head *dead)
712{ 729{
713 struct task_struct *p, *n; 730 struct task_struct *p, *n;
731 int ign = -1;
714 732
715 list_for_each_entry_safe(p, n, &parent->ptraced, ptrace_entry) { 733 list_for_each_entry_safe(p, n, &parent->ptraced, ptrace_entry) {
716 __ptrace_unlink(p); 734 __ptrace_unlink(p);
@@ -726,10 +744,18 @@ static void ptrace_exit(struct task_struct *parent, struct list_head *dead)
726 * release_task() here because we already hold tasklist_lock. 744 * release_task() here because we already hold tasklist_lock.
727 * 745 *
728 * If it's our own child, there is no notification to do. 746 * If it's our own child, there is no notification to do.
747 * But if our normal children self-reap, then this child
748 * was prevented by ptrace and we must reap it now.
729 */ 749 */
730 if (!task_detached(p) && thread_group_empty(p)) { 750 if (!task_detached(p) && thread_group_empty(p)) {
731 if (!same_thread_group(p->real_parent, parent)) 751 if (!same_thread_group(p->real_parent, parent))
732 do_notify_parent(p, p->exit_signal); 752 do_notify_parent(p, p->exit_signal);
753 else {
754 if (ign < 0)
755 ign = ignoring_children(parent);
756 if (ign)
757 p->exit_signal = -1;
758 }
733 } 759 }
734 760
735 if (task_detached(p)) { 761 if (task_detached(p)) {