The branch main has been updated by kib: URL: https://cgit.FreeBSD.org/src/commit/?id=fd5faa5629aed98f4daee84063da3494c4d1eee9
commit fd5faa5629aed98f4daee84063da3494c4d1eee9 Author: Konstantin Belousov <[email protected]> AuthorDate: 2026-07-15 02:23:17 +0000 Commit: Konstantin Belousov <[email protected]> CommitDate: 2026-07-16 22:22:55 +0000 ptrace(2): PT_SET_SC_RET request Reviewed by: markj Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D58247 --- sys/compat/freebsd32/freebsd32_misc.c | 19 +++++++++++++++++++ sys/kern/subr_syscall.c | 12 ++++++++---- sys/kern/sys_process.c | 24 ++++++++++++++++++++++++ sys/sys/proc.h | 1 + sys/sys/ptrace.h | 3 ++- 5 files changed, 54 insertions(+), 5 deletions(-) diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index e4a4f256feb4..e07000015d54 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -1005,6 +1005,16 @@ ptrace_sc_ret_to32(const struct ptrace_sc_ret *psr, psr32->sr_error = psr->sr_error; } +static void +ptrace_sc_ret32_to_ret(const struct ptrace_sc_ret32 *psr32, + struct ptrace_sc_ret *psr) +{ + bzero(psr, sizeof(*psr)); + psr->sr_retval[0] = psr32->sr_retval[0]; + psr->sr_retval[1] = psr32->sr_retval[1]; + psr->sr_error = psr32->sr_error; +} + int freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap) { @@ -1051,6 +1061,15 @@ freebsd32_ptrace(struct thread *td, struct freebsd32_ptrace_args *uap) 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); + } + break; case PT_LWPINFO: if (uap->data > sizeof(r32.pl)) return (EINVAL); diff --git a/sys/kern/subr_syscall.c b/sys/kern/subr_syscall.c index 660170cc7a5f..9997a6b638f7 100644 --- a/sys/kern/subr_syscall.c +++ b/sys/kern/subr_syscall.c @@ -66,10 +66,11 @@ syscallenter(struct thread *td) if (__predict_false(td->td_cowgen != atomic_load_int(&p->p_cowgen))) thread_cow_update(td); traced = (p->p_flag & P_TRACED) != 0; - if (__predict_false(traced || td->td_dbgflags & TDB_USERWR)) { + if (__predict_false(traced || (td->td_dbgflags & (TDB_USERWR | + TDB_SET_SC_RET)) != 0)) { PROC_LOCK(p); MPASS((td->td_dbgflags & TDB_BOUNDARY) == 0); - td->td_dbgflags &= ~TDB_USERWR; + td->td_dbgflags &= ~(TDB_USERWR | TDB_SET_SC_RET); if (traced) td->td_dbgflags |= TDB_SCE; PROC_UNLOCK(p); @@ -97,7 +98,10 @@ syscallenter(struct thread *td) ptracestop((td), SIGTRAP, NULL); PROC_UNLOCK(p); - if ((td->td_dbgflags & TDB_USERWR) != 0) { + if ((td->td_dbgflags & TDB_SET_SC_RET) != 0) { + error = td->td_errno; + goto retval; + } else if ((td->td_dbgflags & TDB_USERWR) != 0) { /* * Reread syscall number and arguments if debugger * modified registers or memory. @@ -201,7 +205,7 @@ syscallenter(struct thread *td) td->td_retval[1]); if (__predict_false(traced)) { PROC_LOCK(p); - td->td_dbgflags &= ~(TDB_SCE | TDB_BOUNDARY); + td->td_dbgflags &= ~(TDB_SCE | TDB_BOUNDARY | TDB_SET_SC_RET); PROC_UNLOCK(p); } (p->p_sysent->sv_set_syscall_retval)(td, error); diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index e10d1fc4b8be..d3f8e4c81669 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -752,6 +752,12 @@ sys_ptrace(struct thread *td, struct ptrace_args *uap) 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)); + break; case PT_GETREGS: bzero(&r.reg, sizeof(r.reg)); break; @@ -1340,6 +1346,24 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) psr->sr_retval[1]); break; + case PT_SET_SC_RET: + if ((td2->td_dbgflags & TDB_SCE) == 0 +#ifdef COMPAT_FREEBSD32 + || (wrap32 && !safe) +#endif + ) { + error = EINVAL; + break; + } + psr = addr; + td2->td_errno = psr->sr_error; + if (td2->td_errno == 0) { + td2->td_retval[0] = psr->sr_retval[0]; + td2->td_retval[1] = psr->sr_retval[1]; + } + td2->td_dbgflags |= TDB_SET_SC_RET; + break; + case PT_STEP: case PT_CONTINUE: case PT_TO_SCE: diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 6736fa5bb5f9..9b98550b9f8c 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -531,6 +531,7 @@ enum { #define TDB_BOUNDARY 0x00008000 /* ptracestop() at boundary */ #define TDB_COREDUMPREQ 0x00010000 /* Coredump request */ #define TDB_SCREMOTEREQ 0x00020000 /* Remote syscall request */ +#define TDB_SET_SC_RET 0x00040000 /* PT_SET_SC_RET applied */ /* * "Private" flags kept in td_pflags: diff --git a/sys/sys/ptrace.h b/sys/sys/ptrace.h index 2ef52b22044c..2aa82ce07a1d 100644 --- a/sys/sys/ptrace.h +++ b/sys/sys/ptrace.h @@ -85,6 +85,7 @@ #define PT_GETREGSET 42 /* Get a target register set */ #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_FIRSTMACH 64 /* for machine-specific requests */ #define PT_LASTMACH 127 @@ -166,7 +167,7 @@ struct ptrace_lwpinfo32 { }; #endif -/* Argument structure for PT_GET_SC_RET. */ +/* Argument structure for PT_GET_SC_RET and PT_SET_SC_RET. */ struct ptrace_sc_ret { syscallarg_t sr_retval[2]; /* Only valid if sr_error == 0. */ int sr_error;
