Index: sys/kern/kern_exit.c =================================================================== --- sys/kern/kern_exit.c (revision 225189) +++ sys/kern/kern_exit.c (working copy) @@ -680,7 +680,7 @@ wait4(struct thread *td, struct wait_args *uap) */ void proc_reap(struct thread *td, struct proc *p, int *status, int options, - struct rusage *rusage) + struct rusage *rusage, int child) { struct proc *q, *t; @@ -720,7 +720,6 @@ proc_reap(struct thread *td, struct proc *p, int * if (p->p_oppid && (t = pfind(p->p_oppid)) != NULL) { PROC_LOCK(p); proc_reparent(p, t); - p->p_pptr->p_dbg_child--; p->p_oppid = 0; PROC_UNLOCK(p); pksignal(t, SIGCHLD, p->p_ksi); @@ -738,7 +737,10 @@ proc_reap(struct thread *td, struct proc *p, int * sx_xlock(&allproc_lock); LIST_REMOVE(p, p_list); /* off zombproc */ sx_xunlock(&allproc_lock); - LIST_REMOVE(p, p_sibling); + if (child) + LIST_REMOVE(p, p_sibling); + else + LIST_REMOVE(p, p_orphan); leavepgrp(p); #ifdef PROCDESC if (p->p_procdesc != NULL) @@ -859,7 +861,7 @@ loop: nfound++; PROC_SLOCK(p); if (p->p_state == PRS_ZOMBIE) { - proc_reap(td, p, status, options, rusage); + proc_reap(td, p, status, options, rusage, 1); return (0); } if ((p->p_flag & P_STOPPED_SIG) && @@ -893,16 +895,47 @@ loop: if (status) *status = SIGCONT; + } + PROC_UNLOCK(p); + } + LIST_FOREACH(p, &q->p_orphans, p_orphan) { + PROC_LOCK(p); + if (pid != WAIT_ANY && + p->p_pid != pid && p->p_pgid != -pid) { + PROC_UNLOCK(p); + continue; + } + if (p_canwait(td, p)) { + PROC_UNLOCK(p); + continue; + } + + /* + * This special case handles a kthread spawned by linux_clone + * (see linux_misc.c). The linux_wait4 and linux_waitpid + * functions need to be able to distinguish between waiting + * on a process and waiting on a thread. It is a thread if + * p_sigparent is not SIGCHLD, and the WLINUXCLONE option + * signifies we want to wait for threads and not processes. + */ + if ((p->p_sigparent != SIGCHLD) ^ + ((options & WLINUXCLONE) != 0)) { + PROC_UNLOCK(p); + continue; + } + + nfound++; + PROC_SLOCK(p); + if (p->p_state == PRS_ZOMBIE) { + proc_reap(td, p, status, options, rusage, 0); return (0); } + PROC_SUNLOCK(p); PROC_UNLOCK(p); } if (nfound == 0) { sx_xunlock(&proctree_lock); - if (td->td_proc->p_dbg_child) - return (0); - else - return (ECHILD); + return (ECHILD); } if (options & WNOHANG) { sx_xunlock(&proctree_lock); @@ -932,6 +965,7 @@ proc_reparent(struct proc *child, struct proc *par #ifdef RACCT int locked; #endif + struct proc *p; sx_assert(&proctree_lock, SX_XLOCKED); PROC_LOCK_ASSERT(child, MA_OWNED); @@ -952,5 +986,17 @@ proc_reparent(struct proc *child, struct proc *par PROC_UNLOCK(child->p_pptr); LIST_REMOVE(child, p_sibling); LIST_INSERT_HEAD(&parent->p_children, child, p_sibling); + + LIST_FOREACH(p, &parent->p_orphans, p_orphan) { + if (p == child) { + LIST_REMOVE(child, p_orphan); + break; + } + } + if (child->p_flag & P_TRACED) { + LIST_INSERT_HEAD(&child->p_pptr->p_orphans, child, p_orphan); + PROC_UNLOCK(child); + PROC_LOCK(child); + } child->p_pptr = parent; } Index: sys/kern/kern_exec.c =================================================================== --- sys/kern/kern_exec.c (revision 225189) +++ sys/kern/kern_exec.c (working copy) @@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -895,16 +896,22 @@ exec_fail_dealloc: free(imgp->freepath, M_TEMP); if (error == 0) { + if ((p->p_flag & (P_TRACED | P_FOLLOWEXEC)) == + (P_TRACED | P_FOLLOWEXEC)) { + PROC_LOCK(p); + td->td_dbgflags |= TDB_EXEC; + PROC_UNLOCK(p); + } + /* + * Stop the process here if its stop event mask has + * the S_EXEC bit set. + */ + STOPEVENT(p, S_EXEC, 0); + PTRACESTOP_SC(p, td, S_PT_EXEC); PROC_LOCK(p); - td->td_dbgflags |= TDB_EXEC; + td->td_dbgflags &= ~TDB_EXEC; PROC_UNLOCK(p); - - /* - * Stop the process here if its stop event mask has - * the S_EXEC bit set. - */ - STOPEVENT(p, S_EXEC, 0); - goto done2; + goto done2; } exec_fail: Index: sys/kern/kern_fork.c =================================================================== --- sys/kern/kern_fork.c (revision 225189) +++ sys/kern/kern_fork.c (working copy) @@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -590,6 +591,7 @@ do_fork(struct thread *td, int flags, struct proc LIST_INSERT_AFTER(p1, p2, p_pglist); PGRP_UNLOCK(p1->p_pgrp); LIST_INIT(&p2->p_children); + LIST_INIT(&p2->p_orphans); callout_init(&p2->p_itcallout, CALLOUT_MPSAFE); @@ -702,7 +704,8 @@ do_fork(struct thread *td, int flags, struct proc * for runaway child. */ td->td_dbgflags |= TDB_FORK; - td->td_dbg_forked = p2->p_pid; + td2->td_dbgflags |= TDB_FORK; + td->td_dbg_forked = td2->td_dbg_forked = p2->p_pid; td2->td_dbgflags |= TDB_STOPATFORK; _PHOLD(p2); p2_held = 1; @@ -731,6 +734,13 @@ do_fork(struct thread *td, int flags, struct proc knote_fork(&p1->p_klist, p2->p_pid); SDT_PROBE(proc, kernel, , create, p2, p1, flags, 0, 0); + if (td->td_dbgflags & TDB_FORK) { + PTRACESTOP_SC(p1, td, S_PT_FORK); + PROC_LOCK(p1); + td->td_dbgflags &= ~TDB_FORK; + PROC_UNLOCK(p1); + } + /* * Wait until debugger is attached to child. */ @@ -797,6 +807,10 @@ fork1(struct thread *td, int flags, int pages, str p1 = td->td_proc; + /* Don't do vfork while being traced. */ + if (p1->p_flag & P_TRACED) + flags &= ~(RFPPWAIT | RFMEM); + /* * Here we don't create a new process, but we divorce * certain parts of a process from itself. @@ -1066,6 +1080,7 @@ fork_return(struct thread *td, struct trapframe *f proc_reparent(p, dbg); sx_xunlock(&proctree_lock); ptracestop(td, SIGSTOP); + td->td_dbgflags &= ~TDB_FORK; } else { /* * ... otherwise clear the request. Index: sys/kern/sys_process.c =================================================================== --- sys/kern/sys_process.c (revision 225189) +++ sys/kern/sys_process.c (working copy) @@ -660,6 +660,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, case PT_TO_SCX: case PT_SYSCALL: case PT_FOLLOW_FORK: + case PT_FOLLOW_EXEC: case PT_DETACH: sx_xlock(&proctree_lock); proctree_locked = 1; @@ -841,8 +842,6 @@ kern_ptrace(struct thread *td, int req, pid_t pid, p->p_flag |= P_TRACED; p->p_oppid = p->p_pptr->p_pid; if (p->p_pptr != td->td_proc) { - /* Remember that a child is being debugged(traced). */ - p->p_pptr->p_dbg_child++; proc_reparent(p, td->td_proc); } data = SIGSTOP; @@ -868,12 +867,28 @@ kern_ptrace(struct thread *td, int req, pid_t pid, break; case PT_FOLLOW_FORK: - if (data) + if (data) { p->p_flag |= P_FOLLOWFORK; - else + p->p_stops |= S_PT_FORK; + } + else { p->p_flag &= ~P_FOLLOWFORK; + p->p_stops &= ~S_PT_FORK; + } break; + case PT_FOLLOW_EXEC: + if (data) { + p->p_flag |= P_FOLLOWEXEC; + p->p_stops |= S_PT_EXEC; + } + else { + p->p_flag &= ~P_FOLLOWEXEC; + p->p_stops &= ~S_PT_EXEC; + } + break; + + case PT_STEP: case PT_CONTINUE: case PT_TO_SCE: @@ -931,12 +946,12 @@ kern_ptrace(struct thread *td, int req, pid_t pid, PROC_UNLOCK(pp); PROC_LOCK(p); proc_reparent(p, pp); - p->p_pptr->p_dbg_child--; if (pp == initproc) p->p_sigparent = SIGCHLD; } p->p_oppid = 0; - p->p_flag &= ~(P_TRACED | P_WAITED | P_FOLLOWFORK); + p->p_flag &= ~(P_TRACED | P_WAITED | P_FOLLOWFORK | + P_FOLLOWEXEC); /* should we send SIGCHLD? */ /* childproc_continued(p); */ Index: sys/kern/sys_procdesc.c =================================================================== --- sys/kern/sys_procdesc.c (revision 225189) +++ sys/kern/sys_procdesc.c (working copy) @@ -367,7 +367,7 @@ procdesc_close(struct file *fp, struct thread *td) * procdesc_reap(). */ PROC_SLOCK(p); - proc_reap(curthread, p, NULL, 0, NULL); + proc_reap(curthread, p, NULL, 0, NULL, 0); } else { /* * If the process is not yet dead, we need to kill it, but we Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h (revision 225189) +++ sys/sys/proc.h (working copy) @@ -504,8 +504,6 @@ struct proc { /* The following fields are all zeroed upon creation in fork. */ #define p_startzero p_oppid pid_t p_oppid; /* (c + e) Save ppid in ptrace. XXX */ - int p_dbg_child; /* (c + e) # of debugged children in - ptrace. */ struct vmspace *p_vmspace; /* (b) Address space. */ u_int p_swtick; /* (c) Tick when swapped in or out. */ struct itimerval p_realtimer; /* (c) Alarm timer. */ @@ -573,6 +571,8 @@ struct proc { after fork. */ uint64_t p_prev_runtime; /* (c) Resource usage accounting. */ struct racct *p_racct; /* (b) Resource accounting. */ + LIST_ENTRY(proc) p_orphan; /* (e) List of orphan processes. */ + LIST_HEAD(, proc) p_orphans; /* (e) Pointer to list of orphans. */ }; #define p_session p_pgrp->pg_session @@ -616,6 +616,7 @@ struct proc { #define P_INMEM 0x10000000 /* Loaded into memory. */ #define P_SWAPPINGOUT 0x20000000 /* Process is being swapped out. */ #define P_SWAPPINGIN 0x40000000 /* Process is being swapped in. */ +#define P_FOLLOWEXEC 0x80000000 /* Notify debugger of exec events. */ #define P_STOPPED (P_STOPPED_SIG|P_STOPPED_SINGLE|P_STOPPED_TRACE) #define P_SHOULDSTOP(p) ((p)->p_flag & P_STOPPED) @@ -847,7 +848,7 @@ void procinit(void); void proc_linkup0(struct proc *p, struct thread *td); void proc_linkup(struct proc *p, struct thread *td); void proc_reap(struct thread *td, struct proc *p, int *status, int options, - struct rusage *rusage); + struct rusage *rusage, int child); void proc_reparent(struct proc *child, struct proc *newparent); struct pstats *pstats_alloc(void); void pstats_fork(struct pstats *src, struct pstats *dst); Index: sys/sys/ptrace.h =================================================================== --- sys/sys/ptrace.h (revision 225189) +++ sys/sys/ptrace.h (working copy) @@ -64,6 +64,7 @@ #define PT_SYSCALL 22 #define PT_FOLLOW_FORK 23 +#define PT_FOLLOW_EXEC 24 #define PT_GETREGS 33 /* get general-purpose registers */ #define PT_SETREGS 34 /* set general-purpose registers */ @@ -106,7 +107,7 @@ struct ptrace_lwpinfo { #define PL_FLAG_SCX 0x08 /* syscall leave point */ #define PL_FLAG_EXEC 0x10 /* exec(2) succeeded */ #define PL_FLAG_SI 0x20 /* siginfo is valid */ -#define PL_FLAG_FORKED 0x40 /* new child */ +#define PL_FLAG_FORKED 0x40 /* new child from fork() */ sigset_t pl_sigmask; /* LWP signal mask */ sigset_t pl_siglist; /* LWP pending signal */ struct __siginfo pl_siginfo; /* siginfo for signal */ @@ -142,6 +143,8 @@ struct ptrace_vm_entry { */ #define S_PT_SCE 0x000010000 #define S_PT_SCX 0x000020000 +#define S_PT_FORK 0x000040000 +#define S_PT_EXEC 0x000080000 int ptrace_set_pc(struct thread *_td, unsigned long _addr); int ptrace_single_step(struct thread *_td);