Index: bin/ps/ps.1 =================================================================== --- bin/ps/ps.1 +++ bin/ps/ps.1 @@ -360,6 +360,7 @@ .It Dv "P2_NOTRACE" Ta No "0x00000002" Ta "No ptrace(2) attach or coredumps" .It Dv "P2_NOTRACE_EXEC" Ta No "0x00000004" Ta "Keep P2_NOPTRACE on exec(2)" .It Dv "P2_AST_SU" Ta No "0x00000008" Ta "Handles SU ast for kthreads" +.It Dv "P2_PTRACE_FSTP" Ta No "0x00000010" Ta "SIGSTOP from PT_ATTACH not yet handled" .El .It Cm label The MAC label of the process. Index: sys/kern/kern_exit.c =================================================================== --- sys/kern/kern_exit.c +++ sys/kern/kern_exit.c @@ -476,9 +476,12 @@ */ clear_orphan(q); q->p_flag &= ~(P_TRACED | P_STOPPED_TRACE); + q->p_flag2 &= ~P2_PTRACE_FSTP; q->p_ptevents = 0; - FOREACH_THREAD_IN_PROC(q, tdt) - tdt->td_dbgflags &= ~TDB_SUSPEND; + FOREACH_THREAD_IN_PROC(q, tdt) { + tdt->td_dbgflags &= ~(TDB_SUSPEND | TDB_XSIG | + TDB_FSTP); + } kern_psignal(q, SIGKILL); } PROC_UNLOCK(q); Index: sys/kern/kern_fork.c =================================================================== --- sys/kern/kern_fork.c +++ sys/kern/kern_fork.c @@ -1074,15 +1074,13 @@ * parent's children, do it now. */ dbg = p->p_pptr->p_pptr; - p->p_flag |= P_TRACED; - p->p_ptevents = PTRACE_DEFAULT; - p->p_oppid = p->p_pptr->p_pid; + proc_set_traced(p); CTR2(KTR_PTRACE, "fork_return: attaching to new child pid %d: oppid %d", p->p_pid, p->p_oppid); proc_reparent(p, dbg); sx_xunlock(&proctree_lock); - td->td_dbgflags |= TDB_CHILD | TDB_SCX; + td->td_dbgflags |= TDB_CHILD | TDB_SCX | TDB_FSTP; ptracestop(td, SIGSTOP); td->td_dbgflags &= ~(TDB_CHILD | TDB_SCX); } else { Index: sys/kern/kern_sig.c =================================================================== --- sys/kern/kern_sig.c +++ sys/kern/kern_sig.c @@ -2526,14 +2526,26 @@ PROC_SUNLOCK(p); return (sig); } + /* - * Just make wait() to work, the last stopped thread - * will win. + * Make wait(2) work. Ensure that right after the + * attach, the thread which was decided to become the + * leader of attach gets reported to the waiter. + * Otherwise, just avoid overwriting another thread's + * assignment to p_xthread. If another thread has + * already set p_xthread, the current thread will get + * a chance to report itself upon the next iteration. */ - p->p_xsig = sig; - p->p_xthread = td; - p->p_flag |= (P_STOPPED_SIG|P_STOPPED_TRACE); - sig_suspend_threads(td, p, 0); + if ((td->td_dbgflags & TDB_FSTP) != 0 || + ((p->p_flag & P2_PTRACE_FSTP) == 0 && + p->p_xthread == NULL)) { + p->p_xsig = sig; + p->p_xthread = td; + td->td_dbgflags &= ~TDB_FSTP; + p->p_flag2 &= ~P2_PTRACE_FSTP; + p->p_flag |= P_STOPPED_SIG | P_STOPPED_TRACE; + sig_suspend_threads(td, p, 0); + } if ((td->td_dbgflags & TDB_STOPATFORK) != 0) { td->td_dbgflags &= ~TDB_STOPATFORK; cv_broadcast(&p->p_dbgwait); @@ -2726,7 +2738,20 @@ SIG_STOPSIGMASK(sigpending); if (SIGISEMPTY(sigpending)) /* no signal to send */ return (0); - sig = sig_ffs(&sigpending); + if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED && + (p->p_flag2 & P2_PTRACE_FSTP) != 0 && + SIGISMEMBER(sigpending, SIGSTOP)) { + /* + * If debugger just attached, always consume + * SIGSTOP from ptrace(PT_ATTACH) first, to + * execute the debugger attach ritual in + * order. + */ + sig = SIGSTOP; + td->td_dbgflags |= TDB_FSTP; + } else { + sig = sig_ffs(&sigpending); + } if (p->p_stops & S_SIG) { mtx_unlock(&ps->ps_mtx); @@ -2743,7 +2768,7 @@ sigqueue_delete(&p->p_sigqueue, sig); continue; } - if (p->p_flag & P_TRACED && (p->p_flag & P_PPTRACE) == 0) { + if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED) { /* * If traced, always stop. * Remove old signal from queue before the stop. @@ -2846,6 +2871,8 @@ mtx_unlock(&ps->ps_mtx); WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, &p->p_mtx.lock_object, "Catching SIGSTOP"); + sigqueue_delete(&td->td_sigqueue, sig); + sigqueue_delete(&p->p_sigqueue, sig); p->p_flag |= P_STOPPED_SIG; p->p_xsig = sig; PROC_SLOCK(p); @@ -2853,7 +2880,7 @@ thread_suspend_switch(td, p); PROC_SUNLOCK(p); mtx_lock(&ps->ps_mtx); - break; + goto next; } else if (prop & SA_IGNORE) { /* * Except for SIGCONT, shouldn't get here. @@ -2884,6 +2911,7 @@ } sigqueue_delete(&td->td_sigqueue, sig); /* take the signal! */ sigqueue_delete(&p->p_sigqueue, sig); +next:; } /* NOTREACHED */ } Index: sys/kern/sys_process.c =================================================================== --- sys/kern/sys_process.c +++ sys/kern/sys_process.c @@ -692,6 +692,17 @@ #define PROC_WRITE(w, t, a) proc_write_ ## w (t, a) #endif +void +proc_set_traced(struct proc *p) +{ + + PROC_LOCK_ASSERT(p, MA_OWNED); + p->p_flag |= P_TRACED; + p->p_flag2 |= P2_PTRACE_FSTP; + p->p_ptevents = PTRACE_DEFAULT; + p->p_oppid = p->p_pptr->p_pid; +} + int kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) { @@ -899,11 +910,9 @@ switch (req) { case PT_TRACE_ME: /* set my trace flag and "owner" so it can read/write me */ - p->p_flag |= P_TRACED; - p->p_ptevents = PTRACE_DEFAULT; + proc_set_traced(p); if (p->p_flag & P_PPWAIT) p->p_flag |= P_PPTRACE; - p->p_oppid = p->p_pptr->p_pid; CTR1(KTR_PTRACE, "PT_TRACE_ME: pid %d", p->p_pid); break; @@ -918,9 +927,7 @@ * The old parent is remembered so we can put things back * on a "detach". */ - p->p_flag |= P_TRACED; - p->p_ptevents = PTRACE_DEFAULT; - p->p_oppid = p->p_pptr->p_pid; + proc_set_traced(p); if (p->p_pptr != td->td_proc) { proc_reparent(p, td->td_proc); } @@ -1088,6 +1095,17 @@ p->p_pid, data); p->p_oppid = 0; p->p_ptevents = 0; + FOREACH_THREAD_IN_PROC(p, td3) { + td3->td_dbgflags &= ~(TDB_XSIG | TDB_FSTP); + if ((p->p_flag2 & P2_PTRACE_FSTP) != 0) { + sigqueue_delete(&td3->td_sigqueue, + SIGSTOP); + } + } + if ((p->p_flag2 & P2_PTRACE_FSTP) != 0) { + sigqueue_delete(&p->p_sigqueue, SIGSTOP); + p->p_flag2 &= ~P2_PTRACE_FSTP; + } /* should we send SIGCHLD? */ /* childproc_continued(p); */ @@ -1108,7 +1126,7 @@ if (req == PT_DETACH) { FOREACH_THREAD_IN_PROC(p, td3) - td3->td_dbgflags &= ~TDB_SUSPEND; + td3->td_dbgflags &= ~TDB_SUSPEND; } /* * unsuspend all threads, to not let a thread run, Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -423,6 +423,7 @@ #define TDB_BORN 0x00000200 /* New LWP indicator for ptrace() */ #define TDB_EXIT 0x00000400 /* Exiting LWP indicator for ptrace() */ #define TDB_VFORK 0x00000800 /* vfork indicator for ptrace() */ +#define TDB_FSTP 0x00001000 /* The thread is PT_ATTACH leader */ /* * "Private" flags kept in td_pflags: @@ -713,6 +714,7 @@ #define P2_NOTRACE 0x00000002 /* No ptrace(2) attach or coredumps. */ #define P2_NOTRACE_EXEC 0x00000004 /* Keep P2_NOPTRACE on exec(2). */ #define P2_AST_SU 0x00000008 /* Handles SU ast for kthreads. */ +#define P2_PTRACE_FSTP 0x00000010 /* SIGSTOP from PT_ATTACH not yet handled. */ /* Flags protected by proctree_lock, kept in p_treeflags. */ #define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */ @@ -1003,6 +1005,7 @@ 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); +void proc_set_traced(struct proc *p); struct pstats *pstats_alloc(void); void pstats_fork(struct pstats *src, struct pstats *dst); void pstats_free(struct pstats *ps);