diff options
| author | Oleg Nesterov <oleg@redhat.com> | 2009-04-02 19:58:17 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-02 22:04:59 -0400 |
| commit | 7f5d3652d469cdf9eb2365dfea7ce3fb9e1409cc (patch) | |
| tree | 590f845665e166694ba0f9ba0e6d2267e15d8aae /kernel/exit.c | |
| parent | b1442b055c154699a6a2c436f3352f71b6beede3 (diff) | |
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>
Cc: Roland McGrath <roland@redhat.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/exit.c')
| -rw-r--r-- | kernel/exit.c | 22 |
1 files changed, 17 insertions, 5 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index 5be0a406fae..3e09b7cb3b2 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
| @@ -810,8 +810,11 @@ static void ptrace_exit_finish(struct task_struct *parent, | |||
| 810 | } | 810 | } |
| 811 | } | 811 | } |
| 812 | 812 | ||
| 813 | static void reparent_thread(struct task_struct *p, struct task_struct *father) | 813 | /* Returns nonzero if the child should be released. */ |
| 814 | static int reparent_thread(struct task_struct *p, struct task_struct *father) | ||
| 814 | { | 815 | { |
| 816 | int dead; | ||
| 817 | |||
| 815 | if (p->pdeath_signal) | 818 | if (p->pdeath_signal) |
| 816 | /* We already hold the tasklist_lock here. */ | 819 | /* We already hold the tasklist_lock here. */ |
| 817 | group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p); | 820 | group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p); |
| @@ -819,12 +822,12 @@ static void reparent_thread(struct task_struct *p, struct task_struct *father) | |||
| 819 | list_move_tail(&p->sibling, &p->real_parent->children); | 822 | list_move_tail(&p->sibling, &p->real_parent->children); |
| 820 | 823 | ||
| 821 | if (task_detached(p)) | 824 | if (task_detached(p)) |
| 822 | return; | 825 | return 0; |
| 823 | /* If this is a threaded reparent there is no need to | 826 | /* If this is a threaded reparent there is no need to |
| 824 | * notify anyone anything has happened. | 827 | * notify anyone anything has happened. |
| 825 | */ | 828 | */ |
| 826 | if (same_thread_group(p->real_parent, father)) | 829 | if (same_thread_group(p->real_parent, father)) |
| 827 | return; | 830 | return 0; |
| 828 | 831 | ||
| 829 | /* We don't want people slaying init. */ | 832 | /* We don't want people slaying init. */ |
| 830 | p->exit_signal = SIGCHLD; | 833 | p->exit_signal = SIGCHLD; |
| @@ -832,11 +835,19 @@ static void reparent_thread(struct task_struct *p, struct task_struct *father) | |||
| 832 | /* If we'd notified the old parent about this child's death, | 835 | /* If we'd notified the old parent about this child's death, |
| 833 | * also notify the new parent. | 836 | * also notify the new parent. |
| 834 | */ | 837 | */ |
| 838 | dead = 0; | ||
| 835 | if (!p->ptrace && | 839 | if (!p->ptrace && |
| 836 | p->exit_state == EXIT_ZOMBIE && thread_group_empty(p)) | 840 | p->exit_state == EXIT_ZOMBIE && thread_group_empty(p)) { |
| 837 | do_notify_parent(p, p->exit_signal); | 841 | do_notify_parent(p, p->exit_signal); |
| 842 | if (task_detached(p)) { | ||
| 843 | p->exit_state = EXIT_DEAD; | ||
| 844 | dead = 1; | ||
| 845 | } | ||
| 846 | } | ||
| 838 | 847 | ||
| 839 | kill_orphaned_pgrp(p, father); | 848 | kill_orphaned_pgrp(p, father); |
| 849 | |||
| 850 | return dead; | ||
| 840 | } | 851 | } |
| 841 | 852 | ||
| 842 | /* | 853 | /* |
| @@ -896,7 +907,8 @@ static void forget_original_parent(struct task_struct *father) | |||
| 896 | BUG_ON(p->ptrace); | 907 | BUG_ON(p->ptrace); |
| 897 | p->parent = p->real_parent; | 908 | p->parent = p->real_parent; |
| 898 | } | 909 | } |
| 899 | reparent_thread(p, father); | 910 | if (reparent_thread(p, father)) |
| 911 | list_add(&p->ptrace_entry, &ptrace_dead);; | ||
| 900 | } | 912 | } |
| 901 | 913 | ||
| 902 | write_unlock_irq(&tasklist_lock); | 914 | write_unlock_irq(&tasklist_lock); |
