mirror of
https://github.com/opnsense/src.git
synced 2026-06-12 10:10:24 -04:00
kern: send parent a SIGCHLD when the debugger has detached
The practical scenario that leads to this is porch(1) spawning some utility and sending it a SIGSTOP as a debugging aide. The user then attaches a debugger and walks through how some specific input is processed, then detaches to allow the script to continue. When ptrace is detached, the process resumes execution but the parent is never notified and may be stuck in wait(2) for it to continue or terminate. Other platforms seem to re-suspend the process after the debugger is detached, but neither behavior seems unreasonable. Just notifying the parent that the child has resumed is a relatively low-risk departure from our current behavior and had apparently been considered in the past, based on pre-existing comments. Move p_flag and p_xsig handling into childproc_continued(), as just sending the SIGCHLD here isn't really useful without P_CONTINUED set and the other caller already sets these up as well. Reviewed by: kib, markj Differential Revision: https://reviews.freebsd.org/D50917
This commit is contained in:
parent
5110a74afe
commit
ee9895e10d
5 changed files with 97 additions and 7 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue