summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2016-08-15 21:44:06 +0000
committerkib <kib@FreeBSD.org>2016-08-15 21:44:06 +0000
commite7752c37e6f8d7e84a407ca0b274c42fc703fb45 (patch)
tree12493eed208368a32e993089822e5a392a6f35d4
parentad523ae767350f96407db84085c792c544327aa6 (diff)
downloadFreeBSD-src-e7752c37e6f8d7e84a407ca0b274c42fc703fb45.zip
FreeBSD-src-e7752c37e6f8d7e84a407ca0b274c42fc703fb45.tar.gz
MFC r303423:
Force SIGSTOP to be the first signal reported after the attach.
-rw-r--r--bin/ps/ps.11
-rw-r--r--sys/kern/kern_exit.c7
-rw-r--r--sys/kern/kern_fork.c6
-rw-r--r--sys/kern/kern_sig.c46
-rw-r--r--sys/kern/sys_process.c32
-rw-r--r--sys/sys/proc.h3
6 files changed, 73 insertions, 22 deletions
diff --git a/bin/ps/ps.1 b/bin/ps/ps.1
index 0ff3268..fdb4f1e 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 b6a697c..c362530 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 600cb02..4d96840 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 5996c60..2c37d76 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 57b9fb7..c4533ce 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 34e2924..1b8bda5 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);
OpenPOWER on IntegriCloud