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>
