git: 7291cbf77d6c - releng/15.1 - ptrace: Fix validation of PT_SC_REMOTE arguments

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Wed, 20 May 2026 19:38:54 UTC
The branch releng/15.1 has been updated by markj:

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

commit 7291cbf77d6c6a8bf535940332ee8243c529c501
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-05-12 17:32:17 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-05-20 13:52:00 +0000

    ptrace: Fix validation of PT_SC_REMOTE arguments
    
    - Fix an off-by-one in the system call number check.  A value of
      SYS_MAXSYSCALL was permitted.
    - Validate the system call number after we've dealt with
      syscall(2)/__syscall(2), since they pass the syscall number as an
      argument.
    - When the syscall number is for syscall(2) or __syscall(2), we must
      make sure that nargs > 0 to avoid an underflow when shifting arguments
      down.
    
    Add regression tests.
    
    Approved by:    re
    Approved by:    so
    Security:       FreeBSD-SA-26:21.ptrace
    Security:       CVE-2026-45253
    Fixes:          140ceb5d956b ("ptrace(2): add PT_SC_REMOTE remote syscall request")
    Reported by:    Yuxiang Yang, Yizhou Zhao, Ao Wang, Xuewei Feng, Qi Li, and Ke Xu from Tsinghua University using GLM-5.1 from Z.ai
    Reviewed by:    kib, emaste
    Differential Revision:  https://reviews.freebsd.org/D56978
---
 sys/kern/kern_sig.c          | 17 +++++----
 tests/sys/kern/ptrace_test.c | 85 ++++++++++++++++++++++++++++++++++----------
 2 files changed, 76 insertions(+), 26 deletions(-)

diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index a55f3c761449..9be7c82ee98b 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -2679,23 +2679,26 @@ ptrace_syscallreq(struct thread *td, struct proc *p,
 	struct sysentvec *sv;
 	struct sysent *se;
 	register_t rv_saved[2];
+	unsigned int sc;
 	int error, nerror;
-	int sc;
 	bool audited, sy_thr_static;
 
-	sv = p->p_sysent;
-	if (sv->sv_table == NULL || sv->sv_size < tsr->ts_sa.code) {
-		tsr->ts_ret.sr_error = ENOSYS;
-		return;
-	}
-
 	sc = tsr->ts_sa.code;
 	if (sc == SYS_syscall || sc == SYS___syscall) {
+		if (tsr->ts_nargs == 0) {
+			tsr->ts_ret.sr_error = EINVAL;
+			return;
+		}
 		sc = tsr->ts_sa.args[0];
 		memmove(&tsr->ts_sa.args[0], &tsr->ts_sa.args[1],
 		    sizeof(register_t) * (tsr->ts_nargs - 1));
 	}
 
+	sv = p->p_sysent;
+	if (sv->sv_table == NULL || sc >= sv->sv_size) {
+		tsr->ts_ret.sr_error = ENOSYS;
+		return;
+	}
 	tsr->ts_sa.callp = se = &sv->sv_table[sc];
 
 	VM_CNT_INC(v_syscall);
diff --git a/tests/sys/kern/ptrace_test.c b/tests/sys/kern/ptrace_test.c
index fee0bd2ffa38..c1a5d226e990 100644
--- a/tests/sys/kern/ptrace_test.c
+++ b/tests/sys/kern/ptrace_test.c
@@ -4362,6 +4362,25 @@ ATF_TC_BODY(ptrace__procdesc_reparent_wait_child, tc)
 	REQUIRE_EQ(close(pd), 0);
 }
 
+static void
+pt_sc_remote(pid_t pid, struct ptrace_sc_remote *pscr, int error,
+    syscallarg_t ret)
+{
+	pid_t wpid;
+	int status;
+
+	ATF_REQUIRE(ptrace(PT_SC_REMOTE, pid, (caddr_t)pscr, sizeof(*pscr)) !=
+	    -1);
+	ATF_REQUIRE_EQ(pscr->pscr_ret.sr_error, error);
+	if (error == 0)
+		ATF_REQUIRE_EQ(pscr->pscr_ret.sr_retval[0], ret);
+
+	wpid = waitpid(pid, &status, 0);
+	REQUIRE_EQ(wpid, pid);
+	ATF_REQUIRE(WIFSTOPPED(status));
+	REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+}
+
 /*
  * Try using PT_SC_REMOTE to get the PID of a traced child process.
  */
@@ -4386,35 +4405,62 @@ ATF_TC_BODY(ptrace__PT_SC_REMOTE_getpid, tc)
 	pscr.pscr_syscall = SYS_getpid;
 	pscr.pscr_nargs = 0;
 	pscr.pscr_args = NULL;
-	ATF_REQUIRE(ptrace(PT_SC_REMOTE, fpid, (caddr_t)&pscr, sizeof(pscr)) !=
-	    -1);
-	ATF_REQUIRE_MSG(pscr.pscr_ret.sr_error == 0,
-	    "remote getpid failed with error %d", pscr.pscr_ret.sr_error);
-	ATF_REQUIRE_MSG(pscr.pscr_ret.sr_retval[0] == fpid,
-	    "unexpected return value %jd instead of %d",
-	    (intmax_t)pscr.pscr_ret.sr_retval[0], fpid);
-
-	wpid = waitpid(fpid, &status, 0);
-	REQUIRE_EQ(wpid, fpid);
-	ATF_REQUIRE(WIFSTOPPED(status));
-	REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
+	pt_sc_remote(fpid, &pscr, 0, fpid);
 
 	pscr.pscr_syscall = SYS_getppid;
 	pscr.pscr_nargs = 0;
 	pscr.pscr_args = NULL;
-	ATF_REQUIRE(ptrace(PT_SC_REMOTE, fpid, (caddr_t)&pscr, sizeof(pscr)) !=
-	    -1);
-	ATF_REQUIRE_MSG(pscr.pscr_ret.sr_error == 0,
-	    "remote getppid failed with error %d", pscr.pscr_ret.sr_error);
-	ATF_REQUIRE_MSG(pscr.pscr_ret.sr_retval[0] == getpid(),
-	    "unexpected return value %jd instead of %d",
-	    (intmax_t)pscr.pscr_ret.sr_retval[0], fpid);
+	pt_sc_remote(fpid, &pscr, 0, getpid());
+
+	ATF_REQUIRE(ptrace(PT_DETACH, fpid, (caddr_t)1, 0) != -1);
+}
+
+ATF_TC_WITHOUT_HEAD(ptrace__PT_SC_REMOTE_syscall_validation);
+ATF_TC_BODY(ptrace__PT_SC_REMOTE_syscall_validation, tc)
+{
+	struct ptrace_sc_remote pscr;
+	quad_t code;
+	int status;
+	pid_t fpid, wpid;
+
+	code = SYS_MAXSYSCALL;
+
+	ATF_REQUIRE((fpid = fork()) != -1);
+	if (fpid == 0) {
+		trace_me();
+		exit(0);
+	}
 
 	wpid = waitpid(fpid, &status, 0);
 	REQUIRE_EQ(wpid, fpid);
 	ATF_REQUIRE(WIFSTOPPED(status));
 	REQUIRE_EQ(WSTOPSIG(status), SIGSTOP);
 
+	pscr.pscr_syscall = SYS_MAXSYSCALL;
+	pscr.pscr_nargs = 0;
+	pscr.pscr_args = NULL;
+	pt_sc_remote(fpid, &pscr, ENOSYS, 0);
+
+	pscr.pscr_syscall = SYS_syscall;
+	pscr.pscr_nargs = 0;
+	pscr.pscr_args = NULL;
+	pt_sc_remote(fpid, &pscr, EINVAL, 0);
+
+	pscr.pscr_syscall = SYS_syscall;
+	pscr.pscr_nargs = 1;
+	pscr.pscr_args = (syscallarg_t *)&code;
+	pt_sc_remote(fpid, &pscr, ENOSYS, 0);
+
+	pscr.pscr_syscall = SYS___syscall;
+	pscr.pscr_nargs = 0;
+	pscr.pscr_args = NULL;
+	pt_sc_remote(fpid, &pscr, EINVAL, 0);
+
+	pscr.pscr_syscall = SYS___syscall;
+	pscr.pscr_nargs = 1;
+	pscr.pscr_args = (syscallarg_t *)&code;
+	pt_sc_remote(fpid, &pscr, ENOSYS, 0);
+
 	ATF_REQUIRE(ptrace(PT_DETACH, fpid, (caddr_t)1, 0) != -1);
 }
 
@@ -4657,6 +4703,7 @@ ATF_TP_ADD_TCS(tp)
 	ATF_TP_ADD_TC(tp, ptrace__procdesc_wait_child);
 	ATF_TP_ADD_TC(tp, ptrace__procdesc_reparent_wait_child);
 	ATF_TP_ADD_TC(tp, ptrace__PT_SC_REMOTE_getpid);
+	ATF_TP_ADD_TC(tp, ptrace__PT_SC_REMOTE_syscall_validation);
 	ATF_TP_ADD_TC(tp, ptrace__reap_kill_stopped);
 	ATF_TP_ADD_TC(tp, ptrace__PT_ATTACH_no_EINTR);
 	ATF_TP_ADD_TC(tp, ptrace__PT_DETACH_continued);