Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 20 Jul 2026 20:20:15 +0000
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: d3b7bbee9275 - main - ptrace(2): add PT_GET_CHILDREN
Message-ID:  <6a5e82ff.181b7.57447dc8@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by kib:

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

commit d3b7bbee9275d5a3c58a9e75d1fffc60a9333352
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-07-17 14:57:04 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-07-20 20:19:12 +0000

    ptrace(2): add PT_GET_CHILDREN
    
    Reviewed by:    markj
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D58315
---
 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 2ac0a33c3421..fc81ac4ca6c8 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -733,6 +733,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;
@@ -816,6 +817,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;
@@ -870,6 +879,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);
@@ -965,6 +981,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)
 {
@@ -995,7 +1069,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;
@@ -1021,6 +1096,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;
@@ -1132,8 +1208,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
@@ -1864,6 +1946,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..54669493ca75 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;
+	uint64ptr_t rsrv[3];
+};
+
 #ifdef _KERNEL
 
 #include <sys/proc.h>


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a5e82ff.181b7.57447dc8>