When a task exits and uses do_notify_parent to send tsk->exit_signal
or SIGCHLD this has one of two possible meanings.
- The ptraced task has exited
- The thread group has exited

Linux resolves this ambiguity by preferring the thread group exit
interpretation if it is possible.  As the exit of a thread group
containing a task is a superset of the exit of a task.

The code attempts to implement this in it's selection of signal
before calling do_notify_parent for a ptraced task.  Unfortunately
it fails to properly handle the case when the thread_group is still
running (but it's leader has exited).

Fix this for a ptraced child leader by skipping the notification
instead of changing the exit signal.  If we skip sending the signal in
exit_noitfy, when the last of the other threads are reaped
release_task will send the signal for us.

Signed-off-by: "Eric W. Biederman" <ebied...@xmission.com>
---
 kernel/exit.c | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/kernel/exit.c b/kernel/exit.c
index 85b34eff8807..72591eb5e361 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -639,7 +639,7 @@ static void forget_original_parent(struct task_struct 
*father,
  */
 static void exit_notify(struct task_struct *tsk, int group_dead)
 {
-       bool autoreap;
+       bool autoreap = true;
        struct task_struct *p, *n;
        LIST_HEAD(dead);
 
@@ -649,17 +649,12 @@ static void exit_notify(struct task_struct *tsk, int 
group_dead)
        if (group_dead)
                kill_orphaned_pgrp(tsk->group_leader, NULL);
 
-       if (unlikely(tsk->ptrace)) {
-               int sig = thread_group_leader(tsk) &&
-                               thread_group_empty(tsk) &&
-                               !ptrace_reparented(tsk) ?
-                       tsk->exit_signal : SIGCHLD;
-               autoreap = do_notify_parent(tsk, sig);
-       } else if (thread_group_leader(tsk)) {
+       if (thread_group_leader(tsk) && !ptrace_reparented(tsk)) {
                autoreap = thread_group_empty(tsk) &&
                        do_notify_parent(tsk, tsk->exit_signal);
-       } else {
-               autoreap = true;
+       }
+       else if (unlikely(tsk->ptrace)) {
+               autoreap = do_notify_parent(tsk, SIGCHLD);
        }
 
        tsk->exit_state = autoreap ? EXIT_DEAD : EXIT_ZOMBIE;
-- 
2.10.1

Reply via email to