git: c0addb9e88e7 - stable/15 - ptrace(2): add PT_GET_CHILDREN

Konstantin Belousov <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src,gmane.os.freebsd.devel.stable.scm
Message-ID <[email protected]>
The branch stable/15 has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=c0addb9e88e77fd26e6215c87ac98a0233ec7a46

commit c0addb9e88e77fd26e6215c87ac98a0233ec7a46
Author:     Konstantin Belousov <[email protected]>
AuthorDate: 2026-07-17 14:57:04 +0000
Commit:     Konstantin Belousov <[email protected]>
CommitDate: 2026-07-27 00:28:42 +0000

    ptrace(2): add PT_GET_CHILDREN
    
    (cherry picked from commit d3b7bbee9275d5a3c58a9e75d1fffc60a9333352)
---
 sys/compat/freebsd32/freebsd32_misc.c |  16 ++++
 sys/kern/sys_process.c                | 139 +++++++++++++++++++++++++++++++++-
 sys/sys/ptrace.h                      |  12 +++
 3 files changed, 165 insertions(+), 2 deletions(-)

diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index e07000015d54..e899c55ee5b1 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -1031,6 +1031,7 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
 		register_t args[nitems(td->td_sa.args)];
 		struct ptrace_sc_ret psr;
 		int ptevents;
+		struct ptrace_child *children;
 	} r;
 	union {
 		struct ptrace_io_desc32 piod;
@@ -1173,6 +1174,14 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
 			pscr_args[i] = pscr_args32[i];
 		r.sr.pscr_args = pscr_args;
 		break;
+	case PT_GET_CHILDREN:
+		if (uap->addr == NULL)
+			addr = NULL;
+		else if (uap->data < 0)
+			error = EINVAL;
+		else
+			addr = &r.children;
+		break;
 	case PTINTERNAL_FIRST ... PTINTERNAL_LAST:
 		error = EINVAL;
 		break;
@@ -1242,6 +1251,13 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
 		    offsetof(struct ptrace_sc_remote32, pscr_ret),
 		    sizeof(r32.psr));
 		break;
+	case PT_GET_CHILDREN:
+		if (uap->addr != 0) {
+			error = copyout(r.children, uap->addr,
+			    td->td_retval[0] * sizeof(struct ptrace_child));
+			free(r.children, M_TEMP);
+		}
+		break;
 	}
 
 	return (error);
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index 631b142d366a..b5cba235de47 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -700,6 +700,7 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
 		syscallarg_t args[nitems(td->td_sa.args)];
 		struct ptrace_sc_ret psr;
 		int ptevents;
+		struct ptrace_child *children;
 	} r;
 	syscallarg_t pscr_args[nitems(td->td_sa.args)];
 	void *addr;
@@ -783,6 +784,14 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
 			break;
 		r.sr.pscr_args = pscr_args;
 		break;
+	case PT_GET_CHILDREN:
+		if (uap->addr == NULL)
+			addr = NULL;
+		else if (uap->data < 0)
+			error = EINVAL;
+		else
+			addr = &r.children;
+		break;
 	case PTINTERNAL_FIRST ... PTINTERNAL_LAST:
 		error = EINVAL;
 		break;
@@ -837,6 +846,13 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
 		    offsetof(struct ptrace_sc_remote, pscr_ret),
 		    sizeof(r.sr.pscr_ret));
 		break;
+	case PT_GET_CHILDREN:
+		if (uap->addr != NULL) {
+			error = copyout(r.children, uap->addr,
+			    td->td_retval[0] * sizeof(struct ptrace_child));
+			free(r.children, M_TEMP);
+		}
+		break;
 	}
 
 	return (error);
@@ -921,6 +937,64 @@ proc_can_ptrace(struct thread *td, struct proc *p)
 	return (0);
 }
 
+static int
+ptrace_count_children(struct thread *td, struct proc *p, bool count_everything)
+{
+	struct proc *pp;
+	int error, num;
+
+	sx_assert(&proctree_lock, SX_LOCKED);
+	num = 0;
+	LIST_FOREACH(pp, &p->p_children, p_sibling) {
+		if (count_everything) {
+			error = 0;
+		} else {
+			PROC_LOCK(pp);
+			error = p_cansee(td, pp);
+			PROC_UNLOCK(pp);
+		}
+		if (error != 0)
+			continue;
+		num++;
+	}
+	LIST_FOREACH(pp, &p->p_orphans, p_orphan) {
+		if (count_everything) {
+			error = 0;
+		} else {
+			PROC_LOCK(pp);
+			error = p_cansee(td, pp);
+			PROC_UNLOCK(pp);
+		}
+		if (error != 0)
+			continue;
+		num++;
+	}
+	return (num);
+}
+
+static bool
+ptrace_report_child(struct thread *td, struct proc *p, struct proc *pp,
+    struct ptrace_child *ptc)
+{
+	sx_assert(&proctree_lock, SX_LOCKED);
+
+	PROC_LOCK(pp);
+	if (p_cansee(td, pp) != 0) {
+		PROC_UNLOCK(pp);
+		return (false);
+	}
+	ptc->pid = pp->p_pid;
+	if ((pp->p_flag & P_TRACED) != 0) {
+		ptc->flags |= PTCHLD_TRACED;
+		if (pp->p_pptr == td->td_proc)
+			ptc->flags |= PTCHLD_TRACED_BY_ME;
+	}
+	if ((pp->p_flag & P_WEXIT) != 0)
+		ptc->flags |= PTCHLD_EXITED;
+	PROC_UNLOCK(pp);
+	return (true);
+}
+
 static struct thread *
 ptrace_sel_coredump_thread(struct proc *p)
 {
@@ -951,7 +1025,8 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
 	struct ptrace_coredump *pc;
 	struct thr_coredump_req *tcq;
 	struct thr_syscall_req *tsr;
-	int error, num, tmp;
+	struct ptrace_child *children, *ptc;
+	int error, num, num1, tmp;
 	lwpid_t tid = 0, *buf;
 #ifdef COMPAT_FREEBSD32
 	int wrap32 = 0, safe = 0;
@@ -977,6 +1052,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
 	case PT_SET_EVENT_MASK:
 	case PT_DETACH:
 	case PT_GET_SC_ARGS:
+	case PT_GET_CHILDREN:
 		sx_xlock(&proctree_lock);
 		proctree_locked = true;
 		break;
@@ -1094,8 +1170,14 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
 		/* Allow thread to clear single step for itself */
 		if (td->td_tid == tid)
 			break;
+		goto default_check;
+
+	case PT_GET_CHILDREN:
+		if (p == curp)
+			break;
+		goto default_check;
 
-		/* FALLTHROUGH */
+default_check:
 	default:
 		/*
 		 * Check for ptrace eligibility before waiting for
@@ -1826,6 +1908,59 @@ coredump_cleanup_nofp:
 		free(tsr, M_TEMP);
 		break;
 
+	case PT_GET_CHILDREN:
+		PROC_UNLOCK(p);
+get_children_repeat:
+		/*
+		 * If addr != NULL, we should ignore p_cansee() to
+		 * allocate enough space for the children array,
+		 * because the process is allowed to change visibility
+		 * between loops.  But do not count children which
+		 * we cannot see when only returning the count, to
+		 * avoid a leak of information.
+		 */
+		num = ptrace_count_children(td, p, addr != NULL);
+
+		if (addr == NULL) {
+			td->td_retval[0] = num;
+			PROC_LOCK(p);
+			break;
+		}
+		if (data < num * sizeof(struct ptrace_child)) {
+			error = ENOMEM;
+			PROC_LOCK(p);
+			break;
+		}
+		sx_xunlock(&proctree_lock);
+		children = mallocarray(num, sizeof(struct ptrace_child),
+		    M_TEMP, M_WAITOK | M_ZERO);
+		sx_xlock(&proctree_lock);
+		num1 = ptrace_count_children(td, p, true);
+		if (num1 > num) {
+			free(children, M_TEMP);
+			goto get_children_repeat;
+		}
+		num = num1;
+		num1 = 0;
+		LIST_FOREACH(pp, &p->p_children, p_sibling) {
+			MPASS(num1 < num);
+			ptc = &children[num1];
+			if (ptrace_report_child(td, p, pp, ptc))
+				num1++;
+		}
+		LIST_FOREACH(pp, &p->p_orphans, p_orphan) {
+			MPASS(num1 < num);
+			ptc = &children[num1];
+			if (ptrace_report_child(td, p, pp, ptc)) {
+				num1++;
+				ptc->flags |= PTCHLD_ORPHAN;
+			}
+		}
+		*(struct ptrace_child **)addr = children;
+		td->td_retval[0] = num1;
+		PROC_LOCK(p);
+		break;
+
 	default:
 #ifdef __HAVE_PTRACE_MACHDEP
 		if (req >= PT_FIRSTMACH) {
diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h
index 2aa82ce07a1d..231f93915b3b 100644
--- a/sys/sys/ptrace.h
+++ b/sys/sys/ptrace.h
@@ -86,6 +86,7 @@
 #define	PT_SETREGSET	43	/* Set a target register set */
 #define	PT_SC_REMOTE	44	/* Execute a syscall */
 #define	PT_SET_SC_RET	45	/* Set (fake) syscall results */
+#define	PT_GET_CHILDREN	46	/* Report children */
 
 #define PT_FIRSTMACH    64	/* for machine-specific requests */
 #define	PT_LASTMACH     127
@@ -206,6 +207,17 @@ struct ptrace_sc_remote {
 	syscallarg_t	*pscr_args;
 };
 
+#define	PTCHLD_TRACED		0x00000001
+#define	PTCHLD_TRACED_BY_ME	0x00000002
+#define	PTCHLD_EXITED		0x00000004
+#define	PTCHLD_ORPHAN		0x00000008
+
+struct ptrace_child {
+	pid_t	pid;
+	int	flags;
+	uint64_t rsrv[3];
+};
+
 #ifdef _KERNEL
 
 #include <sys/proc.h>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.