aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2017-11-13 19:58:58 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2017-11-13 19:58:58 +0000
commitfeeaec18d47bdbcba15bd9214a34762376d08c04 (patch)
treefc3c558719314fc7918c5fcb53762ec5d900213d
parented617948c4f13ab969ef6f2934ac2902939ad487 (diff)
downloadsrc-feeaec18d47bdbcba15bd9214a34762376d08c04.tar.gz
src-feeaec18d47bdbcba15bd9214a34762376d08c04.zip
Only clear a pending thread event if one is pending.
This fixes a panic when attaching to an already-stopped process after r325028. While here, clean up a few other things in the control flow of the 'sendsig' section: - Only check for P_STOPPED_TRACE rather than either of P_STOPPED_SIG or P_STOPPED_TRACE for most ptrace requests. The signal handling code in kern_sig.c never sets just P_STOPPED_SIG for a traced process, so if P_STOPPED_SIG is stopped, P_STOPPED_TRACE should be set anyway. Remove a related debug printf. Assuming P_STOPPED_TRACE permits simplifications in the 'sendsig:' block. - Move the block to clear the pending thread state up into a new block conditional on P_STOPPED_TRACE and handle delivering pending signals to the reporting thread and clearing the reporting thread's state in this block. - Consolidate case to send a signal to the process in a single case for PT_ATTACH. The only case that could have been in the else before was a PT_ATTACH where P_STOPPED_SIG was not set, so both instances of kern_psignal() collapse down to just PT_ATTACH. Reported by: pho, mmel Reviewed by: kib Differential Revision: https://reviews.freebsd.org/D12837
Notes
Notes: svn path=/head/; revision=325771
-rw-r--r--sys/kern/sys_process.c61
1 files changed, 35 insertions, 26 deletions
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index 67aaff6389bc..c93667d0813b 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -869,19 +869,13 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
}
/* not currently stopped */
- if ((p->p_flag & (P_STOPPED_SIG | P_STOPPED_TRACE)) == 0 ||
+ if ((p->p_flag & P_STOPPED_TRACE) == 0 ||
p->p_suspcount != p->p_numthreads ||
(p->p_flag & P_WAITED) == 0) {
error = EBUSY;
goto fail;
}
- if ((p->p_flag & P_STOPPED_TRACE) == 0) {
- static int count = 0;
- if (count++ == 0)
- printf("P_STOPPED_TRACE not set.\n");
- }
-
/* OK */
break;
}
@@ -1130,24 +1124,32 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
}
sendsig:
- /*
+ /* proctree_locked is true for all but PT_KILL. */
+ if (proctree_locked) {
+ sx_xunlock(&proctree_lock);
+ proctree_locked = 0;
+ }
+
+ /*
* Clear the pending event for the thread that just
* reported its event (p_xthread). This may not be
* the thread passed to PT_CONTINUE, PT_STEP, etc. if
* the debugger is resuming a different thread.
+ *
+ * Deliver any pending signal via the reporting thread.
*/
- td2 = p->p_xthread;
- if (proctree_locked) {
- sx_xunlock(&proctree_lock);
- proctree_locked = 0;
+ if ((p->p_flag & P_STOPPED_TRACE) != 0) {
+ MPASS(p->p_xthread != NULL);
+ p->p_xthread->td_dbgflags &= ~TDB_XSIG;
+ p->p_xthread->td_xsig = data;
+ p->p_xthread = NULL;
+ p->p_xsig = data;
+ } else {
+ MPASS(p->p_xthread == NULL);
+ MPASS(req == PT_ATTACH);
}
- p->p_xsig = data;
- p->p_xthread = NULL;
- if ((p->p_flag & (P_STOPPED_SIG | P_STOPPED_TRACE)) != 0) {
- /* deliver or queue signal */
- td2->td_dbgflags &= ~TDB_XSIG;
- td2->td_xsig = data;
+ if ((p->p_flag & (P_STOPPED_SIG | P_STOPPED_TRACE)) != 0) {
/*
* P_WKILLED is insurance that a PT_KILL/SIGKILL always
* works immediately, even if another thread is
@@ -1162,21 +1164,28 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
FOREACH_THREAD_IN_PROC(p, td3)
td3->td_dbgflags &= ~TDB_SUSPEND;
}
+
/*
- * unsuspend all threads, to not let a thread run,
- * you should use PT_SUSPEND to suspend it before
- * continuing process.
+ * Unsuspend all threads. To leave a thread
+ * suspended, use PT_SUSPEND to suspend it
+ * before continuing the process.
*/
PROC_SLOCK(p);
p->p_flag &= ~(P_STOPPED_TRACE|P_STOPPED_SIG|P_WAITED);
thread_unsuspend(p);
PROC_SUNLOCK(p);
- if (req == PT_ATTACH)
- kern_psignal(p, data);
- } else {
- if (data)
- kern_psignal(p, data);
}
+
+ /*
+ * For requests other than PT_ATTACH, P_STOPPED_TRACE
+ * was set, so any pending signal in 'data' is
+ * delivered via the reporting thread (p_xthread).
+ * For PT_ATTACH the process is not yet stopped for
+ * tracing, so P_STOPPED_TRACE cannot be set and the
+ * SIGSTOP must be delivered to the process.
+ */
+ if (req == PT_ATTACH)
+ kern_psignal(p, data);
break;
case PT_WRITE_I: