Skip to content

Commit

Permalink
wait: fix reparent_leader() vs EXIT_DEAD->EXIT_ZOMBIE race
Browse files Browse the repository at this point in the history
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 50b8d25
"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 <[email protected]>
Reported-by: Jan Kratochvil <[email protected]>
Reported-by: Michal Schmidt <[email protected]>
Tested-by: Michal Schmidt <[email protected]>
Cc: Al Viro <[email protected]>
Cc: Lennart Poettering <[email protected]>
Cc: Roland McGrath <[email protected]>
Cc: Tejun Heo <[email protected]>
Cc: <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
oleg-nesterov authored and torvalds committed Apr 7, 2014
1 parent 23aebe1 commit dfccbb5
Showing 1 changed file with 11 additions and 4 deletions.
15 changes: 11 additions & 4 deletions kernel/exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -560,19 +560,26 @@ static void reparent_leader(struct task_struct *father, struct task_struct *p,
struct list_head *dead)
{
list_move_tail(&p->sibling, &p->real_parent->children);

if (p->exit_state == EXIT_DEAD)
return;
/*
* If this is a threaded reparent there is no need to
* notify anyone anything has happened.
*/
if (same_thread_group(p->real_parent, father))
return;

/* We don't want people slaying init. */
/*
* We don't want people slaying init.
*
* Note: we do this even if it is EXIT_DEAD, wait_task_zombie()
* can change ->exit_state to EXIT_ZOMBIE. If this is the final
* state, do_notify_parent() was already called and ->exit_signal
* doesn't matter.
*/
p->exit_signal = SIGCHLD;

if (p->exit_state == EXIT_DEAD)
return;

/* If it has exited notify the new parent about this child's death. */
if (!p->ptrace &&
p->exit_state == EXIT_ZOMBIE && thread_group_empty(p)) {
Expand Down

0 comments on commit dfccbb5

Please sign in to comment.