diff options
author | Kyle Evans <kevans@FreeBSD.org> | 2025-06-19 15:31:58 +0000 |
---|---|---|
committer | Kyle Evans <kevans@FreeBSD.org> | 2025-06-19 15:32:04 +0000 |
commit | ee9895e10d266b7bb0a25677aee3debdc17ab4bf (patch) | |
tree | b673ce7accf54a5e9f4589cfa03ee8b1f584073e | |
parent | 5110a74afe5680882e3274d57da881511ea7c440 (diff) |
-rw-r--r-- | lib/libsys/ptrace.2 | 12 | ||||
-rw-r--r-- | lib/libsys/wait.2 | 8 | ||||
-rw-r--r-- | sys/kern/kern_sig.c | 5 | ||||
-rw-r--r-- | sys/kern/sys_process.c | 11 | ||||
-rw-r--r-- | tests/sys/kern/ptrace_test.c | 68 |
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()); } |