Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Oleg Nesterov and committed by
Eric W. Biederman
c6c70f44 1328c727

+8 -5
+8 -5
kernel/exit.c
··· 578 578 return thread; 579 579 580 580 if (father->signal->has_child_subreaper) { 581 + unsigned int ns_level = task_pid(father)->level; 581 582 /* 582 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 - * namespace, this is safe because all its threads are dead. 584 + * We can't check reaper != child_reaper to ensure we do not 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; 587 - !same_thread_group(reaper, child_reaper); 590 + for (reaper = father->real_parent; 591 + task_pid(reaper)->level == ns_level; 588 592 reaper = reaper->real_parent) { 589 - /* call_usermodehelper() descendants need this check */ 590 593 if (reaper == &init_task) 591 594 break; 592 595 if (!reaper->signal->is_child_subreaper)