From 3c0ce6773b3fcf9d124c25fe222210f5587448b0 Mon Sep 17 00:00:00 2001 From: kib Date: Mon, 15 Aug 2016 21:44:06 +0000 Subject: [PATCH] MFC r303423: Force SIGSTOP to be the first signal reported after the attach. git-svn-id: svn://svn.freebsd.org/base/stable/10@304190 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- bin/ps/ps.1 | 1 + sys/kern/kern_exit.c | 7 +++++-- sys/kern/kern_fork.c | 6 ++---- sys/kern/kern_sig.c | 46 +++++++++++++++++++++++++++++++++--------- sys/kern/sys_process.c | 32 ++++++++++++++++++++++------- sys/sys/proc.h | 3 +++ 6 files changed, 73 insertions(+), 22 deletions(-) diff --git a/bin/ps/ps.1 b/bin/ps/ps.1 index 0ff3268e3..fdb4f1efa 100644 --- a/bin/ps/ps.1 +++ b/bin/ps/ps.1 @@ -347,6 +347,7 @@ the include file .In sys/proc.h : .Bl -column P2_INHERIT_PROTECTED 0x00000001 .It Dv "P2_INHERIT_PROTECTED" Ta No "0x00000001" Ta "New children get P_PROTECTED" +.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. diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index b6a697c8b..c36253000 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -520,9 +520,12 @@ exit1(struct thread *td, int rv) */ 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); diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 600cb02df..4d96840b7 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -1063,15 +1063,13 @@ fork_return(struct thread *td, struct trapframe *frame) * 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 { diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 5996c606e..2c37d7601 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -2499,14 +2499,26 @@ ptracestop(struct thread *td, int sig) 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_xstat = 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_xstat = 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); @@ -2657,7 +2669,20 @@ issignal(struct thread *td) 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); @@ -2674,7 +2699,7 @@ issignal(struct thread *td) 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. @@ -2772,6 +2797,8 @@ issignal(struct thread *td) 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_xstat = sig; PROC_SLOCK(p); @@ -2779,7 +2806,7 @@ issignal(struct thread *td) 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. @@ -2810,6 +2837,7 @@ issignal(struct thread *td) } sigqueue_delete(&td->td_sigqueue, sig); /* take the signal! */ sigqueue_delete(&p->p_sigqueue, sig); +next:; } /* NOTREACHED */ } diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 57b9fb7c6..c4533cef9 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -648,6 +648,17 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap) #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) { @@ -856,11 +867,9 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) 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; @@ -875,9 +884,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) * 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); } @@ -1045,6 +1052,17 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) p->p_pid, data); p->p_oppid = 0; p->p_ptevents = 0; + FOREACH_THREAD_IN_PROC(p, td3) { + if ((td3->td_dbgflags & TDB_FSTP) != 0) { + sigqueue_delete(&td3->td_sigqueue, + SIGSTOP); + } + td3->td_dbgflags &= ~(TDB_XSIG | TDB_FSTP); + } + 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); */ @@ -1065,7 +1083,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) 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, diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 34e292498..1b8bda5d1 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -398,6 +398,7 @@ do { \ #define TDB_CHILD 0x00000100 /* New child indicator for ptrace() */ #define TDB_BORN 0x00000200 /* New LWP indicator for ptrace() */ #define TDB_EXIT 0x00000400 /* Exiting LWP indicator for ptrace() */ +#define TDB_FSTP 0x00001000 /* The thread is PT_ATTACH leader */ /* * "Private" flags kept in td_pflags: @@ -678,6 +679,7 @@ struct proc { #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 */ @@ -930,6 +932,7 @@ void proc_linkup(struct proc *p, struct thread *td); 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); -- 2.42.0