Index: sys/kern/kern_exit.c =================================================================== --- sys/kern/kern_exit.c +++ sys/kern/kern_exit.c @@ -167,7 +167,7 @@ proc_id_clear(PROC_ID_REAP, p->p_reapsubtree); } -static void +void clear_orphan(struct proc *p) { struct proc *p1; @@ -1353,6 +1353,25 @@ goto loop; } +void +proc_add_orphan(struct proc *child, struct proc *parent) +{ + + sx_assert(&proctree_lock, SX_XLOCKED); + PROC_LOCK_ASSERT(child, MA_OWNED); + KASSERT((child->p_flag & P_TRACED) != 0, + ("proc_add_orphan: not traced")); + + if (LIST_EMPTY(&parent->p_orphans)) { + child->p_treeflag |= P_TREE_FIRST_ORPHAN; + LIST_INSERT_HEAD(&parent->p_orphans, child, p_orphan); + } else { + LIST_INSERT_AFTER(LIST_FIRST(&parent->p_orphans), + child, p_orphan); + } + child->p_treeflag |= P_TREE_ORPHANED; +} + /* * Make process 'parent' the new parent of process 'child'. * Must be called with an exclusive hold of proctree lock. @@ -1373,16 +1392,8 @@ LIST_INSERT_HEAD(&parent->p_children, child, p_sibling); clear_orphan(child); - if (child->p_flag & P_TRACED) { - if (LIST_EMPTY(&child->p_pptr->p_orphans)) { - child->p_treeflag |= P_TREE_FIRST_ORPHAN; - LIST_INSERT_HEAD(&child->p_pptr->p_orphans, child, - p_orphan); - } else { - LIST_INSERT_AFTER(LIST_FIRST(&child->p_pptr->p_orphans), - child, p_orphan); - } - child->p_treeflag |= P_TREE_ORPHANED; + if ((child->p_flag & P_TRACED) != 0) { + proc_add_orphan(child, child->p_pptr); } child->p_pptr = parent; Index: sys/kern/sys_procdesc.c =================================================================== --- sys/kern/sys_procdesc.c +++ sys/kern/sys_procdesc.c @@ -416,7 +416,13 @@ * terminate with prejudice. */ p->p_sigparent = SIGCHLD; - proc_reparent(p, p->p_reaper, true); + if ((p->p_flag & P_TRACED) == 0) { + proc_reparent(p, p->p_reaper, true); + } else { + clear_orphan(p); + p->p_oppid = p->p_reaper->p_pid; + proc_add_orphan(p, p->p_reaper); + } if ((pd->pd_flags & PDF_DAEMON) == 0) kern_psignal(p, SIGKILL); PROC_UNLOCK(p); Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -1068,12 +1068,14 @@ struct proc *proc_realparent(struct proc *child); void proc_reap(struct thread *td, struct proc *p, int *status, int options); void proc_reparent(struct proc *child, struct proc *newparent, bool set_oppid); +void proc_add_orphan(struct proc *child, struct proc *parent); void proc_set_traced(struct proc *p, bool stop); void proc_wkilled(struct proc *p); struct pstats *pstats_alloc(void); void pstats_fork(struct pstats *src, struct pstats *dst); void pstats_free(struct pstats *ps); void reaper_abandon_children(struct proc *p, bool exiting); +void clear_orphan(struct proc *p); int securelevel_ge(struct ucred *cr, int level); int securelevel_gt(struct ucred *cr, int level); void sess_hold(struct session *); Index: tests/sys/kern/ptrace_test.c =================================================================== --- tests/sys/kern/ptrace_test.c +++ tests/sys/kern/ptrace_test.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -3904,6 +3905,60 @@ ATF_REQUIRE(errno == ECHILD); } +/* + * Verify that when the process is traced that it isn't reparent + * to the init process when we close all process descriptors. + */ +ATF_TC(ptrace__proc_reparent); +ATF_TC_HEAD(ptrace__proc_reparent, tc) +{ + + atf_tc_set_md_var(tc, "timeout", "2"); +} +ATF_TC_BODY(ptrace__proc_reparent, tc) +{ + pid_t traced, debuger, wpid; + int pd, status; + + traced = pdfork(&pd, 0); + ATF_REQUIRE(traced >= 0); + if (traced == 0) { + raise(SIGSTOP); + exit(0); + } + ATF_REQUIRE(pd >= 0); + + debuger = fork(); + ATF_REQUIRE(debuger >= 0); + if (debuger == 0) { + /* The traced process is reparented to debuger. */ + ATF_REQUIRE(ptrace(PT_ATTACH, traced, 0, 0) == 0); + wpid = waitpid(traced, &status, 0); + ATF_REQUIRE(wpid == traced); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); + ATF_REQUIRE(close(pd) == 0); + ATF_REQUIRE(ptrace(PT_DETACH, traced, (caddr_t)1, 0) == 0); + + /* We closed pd so we should not have any child. */ + wpid = wait(&status); + ATF_REQUIRE(wpid == -1); + ATF_REQUIRE(errno == ECHILD); + + exit(0); + } + + ATF_REQUIRE(close(pd) == 0); + wpid = waitpid(debuger, &status, 0); + ATF_REQUIRE(wpid == debuger); + ATF_REQUIRE(WEXITSTATUS(status) == 0); + + /* Check if we still have any child. */ + wpid = wait(&status); + ATF_REQUIRE(wpid == -1); + ATF_REQUIRE(errno == ECHILD); +} + ATF_TP_ADD_TCS(tp) { @@ -3965,6 +4020,7 @@ ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_different_thread); #endif ATF_TP_ADD_TC(tp, ptrace__PT_LWPINFO_stale_siginfo); + ATF_TP_ADD_TC(tp, ptrace__proc_reparent); return (atf_no_error()); }