diff options
| author | Oleg Nesterov <oleg@redhat.com> | 2014-04-07 18:38:41 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-07 19:36:05 -0400 |
| commit | dfccbb5e49a621c1b21a62527d61fc4305617aca (patch) | |
| tree | e0f934f93bb7fe7093064216a1945a6b16fecaf7 /kernel | |
| parent | 23aebe1691a3d98a79676db6c0fd813e16478804 (diff) | |
wait: fix reparent_leader() vs EXIT_DEAD->EXIT_ZOMBIE race
wait_task_zombie() first does EXIT_ZOMBIE->EXIT_DEAD transition and
drops tasklist_lock. If this task is not the natural child and it is
traced, we change its state back to EXIT_ZOMBIE for ->real_parent.
The last transition is racy, this is even documented in 50b8d257486a
"ptrace: partially fix the do_wait(WEXITED) vs EXIT_DEAD->EXIT_ZOMBIE
race". wait_consider_task() tries to detect this transition and clear
->notask_error but we can't rely on ptrace_reparented(), debugger can
exit and do ptrace_unlink() before its sub-thread sets EXIT_ZOMBIE.
And there is another problem which were missed before: this transition
can also race with reparent_leader() which doesn't reset >exit_signal if
EXIT_DEAD, assuming that this task must be reaped by someone else. So
the tracee can be re-parented with ->exit_signal != SIGCHLD, and if
/sbin/init doesn't use __WALL it becomes unreapable.
Change reparent_leader() to update ->exit_signal even if EXIT_DEAD.
Note: this is the simple temporary hack for -stable, it doesn't try to
solve all problems, it will be reverted by the next changes.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Reported-by: Jan Kratochvil <jan.kratochvil@redhat.com>
Reported-by: Michal Schmidt <mschmidt@redhat.com>
Tested-by: Michal Schmidt <mschmidt@redhat.com>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Lennart Poettering <lpoetter@redhat.com>
Cc: Roland McGrath <roland@hack.frob.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/exit.c | 15 |
1 files changed, 11 insertions, 4 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index decf648574f6..e354cbb13a9b 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
| @@ -560,9 +560,6 @@ static void reparent_leader(struct task_struct *father, struct task_struct *p, | |||
| 560 | struct list_head *dead) | 560 | struct list_head *dead) |
| 561 | { | 561 | { |
| 562 | list_move_tail(&p->sibling, &p->real_parent->children); | 562 | list_move_tail(&p->sibling, &p->real_parent->children); |
| 563 | |||
| 564 | if (p->exit_state == EXIT_DEAD) | ||
| 565 | return; | ||
| 566 | /* | 563 | /* |
| 567 | * If this is a threaded reparent there is no need to | 564 | * If this is a threaded reparent there is no need to |
| 568 | * notify anyone anything has happened. | 565 | * notify anyone anything has happened. |
| @@ -570,9 +567,19 @@ static void reparent_leader(struct task_struct *father, struct task_struct *p, | |||
| 570 | if (same_thread_group(p->real_parent, father)) | 567 | if (same_thread_group(p->real_parent, father)) |
| 571 | return; | 568 | return; |
| 572 | 569 | ||
| 573 | /* We don't want people slaying init. */ | 570 | /* |
| 571 | * We don't want people slaying init. | ||
| 572 | * | ||
| 573 | * Note: we do this even if it is EXIT_DEAD, wait_task_zombie() | ||
| 574 | * can change ->exit_state to EXIT_ZOMBIE. If this is the final | ||
| 575 | * state, do_notify_parent() was already called and ->exit_signal | ||
| 576 | * doesn't matter. | ||
| 577 | */ | ||
| 574 | p->exit_signal = SIGCHLD; | 578 | p->exit_signal = SIGCHLD; |
| 575 | 579 | ||
| 580 | if (p->exit_state == EXIT_DEAD) | ||
| 581 | return; | ||
| 582 | |||
| 576 | /* If it has exited notify the new parent about this child's death. */ | 583 | /* If it has exited notify the new parent about this child's death. */ |
| 577 | if (!p->ptrace && | 584 | if (!p->ptrace && |
| 578 | p->exit_state == EXIT_ZOMBIE && thread_group_empty(p)) { | 585 | p->exit_state == EXIT_ZOMBIE && thread_group_empty(p)) { |
