aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorAndrea Arcangeli <andrea@suse.de>2004-06-30 19:25:45 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-06-30 19:25:45 -0700
commit1e1e19c08a053b9203f440cc8f34132817b54a43 (patch)
tree438c2073a5e50bcb59a6b64593e53da4d8292b3f /kernel
parent82bd5317bbace2c6cfcc573569eddae62a95c1d7 (diff)
downloadhistory-1e1e19c08a053b9203f440cc8f34132817b54a43.tar.gz
[PATCH] zombie with CLONE_THREAD and strace
'strace' shows a problem with a missing release_task for self-reaping clones that have been traced. We need to defer releasing them until the tracer is done with them, but if the tracer dies, we need to handle that case gracefully too. We do that by having 'forget_original_parent()' generate a list of tasks to release when this case happens. Patch based on discussions on linux-kernel, and suggestions from Roland McGrath <roland@redhat.com>.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/exit.c34
1 files changed, 31 insertions, 3 deletions
diff --git a/kernel/exit.c b/kernel/exit.c
index 9cb99b471d2e05..cf71b30d8bd599 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -594,7 +594,8 @@ static inline void reparent_thread(task_t *p, task_t *father, int traced)
* group, and if no such member exists, give it to
* the global child reaper process (ie "init")
*/
-static inline void forget_original_parent(struct task_struct * father)
+static inline void forget_original_parent(struct task_struct * father,
+ struct list_head *to_release)
{
struct task_struct *p, *reaper = father;
struct list_head *_p, *_n;
@@ -612,16 +613,34 @@ static inline void forget_original_parent(struct task_struct * father)
* Search them and reparent children.
*/
list_for_each_safe(_p, _n, &father->children) {
+ int ptrace;
p = list_entry(_p,struct task_struct,sibling);
+
+ ptrace = p->ptrace;
+
+ /* if father isn't the real parent, then ptrace must be enabled */
+ BUG_ON(father != p->real_parent && !ptrace);
+
if (father == p->real_parent) {
+ /* reparent with a reaper, real father it's us */
choose_new_parent(p, reaper, child_reaper);
reparent_thread(p, father, 0);
} else {
- ptrace_unlink (p);
+ /* reparent ptraced task to its real parent */
+ __ptrace_unlink (p);
if (p->state == TASK_ZOMBIE && p->exit_signal != -1 &&
thread_group_empty(p))
do_notify_parent(p, p->exit_signal);
}
+
+ /*
+ * if the ptraced child is a zombie with exit_signal == -1
+ * we must collect it before we exit, or it will remain
+ * zombie forever since we prevented it from self-reap itself
+ * while it was being traced by us, to be able to see it in wait4.
+ */
+ if (unlikely(ptrace && p->state == TASK_ZOMBIE && p->exit_signal == -1))
+ list_add(&p->ptrace_list, to_release);
}
list_for_each_safe(_p, _n, &father->ptrace_children) {
p = list_entry(_p,struct task_struct,ptrace_list);
@@ -638,6 +657,7 @@ static void exit_notify(struct task_struct *tsk)
{
int state;
struct task_struct *t;
+ struct list_head ptrace_dead, *_p, *_n;
if (signal_pending(tsk) && !tsk->signal->group_exit
&& !thread_group_empty(tsk)) {
@@ -673,8 +693,10 @@ static void exit_notify(struct task_struct *tsk)
* jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2)
*/
- forget_original_parent(tsk);
+ INIT_LIST_HEAD(&ptrace_dead);
+ forget_original_parent(tsk, &ptrace_dead);
BUG_ON(!list_empty(&tsk->children));
+ BUG_ON(!list_empty(&tsk->ptrace_children));
/*
* Check to see if any process groups have become orphaned
@@ -759,6 +781,12 @@ static void exit_notify(struct task_struct *tsk)
_raw_write_unlock(&tasklist_lock);
local_irq_enable();
+ list_for_each_safe(_p, _n, &ptrace_dead) {
+ list_del_init(_p);
+ t = list_entry(_p,struct task_struct,ptrace_list);
+ release_task(t);
+ }
+
/* If the process is dead, release it - nobody will wait for it */
if (state == TASK_DEAD)
release_task(tsk);