Index: sys/kern/kern_exit.c =================================================================== --- sys/kern/kern_exit.c +++ sys/kern/kern_exit.c @@ -355,6 +355,9 @@ */ PROC_LOCK(p); stopprofclock(p); + if ((p->p_flag & P_TRACED) != 0) { + p->p_flag2 |= P2_EXIT_TRACED; + } p->p_flag &= ~(P_TRACED | P_PPWAIT | P_PPTRACE); p->p_ptevents = 0; @@ -524,7 +527,7 @@ */ clear_orphan(q); q->p_flag &= ~(P_TRACED | P_STOPPED_TRACE); - q->p_flag2 &= ~P2_PTRACE_FSTP; + q->p_flag2 &= ~(P2_PTRACE_FSTP | P2_EXIT_TRACED); q->p_ptevents = 0; FOREACH_THREAD_IN_PROC(q, tdt) { tdt->td_dbgflags &= ~(TDB_SUSPEND | TDB_XSIG | @@ -996,11 +999,15 @@ switch (idtype) { case P_ALL: - if (p->p_procdesc != NULL) { - PROC_UNLOCK(p); - return (0); + if (p->p_procdesc == NULL || + (p->p_pptr == td->td_proc && + ((p->p_flag & P_TRACED) != 0 || + (p->p_flag2 & P2_EXIT_TRACED) != 0))) { + break; } - break; + + PROC_UNLOCK(p); + return (0); case P_PID: if (p->p_pid != (pid_t)id) { PROC_UNLOCK(p); @@ -1394,6 +1401,12 @@ clear_orphan(child); if ((child->p_flag & P_TRACED) != 0) { proc_add_orphan(child, child->p_pptr); + } else if ((child->p_flag2 & P2_EXIT_TRACED) != 0) { + /* + * If process is not traced and it's getting reparent, + * we don't allow any more to collect it exit status. + */ + child->p_flag2 &= ~P2_EXIT_TRACED; } child->p_pptr = parent; Index: sys/kern/sys_procdesc.c =================================================================== --- sys/kern/sys_procdesc.c +++ sys/kern/sys_procdesc.c @@ -60,7 +60,6 @@ * * Open questions: * - * - How to handle ptrace(2)? * - Will we want to add a pidtoprocdesc(2) system call to allow process * descriptors to be created for processes without pdfork(2)? */ Index: sys/kern/sys_process.c =================================================================== --- sys/kern/sys_process.c +++ sys/kern/sys_process.c @@ -1116,6 +1116,7 @@ * as an orphan of the debugger. */ p->p_flag &= ~(P_TRACED | P_WAITED); + p->p_flag2 &= ~P2_EXIT_TRACED; if (p->p_oppid != p->p_pptr->p_pid) { PROC_LOCK(p->p_pptr); sigqueue_take(p->p_ksi); Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -761,6 +761,7 @@ #define P2_ASLR_ENABLE 0x00000040 /* Force enable ASLR. */ #define P2_ASLR_DISABLE 0x00000080 /* Force disable ASLR. */ #define P2_ASLR_IGNSTART 0x00000100 /* Enable ASLR to consume sbrk area. */ +#define P2_EXIT_TRACED 0x00000200 /* The process exited while it was traced. */ /* Flags protected by proctree_lock, kept in p_treeflags. */ #define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */ Index: tests/sys/kern/ptrace_test.c =================================================================== --- tests/sys/kern/ptrace_test.c +++ tests/sys/kern/ptrace_test.c @@ -3959,6 +3959,103 @@ ATF_REQUIRE(errno == ECHILD); } +/* + * Ensure that traced processes created with pdfork(2) are visible to + * waitid(P_ALL). + */ +ATF_TC_WITHOUT_HEAD(ptrace__procdesc_wait_child); +ATF_TC_BODY(ptrace__procdesc_wait_child, tc) +{ + pid_t child, wpid; + int pd, status; + + child = pdfork(&pd, 0); + ATF_REQUIRE(child >= 0); + + if (child == 0) { + trace_me(); + (void)raise(SIGSTOP); + exit(0); + } + + wpid = waitpid(child, &status, 0); + ATF_REQUIRE(wpid == child); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); + + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); + + wpid = wait(&status); + ATF_REQUIRE(wpid == child); + ATF_REQUIRE(WIFSTOPPED(status)); + ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); + + ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); + + wpid = wait(&status); + ATF_REQUIRE(wpid == child); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 0); + + ATF_REQUIRE(close(pd) != -1); +} + +/* + * Ensure that traced processes created with pdfork(2) are not visible + * after returning to parent - waitid(P_ALL). + */ +ATF_TC_WITHOUT_HEAD(ptrace__procdesc_reparent_wait_child); +ATF_TC_BODY(ptrace__procdesc_reparent_wait_child, 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); + + /* Allow process to die. */ + ATF_REQUIRE(ptrace(PT_CONTINUE, traced, (caddr_t)1, 0) == 0); + wpid = waitpid(traced, &status, 0); + ATF_REQUIRE(wpid == traced); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE(WEXITSTATUS(status) == 0); + + /* Reparent back to the orginal process. */ + ATF_REQUIRE(close(pd) == 0); + exit(0); + } + + wpid = waitpid(debuger, &status, 0); + ATF_REQUIRE(wpid == debuger); + ATF_REQUIRE(WEXITSTATUS(status) == 0); + + /* + * We have a child but it has a process descriptori + * so we should not be able to collect it process. + */ + wpid = wait(&status); + printf("%d %d %d\n", wpid, debuger, traced); + ATF_REQUIRE(wpid == -1); + ATF_REQUIRE(errno == ECHILD); + + ATF_REQUIRE(close(pd) == 0); +} + ATF_TP_ADD_TCS(tp) { @@ -4021,6 +4118,8 @@ #endif ATF_TP_ADD_TC(tp, ptrace__PT_LWPINFO_stale_siginfo); ATF_TP_ADD_TC(tp, ptrace__proc_reparent); + ATF_TP_ADD_TC(tp, ptrace__procdesc_wait_child); + ATF_TP_ADD_TC(tp, ptrace__procdesc_reparent_wait_child); return (atf_no_error()); }