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
This commit is contained in:
Konstantin Belousov committed 2026-07-20 23:19:12 +03:00
1 parent aa588213d8
commit d3b7bbee92
3 files changed
+165 -2

No files matched your search

+16
View File
@@ -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);
+137 -2
View File
@@ -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;
/* FALLTHROUGH */
case PT_GET_CHILDREN:
if (p == curp)
break;
goto default_check;
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) {
+12
View File
@@ -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>