git: 9cea869940f1 - main - ptrace: split sys_ptrace()/freebsd32_ptrace()

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Fri, 28 Aug 2026 12:05:06 UTC
The branch main has been updated by kib:

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

commit 9cea869940f197d558f640b6851d44a213be9e8a
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-07-19 21:33:54 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-08-28 11:52:32 +0000

    ptrace: split sys_ptrace()/freebsd32_ptrace()
    
    The code to handle copyin and copyout of the structured parameters is
    moved into the helpers.
    
    Reviewed by:    markj
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D58586
---
 sys/compat/freebsd32/freebsd32_misc.c | 129 +++++++++++++++++-----------------
 sys/kern/sys_process.c                | 114 ++++++++++++++++--------------
 sys/sys/ptrace.h                      |   2 +
 3 files changed, 128 insertions(+), 117 deletions(-)

diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index 08e15140317d..5b5a13a16de1 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -1015,8 +1015,9 @@ ptrace_sc_ret32_to_ret(const struct ptrace_sc_ret32 *psr32,
 	psr->sr_error = psr32->sr_error;
 }
 
-int
-freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
+static int
+freebsd32_ptrace_useraction(struct thread *td, int req, pid_t pid,
+    void *uaddr, int udata)
 {
 	union {
 		struct ptrace_io_desc piod;
@@ -1050,39 +1051,32 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
 
 	if (!allow_ptrace)
 		return (ENOSYS);
-	error = 0;
 
-	AUDIT_ARG_PID(uap->pid);
-	AUDIT_ARG_CMD(uap->req);
-	AUDIT_ARG_VALUE(uap->data);
+	error = 0;
 	addr = &r;
-	data = uap->data;
-	switch (uap->req) {
+	data = udata;
+
+	switch (req) {
 	case PT_GET_EVENT_MASK:
 	case PT_GET_SC_ARGS:
 	case PT_GET_SC_RET:
 		break;
 	case PT_SET_SC_RET:
-		if (uap->data != sizeof(r32.psr)) {
-			error = EINVAL;
-		} else {
-			error = copyin(uap->addr, &r32.psr, sizeof(r32.psr));
-			if (error == 0)
-				ptrace_sc_ret32_to_ret(&r32.psr, &r.psr);
-		}
+		error = udata != sizeof(r32.psr) ? EINVAL :
+		    copyin(uaddr, &r32.psr, sizeof(r32.psr));
+		if (error == 0)
+			ptrace_sc_ret32_to_ret(&r32.psr, &r.psr);
 		break;
 	case PT_LWPINFO:
-		if (uap->data > sizeof(r32.pl))
+		if (udata > sizeof(r32.pl))
 			return (EINVAL);
-
 		/*
 		 * Pass size of native structure in 'data'.  Truncate
 		 * if necessary to avoid siginfo.
 		 */
-		data = sizeof(r.pl);
-		if (uap->data < offsetof(struct ptrace_lwpinfo32, pl_siginfo) +
-		    sizeof(struct __siginfo32))
-			data = offsetof(struct ptrace_lwpinfo, pl_siginfo);
+		data = udata < offsetof(struct ptrace_lwpinfo32,
+		    pl_siginfo) + sizeof(struct __siginfo32) ?
+		    offsetof(struct ptrace_lwpinfo, pl_siginfo) :  sizeof(r.pl);
 		break;
 	case PT_GETREGS:
 		bzero(&r.reg, sizeof(r.reg));
@@ -1094,17 +1088,17 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
 		bzero(&r.dbreg, sizeof(r.dbreg));
 		break;
 	case PT_SETREGS:
-		error = copyin(uap->addr, &r.reg, sizeof(r.reg));
+		error = copyin(uaddr, &r.reg, sizeof(r.reg));
 		break;
 	case PT_SETFPREGS:
-		error = copyin(uap->addr, &r.fpreg, sizeof(r.fpreg));
+		error = copyin(uaddr, &r.fpreg, sizeof(r.fpreg));
 		break;
 	case PT_SETDBREGS:
-		error = copyin(uap->addr, &r.dbreg, sizeof(r.dbreg));
+		error = copyin(uaddr, &r.dbreg, sizeof(r.dbreg));
 		break;
 	case PT_GETREGSET:
 	case PT_SETREGSET:
-		error = copyin(uap->addr, &r32.vec, sizeof(r32.vec));
+		error = copyin(uaddr, &r32.vec, sizeof(r32.vec));
 		if (error != 0)
 			break;
 
@@ -1112,14 +1106,12 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
 		r.vec.iov_base = PTRIN(r32.vec.iov_base);
 		break;
 	case PT_SET_EVENT_MASK:
-		if (uap->data != sizeof(r.ptevents))
-			error = EINVAL;
-		else
-			error = copyin(uap->addr, &r.ptevents, uap->data);
+		error = udata != sizeof(r.ptevents) ? EINVAL :
+		    copyin(uaddr, &r.ptevents, udata);
 		break;
 	case PT_IO:
-		error = copyin(uap->addr, &r32.piod, sizeof(r32.piod));
-		if (error)
+		error = copyin(uaddr, &r32.piod, sizeof(r32.piod));
+		if (error != 0)
 			break;
 		CP(r32.piod, r.piod, piod_op);
 		PTRIN_CP(r32.piod, r.piod, piod_offs);
@@ -1127,8 +1119,8 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
 		CP(r32.piod, r.piod, piod_len);
 		break;
 	case PT_VM_ENTRY:
-		error = copyin(uap->addr, &r32.pve, sizeof(r32.pve));
-		if (error)
+		error = copyin(uaddr, &r32.pve, sizeof(r32.pve));
+		if (error != 0)
 			break;
 
 		CP(r32.pve, r.pve, pve_entry);
@@ -1143,21 +1135,18 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
 		PTRIN_CP(r32.pve, r.pve, pve_path);
 		break;
 	case PT_COREDUMP:
-		if (uap->data != sizeof(r32.pc))
-			error = EINVAL;
-		else
-			error = copyin(uap->addr, &r32.pc, uap->data);
+		error = udata != sizeof(r32.pc) ? EINVAL :
+		    copyin(uaddr, &r32.pc, udata);
+		if (error != 0)
+			break;
 		CP(r32.pc, r.pc, pc_fd);
 		CP(r32.pc, r.pc, pc_flags);
 		r.pc.pc_limit = PAIR32TO64(off_t, r32.pc.pc_limit);
 		data = sizeof(r.pc);
 		break;
 	case PT_SC_REMOTE:
-		if (uap->data != sizeof(r32.sr)) {
-			error = EINVAL;
-			break;
-		}
-		error = copyin(uap->addr, &r32.sr, uap->data);
+		error = udata != sizeof(r32.sr) ? EINVAL :
+		    copyin(uaddr, &r32.sr, udata);
 		if (error != 0)
 			break;
 		CP(r32.sr, r.sr, pscr_syscall);
@@ -1175,9 +1164,9 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
 		r.sr.pscr_args = pscr_args;
 		break;
 	case PT_GET_CHILDREN:
-		if (uap->addr == NULL)
+		if (uaddr == NULL)
 			addr = NULL;
-		else if (uap->data < 0)
+		else if (udata < 0)
 			error = EINVAL;
 		else
 			addr = &r.children;
@@ -1186,17 +1175,17 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
 		error = EINVAL;
 		break;
 	default:
-		addr = uap->addr;
+		addr = uaddr;
 		break;
 	}
-	if (error)
+	if (error != 0)
 		return (error);
 
-	error = kern_ptrace(td, uap->req, uap->pid, addr, data);
-	if (error)
+	error = ptrace_action(td, req, pid, addr, data);
+	if (error != 0)
 		return (error);
 
-	switch (uap->req) {
+	switch (req) {
 	case PT_VM_ENTRY:
 		CP(r.pve, r32.pve, pve_entry);
 		CP(r.pve, r32.pve, pve_timestamp);
@@ -1207,53 +1196,51 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
 		CP(r.pve, r32.pve, pve_pathlen);
 		CP(r.pve, r32.pve, pve_fileid);
 		CP(r.pve, r32.pve, pve_fsid);
-		error = copyout(&r32.pve, uap->addr, sizeof(r32.pve));
+		error = copyout(&r32.pve, uaddr, sizeof(r32.pve));
 		break;
 	case PT_IO:
 		CP(r.piod, r32.piod, piod_len);
-		error = copyout(&r32.piod, uap->addr, sizeof(r32.piod));
+		error = copyout(&r32.piod, uaddr, sizeof(r32.piod));
 		break;
 	case PT_GETREGS:
-		error = copyout(&r.reg, uap->addr, sizeof(r.reg));
+		error = copyout(&r.reg, uaddr, sizeof(r.reg));
 		break;
 	case PT_GETFPREGS:
-		error = copyout(&r.fpreg, uap->addr, sizeof(r.fpreg));
+		error = copyout(&r.fpreg, uaddr, sizeof(r.fpreg));
 		break;
 	case PT_GETDBREGS:
-		error = copyout(&r.dbreg, uap->addr, sizeof(r.dbreg));
+		error = copyout(&r.dbreg, uaddr, sizeof(r.dbreg));
 		break;
 	case PT_GETREGSET:
 		r32.vec.iov_len = r.vec.iov_len;
-		error = copyout(&r32.vec, uap->addr, sizeof(r32.vec));
+		error = copyout(&r32.vec, uaddr, sizeof(r32.vec));
 		break;
 	case PT_GET_EVENT_MASK:
 		/* NB: The size in uap->data is validated in kern_ptrace(). */
-		error = copyout(&r.ptevents, uap->addr, uap->data);
+		error = copyout(&r.ptevents, uaddr, udata);
 		break;
 	case PT_LWPINFO:
 		ptrace_lwpinfo_to32(&r.pl, &r32.pl);
-		error = copyout(&r32.pl, uap->addr, uap->data);
+		error = copyout(&r32.pl, uaddr, udata);
 		break;
 	case PT_GET_SC_ARGS:
 		for (i = 0; i < nitems(r.args); i++)
 			r32.args[i] = (uint32_t)r.args[i];
-		error = copyout(r32.args, uap->addr, MIN(uap->data,
-		    sizeof(r32.args)));
+		error = copyout(r32.args, uaddr, MIN(udata, sizeof(r32.args)));
 		break;
 	case PT_GET_SC_RET:
 		ptrace_sc_ret_to32(&r.psr, &r32.psr);
-		error = copyout(&r32.psr, uap->addr, MIN(uap->data,
-		    sizeof(r32.psr)));
+		error = copyout(&r32.psr, uaddr, MIN(udata, sizeof(r32.psr)));
 		break;
 	case PT_SC_REMOTE:
 		ptrace_sc_ret_to32(&r.sr.pscr_ret, &r32.sr.pscr_ret);
-		error = copyout(&r32.sr.pscr_ret, uap->addr +
+		error = copyout(&r32.sr.pscr_ret, (char *)uaddr +
 		    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,
+		if (uaddr != NULL) {
+			error = copyout(r.children, uaddr,
 			    td->td_retval[0] * sizeof(struct ptrace_child));
 			free(r.children, M_TEMP);
 		}
@@ -1263,6 +1250,20 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
 	return (error);
 }
 
+int
+freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap)
+{
+	int error;
+
+	AUDIT_ARG_PID(uap->pid);
+	AUDIT_ARG_CMD(uap->req);
+	AUDIT_ARG_VALUE(uap->data);
+
+	error = freebsd32_ptrace_useraction(td, uap->req, uap->pid,
+	    uap->addr, uap->data);
+	return (error);
+}
+
 int
 freebsd32_copyinuio(const struct iovec32 *iovp, u_int iovcnt, struct uio **uiop)
 {
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index 46848ca150c0..39196561928c 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -714,8 +714,9 @@ struct ptrace_args {
 };
 #endif
 
-int
-sys_ptrace(struct thread *td, struct ptrace_args *uap)
+static int
+ptrace_useraction(struct thread *td, int req, pid_t pid, void *uaddr,
+    int udata)
 {
 	/*
 	 * XXX this obfuscation is to reduce stack usage, but the register
@@ -742,23 +743,19 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
 
 	if (!allow_ptrace)
 		return (ENOSYS);
-	error = 0;
 
-	AUDIT_ARG_PID(uap->pid);
-	AUDIT_ARG_CMD(uap->req);
-	AUDIT_ARG_VALUE(uap->data);
+	error = 0;
 	addr = &r;
-	switch (uap->req) {
+
+	switch (req) {
 	case PT_GET_EVENT_MASK:
 	case PT_LWPINFO:
 	case PT_GET_SC_ARGS:
 	case PT_GET_SC_RET:
 		break;
 	case PT_SET_SC_RET:
-		if (uap->data != sizeof(r.psr))
-			error = EINVAL;
-		else
-			error = copyin(uap->addr, &r.psr, sizeof(r.psr));
+		error = udata != sizeof(r.psr) ? EINVAL :
+		    copyin(uaddr, &r.psr, sizeof(r.psr));
 		break;
 	case PT_GETREGS:
 		bzero(&r.reg, sizeof(r.reg));
@@ -771,41 +768,34 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
 		break;
 	case PT_GETREGSET:
 	case PT_SETREGSET:
-		error = copyin(uap->addr, &r.vec, sizeof(r.vec));
+		error = copyin(uaddr, &r.vec, sizeof(r.vec));
 		break;
 	case PT_SETREGS:
-		error = copyin(uap->addr, &r.reg, sizeof(r.reg));
+		error = copyin(uaddr, &r.reg, sizeof(r.reg));
 		break;
 	case PT_SETFPREGS:
-		error = copyin(uap->addr, &r.fpreg, sizeof(r.fpreg));
+		error = copyin(uaddr, &r.fpreg, sizeof(r.fpreg));
 		break;
 	case PT_SETDBREGS:
-		error = copyin(uap->addr, &r.dbreg, sizeof(r.dbreg));
+		error = copyin(uaddr, &r.dbreg, sizeof(r.dbreg));
 		break;
 	case PT_SET_EVENT_MASK:
-		if (uap->data != sizeof(r.ptevents))
-			error = EINVAL;
-		else
-			error = copyin(uap->addr, &r.ptevents, uap->data);
+		error = udata != sizeof(r.ptevents) ? EINVAL :
+		    copyin(uaddr, &r.ptevents, udata);
 		break;
 	case PT_IO:
-		error = copyin(uap->addr, &r.piod, sizeof(r.piod));
+		error = copyin(uaddr, &r.piod, sizeof(r.piod));
 		break;
 	case PT_VM_ENTRY:
-		error = copyin(uap->addr, &r.pve, sizeof(r.pve));
+		error = copyin(uaddr, &r.pve, sizeof(r.pve));
 		break;
 	case PT_COREDUMP:
-		if (uap->data != sizeof(r.pc))
-			error = EINVAL;
-		else
-			error = copyin(uap->addr, &r.pc, uap->data);
+		error = udata != sizeof(r.pc) ? EINVAL :
+		    copyin(uaddr, &r.pc, udata);
 		break;
 	case PT_SC_REMOTE:
-		if (uap->data != sizeof(r.sr)) {
-			error = EINVAL;
-			break;
-		}
-		error = copyin(uap->addr, &r.sr, uap->data);
+		error = udata != sizeof(r.sr) ? EINVAL :
+		    copyin(uaddr, &r.sr, udata);
 		if (error != 0)
 			break;
 		if (r.sr.pscr_nargs > nitems(td->td_sa.args)) {
@@ -819,9 +809,9 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
 		r.sr.pscr_args = pscr_args;
 		break;
 	case PT_GET_CHILDREN:
-		if (uap->addr == NULL)
+		if (uaddr == NULL)
 			addr = NULL;
-		else if (uap->data < 0)
+		else if (udata < 0)
 			error = EINVAL;
 		else
 			addr = &r.children;
@@ -830,59 +820,57 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap)
 		error = EINVAL;
 		break;
 	default:
-		addr = uap->addr;
+		addr = uaddr;
 		break;
 	}
 	if (error != 0)
 		return (error);
 
-	error = kern_ptrace(td, uap->req, uap->pid, addr, uap->data);
+	error = ptrace_action(td, req, pid, addr, udata);
 	if (error != 0)
 		return (error);
 
-	switch (uap->req) {
+	switch (req) {
 	case PT_VM_ENTRY:
-		error = copyout(&r.pve, uap->addr, sizeof(r.pve));
+		error = copyout(&r.pve, uaddr, sizeof(r.pve));
 		break;
 	case PT_IO:
-		error = copyout(&r.piod, uap->addr, sizeof(r.piod));
+		error = copyout(&r.piod, uaddr, sizeof(r.piod));
 		break;
 	case PT_GETREGS:
-		error = copyout(&r.reg, uap->addr, sizeof(r.reg));
+		error = copyout(&r.reg, uaddr, sizeof(r.reg));
 		break;
 	case PT_GETFPREGS:
-		error = copyout(&r.fpreg, uap->addr, sizeof(r.fpreg));
+		error = copyout(&r.fpreg, uaddr, sizeof(r.fpreg));
 		break;
 	case PT_GETDBREGS:
-		error = copyout(&r.dbreg, uap->addr, sizeof(r.dbreg));
+		error = copyout(&r.dbreg, uaddr, sizeof(r.dbreg));
 		break;
 	case PT_GETREGSET:
-		error = copyout(&r.vec, uap->addr, sizeof(r.vec));
+		error = copyout(&r.vec, uaddr, sizeof(r.vec));
 		break;
 	case PT_GET_EVENT_MASK:
-		/* NB: The size in uap->data is validated in kern_ptrace(). */
-		error = copyout(&r.ptevents, uap->addr, uap->data);
+		/* NB: The size in uap->data is validated in ptraceimpl(). */
+		error = copyout(&r.ptevents, uaddr, udata);
 		break;
 	case PT_LWPINFO:
-		/* NB: The size in uap->data is validated in kern_ptrace(). */
-		error = copyout(&r.pl, uap->addr, uap->data);
+		/* NB: The size in uap->data is validated in ptraceimpl(). */
+		error = copyout(&r.pl, uaddr, udata);
 		break;
 	case PT_GET_SC_ARGS:
-		error = copyout(r.args, uap->addr, MIN(uap->data,
-		    sizeof(r.args)));
+		error = copyout(r.args, uaddr, MIN(udata, sizeof(r.args)));
 		break;
 	case PT_GET_SC_RET:
-		error = copyout(&r.psr, uap->addr, MIN(uap->data,
-		    sizeof(r.psr)));
+		error = copyout(&r.psr, uaddr, MIN(udata, sizeof(r.psr)));
 		break;
 	case PT_SC_REMOTE:
-		error = copyout(&r.sr.pscr_ret, uap->addr +
+		error = copyout(&r.sr.pscr_ret, (char *)uaddr +
 		    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,
+		if (uaddr != NULL) {
+			error = copyout(r.children, uaddr,
 			    td->td_retval[0] * sizeof(struct ptrace_child));
 			free(r.children, M_TEMP);
 		}
@@ -1056,7 +1044,7 @@ ptrace_sel_coredump_thread(struct proc *p)
 }
 
 int
-kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
+ptrace_action(struct thread *td, int req, pid_t pid, void *addr, int data)
 {
 	struct iovec iov;
 	struct uio uio;
@@ -2027,3 +2015,23 @@ fail:
 }
 #undef PROC_READ
 #undef PROC_WRITE
+
+int
+kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
+{
+	return (ptrace_action(td, req, pid, addr, data));
+}
+
+int
+sys_ptrace(struct thread *td, struct ptrace_args *uap)
+{
+	int error;
+
+	AUDIT_ARG_PID(uap->pid);
+	AUDIT_ARG_CMD(uap->req);
+	AUDIT_ARG_VALUE(uap->data);
+
+	error = ptrace_useraction(td, uap->req, uap->pid, uap->addr,
+	    uap->data);
+	return (error);
+}
diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h
index 54669493ca75..50e7dba5c946 100644
--- a/sys/sys/ptrace.h
+++ b/sys/sys/ptrace.h
@@ -291,6 +291,8 @@ int	proc_write_dbregs32(struct thread *_td, struct dbreg32 *_dbreg32);
 #endif
 
 void	ptrace_unsuspend(struct proc *p);
+int	ptrace_action(struct thread *td, int req, pid_t pid, void *addr,
+	    int data);
 
 extern bool allow_ptrace;