aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyle Evans <kevans@FreeBSD.org>2025-06-19 15:31:58 +0000
committerKyle Evans <kevans@FreeBSD.org>2025-06-19 15:32:04 +0000
commitee9895e10d266b7bb0a25677aee3debdc17ab4bf (patch)
treeb673ce7accf54a5e9f4589cfa03ee8b1f584073e
parent5110a74afe5680882e3274d57da881511ea7c440 (diff)
-rw-r--r--lib/libsys/ptrace.212
-rw-r--r--lib/libsys/wait.28
-rw-r--r--sys/kern/kern_sig.c5
-rw-r--r--sys/kern/sys_process.c11
-rw-r--r--tests/sys/kern/ptrace_test.c68
5 files changed, 97 insertions, 7 deletions
diff --git a/lib/libsys/ptrace.2 b/lib/libsys/ptrace.2
index 9b789a0e45b3..7aa24a3f820b 100644
--- a/lib/libsys/ptrace.2
+++ b/lib/libsys/ptrace.2
@@ -1,7 +1,7 @@
.\" $NetBSD: ptrace.2,v 1.2 1995/02/27 12:35:37 cgd Exp $
.\"
.\" This file is in the public domain.
-.Dd August 18, 2023
+.Dd June 19, 2025
.Dt PTRACE 2
.Os
.Sh NAME
@@ -473,6 +473,16 @@ This request is like PT_CONTINUE, except that it does not allow
specifying an alternate place to continue execution, and after it
succeeds, the traced process is no longer traced and continues
execution normally.
+.Pp
+The parent of the traced process will be sent a
+.Dv SIGCHLD
+to indicate that the process has continued from a stopped state regardless of
+whether the process was in a stopped state prior to the corresponding
+.Dv PT_ATTACH
+request.
+A
+.Xr wait 2
+for the traced process would indicate that it had been continued.
.It Dv PT_GETREGS
This request reads the traced process's machine registers into the
.Do
diff --git a/lib/libsys/wait.2 b/lib/libsys/wait.2
index 3c649f3dfa77..eeddf77aeac7 100644
--- a/lib/libsys/wait.2
+++ b/lib/libsys/wait.2
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 27, 2024
+.Dd June 19, 2025
.Dt WAIT 2
.Os
.Sh NAME
@@ -273,6 +273,10 @@ Report the status of selected processes that
have continued from a job control stop by receiving a
.Dv SIGCONT
signal.
+.Xr ptrace 2
+can also cause a process to be continued, when a
+.Dv PT_DETACH
+request is issued to detach the debugger.
.It Dv WNOHANG
Do not block when
there are no processes wishing to report status.
@@ -450,7 +454,7 @@ value:
.Bl -tag -width Ds
.It Fn WIFCONTINUED status
True if the process has not terminated, and
-has continued after a job control stop.
+has continued after a job control stop or detach of a debugger.
This macro can be true only if the wait call specified the
.Dv WCONTINUED
option.
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index c972d0620ab2..4565abc4b540 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -2461,8 +2461,6 @@ tdsendsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi)
PROC_SLOCK(p);
if (p->p_numthreads == p->p_suspcount) {
PROC_SUNLOCK(p);
- p->p_flag |= P_CONTINUED;
- p->p_xsig = SIGCONT;
PROC_LOCK(p->p_pptr);
childproc_continued(p);
PROC_UNLOCK(p->p_pptr);
@@ -3778,6 +3776,9 @@ childproc_stopped(struct proc *p, int reason)
void
childproc_continued(struct proc *p)
{
+ PROC_LOCK_ASSERT(p, MA_OWNED);
+ p->p_flag |= P_CONTINUED;
+ p->p_xsig = SIGCONT;
childproc_jobstate(p, CLD_CONTINUED, SIGCONT);
}
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index 70f48adc2be8..c67996ad7df1 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -1324,8 +1324,15 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
p->p_flag2 &= ~P2_PTRACE_FSTP;
}
- /* should we send SIGCHLD? */
- /* childproc_continued(p); */
+ /*
+ * Send SIGCHLD and wakeup the parent as needed. It
+ * may be the case that they had stopped the child
+ * before it got ptraced, and now they're in the middle
+ * of a wait(2) for it to continue.
+ */
+ PROC_LOCK(p->p_pptr);
+ childproc_continued(p);
+ PROC_UNLOCK(p->p_pptr);
break;
}
diff --git a/tests/sys/kern/ptrace_test.c b/tests/sys/kern/ptrace_test.c
index c5160d066af7..d36dfe951e20 100644
--- a/tests/sys/kern/ptrace_test.c
+++ b/tests/sys/kern/ptrace_test.c
@@ -4523,6 +4523,73 @@ ATF_TC_BODY(ptrace__PT_ATTACH_no_EINTR, tc)
ATF_REQUIRE(timespeccmp(&shm->sleep_time, &twelve_sec, <=));
}
+ATF_TC_WITHOUT_HEAD(ptrace__PT_DETACH_continued);
+ATF_TC_BODY(ptrace__PT_DETACH_continued, tc)
+{
+ char buf[256];
+ pid_t debuggee, debugger;
+ int dpipe[2] = {-1, -1}, status;
+
+ /* Setup the debuggee's pipe, which we'll use to let it terminate. */
+ ATF_REQUIRE(pipe(dpipe) == 0);
+ ATF_REQUIRE((debuggee = fork()) != -1);
+
+ if (debuggee == 0) {
+ ssize_t readsz;
+
+ /*
+ * The debuggee will just absorb everything until the parent
+ * closes it. In the process, we expect it to get SIGSTOP'd,
+ * then ptrace(2)d and finally, it should resume after we detach
+ * and the parent will be notified.
+ */
+ close(dpipe[1]);
+ while ((readsz = read(dpipe[0], buf, sizeof(buf))) != 0) {
+ if (readsz > 0 || errno == EINTR)
+ continue;
+ _exit(1);
+ }
+
+ _exit(0);
+ }
+
+ close(dpipe[0]);
+
+ ATF_REQUIRE(kill(debuggee, SIGSTOP) == 0);
+ REQUIRE_EQ(waitpid(debuggee, &status, WUNTRACED), debuggee);
+ ATF_REQUIRE(WIFSTOPPED(status));
+
+ /* Child is stopped, enter the debugger to attach/detach. */
+ ATF_REQUIRE((debugger = fork()) != -1);
+ if (debugger == 0) {
+ REQUIRE_EQ(ptrace(PT_ATTACH, debuggee, 0, 0), 0);
+ REQUIRE_EQ(waitpid(debuggee, &status, 0), debuggee);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+
+ REQUIRE_EQ(ptrace(PT_DETACH, debuggee, 0, 0), 0);
+ _exit(0);
+ }
+
+ REQUIRE_EQ(waitpid(debugger, &status, 0), debugger);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ REQUIRE_EQ(waitpid(debuggee, &status, WCONTINUED), debuggee);
+ ATF_REQUIRE(WIFCONTINUED(status));
+
+ /*
+ * Closing the pipe will trigger the debuggee to exit now that the
+ * child has resumed following detach.
+ */
+ close(dpipe[1]);
+
+ REQUIRE_EQ(waitpid(debuggee, &status, 0), debuggee);
+ ATF_REQUIRE(WIFEXITED(status));
+ REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_trace_me);
@@ -4592,6 +4659,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, ptrace__PT_SC_REMOTE_getpid);
ATF_TP_ADD_TC(tp, ptrace__reap_kill_stopped);
ATF_TP_ADD_TC(tp, ptrace__PT_ATTACH_no_EINTR);
+ ATF_TP_ADD_TC(tp, ptrace__PT_DETACH_continued);
return (atf_no_error());
}