diff options
author | Oleg Nesterov <oleg@redhat.com> | 2017-01-30 13:17:35 -0500 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2017-02-01 00:20:48 -0500 |
commit | c6c70f4455d1eda91065e93cc4f7eddf4499b105 (patch) | |
tree | bf2f38330bc30a13329ae275172deffe863b36a8 | |
parent | 1328c727004d432bbdfba0ffa02a166df04c7305 (diff) |
exit: fix the setns() && PR_SET_CHILD_SUBREAPER interaction
find_new_reaper() checks same_thread_group(reaper, child_reaper) to
prevent the cross-namespace reparenting but this is not enough if the
exiting parent was injected by setns() + fork().
Suppose we have a process P in the root namespace and some namespace X.
P does setns() to enter the X namespace, and forks the child C.
C forks a grandchild G and exits.
The grandchild G should be re-parented to X->child_reaper, but in this
case the ->real_parent chain does not lead to ->child_reaper, so it will
be wrongly reparanted to P's sub-reaper or a global init.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
-rw-r--r-- | kernel/exit.c | 13 |
1 files changed, 8 insertions, 5 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index 8f14b866f9f6..5cfbd595f918 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
@@ -578,15 +578,18 @@ static struct task_struct *find_new_reaper(struct task_struct *father, | |||
578 | return thread; | 578 | return thread; |
579 | 579 | ||
580 | if (father->signal->has_child_subreaper) { | 580 | if (father->signal->has_child_subreaper) { |
581 | unsigned int ns_level = task_pid(father)->level; | ||
581 | /* | 582 | /* |
582 | * Find the first ->is_child_subreaper ancestor in our pid_ns. | 583 | * Find the first ->is_child_subreaper ancestor in our pid_ns. |
583 | * We start from father to ensure we can not look into another | 584 | * We can't check reaper != child_reaper to ensure we do not |
584 | * namespace, this is safe because all its threads are dead. | 585 | * cross the namespaces, the exiting parent could be injected |
586 | * by setns() + fork(). | ||
587 | * We check pid->level, this is slightly more efficient than | ||
588 | * task_active_pid_ns(reaper) != task_active_pid_ns(father). | ||
585 | */ | 589 | */ |
586 | for (reaper = father; | 590 | for (reaper = father->real_parent; |
587 | !same_thread_group(reaper, child_reaper); | 591 | task_pid(reaper)->level == ns_level; |
588 | reaper = reaper->real_parent) { | 592 | reaper = reaper->real_parent) { |
589 | /* call_usermodehelper() descendants need this check */ | ||
590 | if (reaper == &init_task) | 593 | if (reaper == &init_task) |
591 | break; | 594 | break; |
592 | if (!reaper->signal->is_child_subreaper) | 595 | if (!reaper->signal->is_child_subreaper) |