use std::{ffi::CStr, mem, ptr};
use cfg_if::cfg_if;
use libc::{
c_int, c_long, c_void, iovec, siginfo_t, PTRACE_CONT, PTRACE_GETEVENTMSG, PTRACE_GETREGSET,
PTRACE_GETSIGINFO, PTRACE_PEEKUSER, PTRACE_POKEUSER, PTRACE_SYSCALL,
};
use libseccomp_sys::seccomp_syscall_resolve_num_arch;
use nix::{errno::Errno, unistd::Pid};
use crate::{
compat::{NT_PRSTATUS, PTRACE_LISTEN},
cookie::safe_ptrace,
path::XPath,
};
cfg_if! {
if #[cfg(any(all(target_os = "linux", target_arch = "s390x"),
all(target_os = "linux", target_env = "gnu"),
target_env = "uclibc"))] {
pub(crate) type PtraceRequest = ::libc::c_uint;
} else {
pub(crate) type PtraceRequest = ::libc::c_int;
}
}
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
const X86_ARG_OFFSETS: [u64; 6] = [
0, 4, 2 * 4, 3 * 4, 4 * 4, 5 * 4, ];
#[cfg(target_arch = "x86_64")]
const X64_ARG_OFFSETS: [u64; 6] = [
14 * 8, 13 * 8, 12 * 8, 7 * 8, 9 * 8, 8 * 8, ];
#[cfg(target_arch = "x86")]
const X86_EAX_OFFSET: u64 = 6 * 4;
#[cfg(target_arch = "x86")]
const X86_ORIG_EAX_OFFSET: u64 = 11 * 4;
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
const MIPS_REG_V0: usize = 2;
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
const MIPS_REG_A0: usize = 4;
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
const MIPS_REG_A3: usize = MIPS_REG_A0 + 3;
#[cfg(target_arch = "x86_64")]
#[repr(C)]
#[derive(Copy, Clone)]
struct I386UserRegsStruct {
ebx: u32,
ecx: u32,
edx: u32,
esi: u32,
edi: u32,
ebp: u32,
eax: u32,
ds: u32,
es: u32,
fs: u32,
gs: u32,
orig_eax: u32,
eip: u32,
cs: u32,
eflags: u32,
esp: u32,
ss: u32,
}
#[repr(C)]
#[cfg(target_arch = "x86_64")]
union X86UserRegsStruct {
x64: libc::user_regs_struct, x32: I386UserRegsStruct, }
#[cfg(target_arch = "aarch64")]
#[repr(C)]
#[derive(Copy, Clone)]
struct Aarch64UserRegsStruct {
regs: [u64; 31], sp: u64, pc: u64, pstate: u64, }
#[cfg(target_arch = "m68k")]
#[repr(C)]
#[derive(Copy, Clone)]
struct M68KUserRegsStruct {
d1: c_long,
d2: c_long,
d3: c_long,
d4: c_long,
d5: c_long,
d6: c_long,
d7: c_long,
a0: c_long,
a1: c_long,
a2: c_long,
a3: c_long,
a4: c_long,
a5: c_long,
a6: c_long,
d0: c_long,
usp: c_long,
orig_d0: c_long,
stkadj: libc::c_short,
sr: libc::c_short,
pc: c_long,
fmtvec: libc::c_short,
__fill: libc::c_short,
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
#[repr(C)]
#[derive(Copy, Clone)]
struct MipsPtRegs {
regs: [u64; 32], lo: u64, hi: u64, cp0_epc: u64, cp0_badvaddr: u64, cp0_status: u64, cp0_cause: u64, }
#[cfg(target_arch = "powerpc64")]
#[repr(C)]
#[derive(Copy, Clone)]
struct PpcPtRegs64 {
gpr: [libc::c_ulong; 32], nip: libc::c_ulong, msr: libc::c_ulong, orig_gpr3: libc::c_ulong, ctr: libc::c_ulong, link: libc::c_ulong, xer: libc::c_ulong, ccr: libc::c_ulong, softe: libc::c_ulong, trap: libc::c_ulong, dar: libc::c_ulong, dsisr: libc::c_ulong, result: libc::c_ulong, }
#[cfg(target_arch = "powerpc64")]
#[repr(C)]
union PpcPtRegsUnion {
ppc64: PpcPtRegs64, ppc32: PpcPtRegs32, }
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
#[repr(C)]
#[derive(Copy, Clone)]
struct PpcPtRegs32 {
gpr: [u32; 32], nip: u32, msr: u32, orig_gpr3: u32, ctr: u32, link: u32, xer: u32, ccr: u32, mq: u32, trap: u32, dar: u32, dsisr: u32, result: u32, }
#[cfg(target_arch = "riscv64")]
#[repr(C)]
#[derive(Copy, Clone)]
struct Riscv64UserRegsStruct {
pc: u64,
ra: u64,
sp: u64,
gp: u64,
tp: u64,
t0: u64,
t1: u64,
t2: u64,
s0: u64,
s1: u64,
a0: u64,
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
s2: u64,
s3: u64,
s4: u64,
s5: u64,
s6: u64,
s7: u64,
s8: u64,
s9: u64,
s10: u64,
s11: u64,
t3: u64,
t4: u64,
t5: u64,
t6: u64,
}
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
#[repr(C)]
#[derive(Copy, Clone)]
struct ArmPtRegs {
uregs: [u32; 18],
}
#[cfg(target_arch = "aarch64")]
#[repr(C)]
union ArmRegsUnion {
aarch64: Aarch64UserRegsStruct, arm: ArmPtRegs, }
#[cfg(target_arch = "s390x")]
#[repr(C, align(8))]
struct S390PswT {
mask: u64,
addr: u64,
}
#[cfg(target_arch = "s390x")]
#[repr(C)]
struct S390Regs {
psw: S390PswT,
gprs: [u64; 16],
acrs: [u32; 16],
orig_gpr2: u64,
}
#[cfg(target_arch = "loongarch64")]
#[repr(C)]
#[derive(Copy, Clone)]
struct LoongarchUserRegsStruct {
regs: [u64; 32],
orig_a0: u64,
csr_era: u64,
csr_badv: u64,
reserved: [u64; 10],
}
#[allow(unused)]
pub fn ptrace_skip_syscall(pid: Pid, arch: u32, errno: Option<Errno>) -> Result<(), Errno> {
#[cfg(any(
target_arch = "x86", // TODO: provide per-arch implementation.
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc64",
target_arch = "powerpc",
target_arch = "s390x",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "loongarch64",
))]
{
use crate::confine::{scmp_arch, scmp_arch_bits};
let sys_invalid = if cfg!(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "s390x",
)) {
return ptrace_set_return(pid, arch, errno);
} else if scmp_arch_bits(scmp_arch(arch)?) == 32 {
u32::MAX.into()
} else {
u64::MAX
};
ptrace_set_syscall(pid, arch, sys_invalid)?;
ptrace_set_return(pid, arch, errno)
}
#[cfg(target_arch = "x86_64")]
{
use libseccomp_sys::{SCMP_ARCH_X32, SCMP_ARCH_X86, SCMP_ARCH_X86_64};
if !matches!(arch, SCMP_ARCH_X86_64 | SCMP_ARCH_X86 | SCMP_ARCH_X32) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<X86UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<X86UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
#[expect(clippy::arithmetic_side_effects)]
let rval = -errno.map(|err| err as i32).unwrap_or(0);
#[expect(clippy::cast_sign_loss)]
match arch {
SCMP_ARCH_X86_64 | SCMP_ARCH_X32 => {
regs.x64.orig_rax = u64::MAX;
regs.x64.rax = i64::from(rval) as u64;
}
SCMP_ARCH_X86 => {
regs.x32.orig_eax = u32::MAX;
regs.x32.eax = rval as u32;
}
_ => return Err(Errno::EINVAL),
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "m68k")]
{
use libc::{PTRACE_GETREGS, PTRACE_SETREGS};
use libseccomp_sys::SCMP_ARCH_M68K;
if arch != SCMP_ARCH_M68K {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<M68KUserRegsStruct>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void) }?;
let mut regs = unsafe { regs.assume_init() };
regs.orig_d0 = c_long::MAX;
regs.d0 = -(errno.map(|err| err as i32).unwrap_or(0) as c_long);
ptrace_setregs(
pid,
PTRACE_SETREGS,
std::ptr::addr_of_mut!(regs) as *mut c_void,
)
.map(drop)
}
#[cfg(target_arch = "riscv64")]
{
use libseccomp_sys::SCMP_ARCH_RISCV64;
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<Riscv64UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
regs.a7 = u64::MAX;
regs.a0 = (-(errno.map(|err| err as i32).unwrap_or(0) as i64)) as u64;
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "s390x",
target_arch = "riscv64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "m68k",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "loongarch64",
)))]
{
compile_error!("BUG: ptrace_skip_syscall is not implemented for this architecture!");
}
}
#[allow(unused)]
pub fn ptrace_set_return(pid: Pid, arch: u32, errno: Option<Errno>) -> Result<(), Errno> {
#[cfg(target_arch = "x86_64")]
{
use libseccomp_sys::{SCMP_ARCH_X32, SCMP_ARCH_X86, SCMP_ARCH_X86_64};
if !matches!(arch, SCMP_ARCH_X86_64 | SCMP_ARCH_X86 | SCMP_ARCH_X32) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<X86UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<X86UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
#[expect(clippy::arithmetic_side_effects)]
let rval = if let Some(e) = errno {
-(e as i64)
} else {
0
};
#[expect(clippy::cast_sign_loss)]
#[expect(clippy::cast_possible_truncation)]
match arch {
SCMP_ARCH_X86_64 | SCMP_ARCH_X32 => regs.x64.rax = rval as u64,
SCMP_ARCH_X86 => regs.x32.eax = (rval as i32) as u32,
_ => return Err(Errno::EINVAL),
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "x86")]
{
use libseccomp_sys::SCMP_ARCH_X86;
use nix::{errno::Errno, sys::ptrace};
if arch != SCMP_ARCH_X86 {
return Err(Errno::EINVAL);
}
#[expect(clippy::arithmetic_side_effects)]
let rval = if let Some(e) = errno {
-(e as i32)
} else {
0
};
unsafe { ptrace_write_user(pid, X86_EAX_OFFSET as *mut c_void, rval.into()) }
}
#[cfg(target_arch = "aarch64")]
{
use libseccomp_sys::{SCMP_ARCH_AARCH64, SCMP_ARCH_ARM};
let mut regs = mem::MaybeUninit::<ArmRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
match arch {
SCMP_ARCH_AARCH64 => {
let regs_ref = unsafe { &mut regs.aarch64 };
#[expect(clippy::arithmetic_side_effects)]
let rval = if let Some(e) = errno {
-(e as i64) } else {
0 };
#[expect(clippy::cast_sign_loss)]
{
regs_ref.regs[0] = rval as u64;
}
}
SCMP_ARCH_ARM => {
let regs_ref = unsafe { &mut regs.arm };
#[expect(clippy::arithmetic_side_effects)]
let rval = if let Some(e) = errno {
-(e as i32) } else {
0 };
#[expect(clippy::cast_sign_loss)]
{
regs_ref.uregs[0] = rval as u32;
}
}
_ => return Err(Errno::EINVAL),
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "arm")]
{
use libseccomp_sys::SCMP_ARCH_ARM;
if arch != SCMP_ARCH_ARM {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<ArmPtRegs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmPtRegs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
#[expect(clippy::arithmetic_side_effects)]
let rval = if let Some(e) = errno {
-(e as i32) } else {
0 };
#[expect(clippy::cast_sign_loss)]
{
regs.uregs[0] = rval as u32;
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "m68k")]
{
use libc::{PTRACE_GETREGS, PTRACE_SETREGS};
use libseccomp_sys::SCMP_ARCH_M68K;
if arch != SCMP_ARCH_M68K {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<M68KUserRegsStruct>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void) }?;
let mut regs = unsafe { regs.assume_init() };
#[expect(clippy::arithmetic_side_effects)]
let rval = if let Some(e) = errno {
-(e as c_long) } else {
0 };
regs.d0 = rval;
ptrace_setregs(
pid,
PTRACE_SETREGS,
std::ptr::addr_of_mut!(regs) as *mut c_void,
)
.map(drop)
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
{
use libc::{PTRACE_GETREGS, PTRACE_SETREGS};
use libseccomp_sys::{
SCMP_ARCH_MIPS, SCMP_ARCH_MIPS64, SCMP_ARCH_MIPS64N32, SCMP_ARCH_MIPSEL,
SCMP_ARCH_MIPSEL64, SCMP_ARCH_MIPSEL64N32,
};
use nix::errno::Errno;
if !matches!(
arch,
SCMP_ARCH_MIPS
| SCMP_ARCH_MIPS64
| SCMP_ARCH_MIPSEL
| SCMP_ARCH_MIPSEL64
| SCMP_ARCH_MIPS64N32
| SCMP_ARCH_MIPSEL64N32
) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<MipsPtRegs>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void) }?;
let mut regs = unsafe { regs.assume_init() };
#[expect(clippy::arithmetic_side_effects)]
if matches!(arch, SCMP_ARCH_MIPS | SCMP_ARCH_MIPSEL) {
if let Some(e) = errno {
regs.regs[MIPS_REG_V0] = (e as u32) as u64;
regs.regs[MIPS_REG_A3] = u32::MAX as u64; } else {
regs.regs[MIPS_REG_V0] = 0;
regs.regs[MIPS_REG_A3] = 0;
}
} else {
if let Some(e) = errno {
regs.regs[MIPS_REG_V0] = e as u64;
regs.regs[MIPS_REG_A3] = u64::MAX; } else {
regs.regs[MIPS_REG_V0] = 0;
regs.regs[MIPS_REG_A3] = 0;
}
}
unsafe {
ptrace_setregs(
pid,
PTRACE_SETREGS,
std::ptr::addr_of_mut!(regs) as *mut c_void,
)
}
.map(drop)
}
#[cfg(target_arch = "riscv64")]
{
use libseccomp_sys::SCMP_ARCH_RISCV64;
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<Riscv64UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
#[expect(clippy::arithmetic_side_effects)]
let rval = if let Some(e) = errno {
-(e as i64) } else {
0 };
#[expect(clippy::cast_sign_loss)]
{
regs.a0 = rval as u64;
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "s390x")]
{
use libseccomp_sys::SCMP_ARCH_S390X;
if arch != SCMP_ARCH_S390X {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<S390Regs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<S390Regs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
#[expect(clippy::arithmetic_side_effects)]
let rval = if let Some(e) = errno {
-(e as i64) } else {
0 };
#[expect(clippy::cast_sign_loss)]
{
regs.gprs[2] = rval as u64;
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "powerpc")]
{
use libseccomp_sys::SCMP_ARCH_PPC;
if arch != SCMP_ARCH_PPC {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegs32>::uninit();
unsafe {
ptrace_getregs(
pid,
12,
regs.as_mut_ptr() as *mut c_void,
)
}?;
let mut regs = unsafe { regs.assume_init() };
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::cast_sign_loss)]
if let Some(e) = errno {
if (regs.trap & 0xfff0) == 0x3000 {
regs.gpr[3] = -(e as i32) as u32;
} else {
regs.gpr[3] = e as i32 as u32;
regs.ccr |= 0x10000000; }
} else {
regs.gpr[3] = 0;
if (regs.trap & 0xfff0) != 0x3000 {
regs.ccr &= !0x10000000;
}
}
unsafe { ptrace_setregs(pid, 13, std::ptr::addr_of_mut!(regs) as *mut c_void) }.map(drop)
}
#[cfg(target_arch = "powerpc64")]
{
use libseccomp_sys::{SCMP_ARCH_PPC, SCMP_ARCH_PPC64, SCMP_ARCH_PPC64LE};
if !matches!(arch, SCMP_ARCH_PPC | SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<PpcPtRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
match arch {
SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE => {
let regs = unsafe { &mut regs.ppc64 };
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::cast_sign_loss)]
if let Some(e) = errno {
if (regs.trap & 0xfff0) == 0x3000 {
regs.gpr[3] = -(e as i32) as u64;
} else {
regs.gpr[3] = e as i32 as u64;
regs.ccr |= 0x10000000; }
} else {
regs.gpr[3] = 0;
if (regs.trap & 0xfff0) != 0x3000 {
regs.ccr &= !0x10000000;
}
}
}
SCMP_ARCH_PPC => {
let regs = unsafe { &mut regs.ppc32 };
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::cast_sign_loss)]
if let Some(e) = errno {
if (regs.trap & 0xfff0) == 0x3000 {
regs.gpr[3] = -(e as i32) as u32;
} else {
regs.gpr[3] = e as i32 as u32;
regs.ccr |= 0x10000000; }
} else {
regs.gpr[3] = 0;
if (regs.trap & 0xfff0) != 0x3000 {
regs.ccr &= !0x10000000;
}
}
}
_ => return Err(Errno::EINVAL),
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "loongarch64")]
{
use libc::user_regs_struct;
use libseccomp_sys::SCMP_ARCH_LOONGARCH64;
if arch != SCMP_ARCH_LOONGARCH64 {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<user_regs_struct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<user_regs_struct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
#[expect(clippy::arithmetic_side_effects)]
let rval = if let Some(e) = errno {
-(e as i64) } else {
0 };
#[expect(clippy::cast_sign_loss)]
{
regs.regs[4] = rval as u64;
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "s390x",
target_arch = "riscv64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "m68k",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "loongarch64",
)))]
{
compile_error!("BUG: ptrace_set_return is not implemented for this architecture!");
}
}
pub fn ptrace_get_error(pid: Pid, arch: u32) -> Result<Option<Errno>, Errno> {
#[cfg(target_arch = "x86_64")]
{
use libseccomp_sys::{SCMP_ARCH_X32, SCMP_ARCH_X86, SCMP_ARCH_X86_64};
if !matches!(arch, SCMP_ARCH_X86_64 | SCMP_ARCH_X86 | SCMP_ARCH_X32) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<X86UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<X86UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
#[expect(clippy::cast_possible_wrap)]
let val: i64 = match arch {
SCMP_ARCH_X86_64 | SCMP_ARCH_X32 => {
let r = unsafe { regs.x64 };
r.rax as i64
}
SCMP_ARCH_X86 => {
let r = unsafe { regs.x32 };
i64::from(r.eax as i32)
}
_ => return Err(Errno::EINVAL),
};
if let Some(e) = check_negated_errno(val) {
Ok(Some(e))
} else {
Ok(None)
}
}
#[cfg(target_arch = "x86")]
{
use libseccomp_sys::SCMP_ARCH_X86;
if arch != SCMP_ARCH_X86 {
return Err(Errno::EINVAL);
}
let raw_eax = unsafe { ptrace_read_user(pid, X86_EAX_OFFSET as *mut c_void)? } as i32;
let val_eax = raw_eax as i64;
if let Some(e) = check_negated_errno(val_eax) {
Ok(Some(e))
} else {
Ok(None)
}
}
#[cfg(target_arch = "aarch64")]
{
use libseccomp_sys::{SCMP_ARCH_AARCH64, SCMP_ARCH_ARM};
let mut regs = mem::MaybeUninit::<ArmRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
let val: i64 = match arch {
SCMP_ARCH_AARCH64 => {
let a64 = unsafe { regs.aarch64 };
a64.regs[0] as i64
}
SCMP_ARCH_ARM => {
let arm = unsafe { regs.arm };
(arm.uregs[0] as i32) as i64
}
_ => return Err(Errno::EINVAL),
};
if let Some(e) = check_negated_errno(val) {
Ok(Some(e))
} else {
Ok(None)
}
}
#[cfg(target_arch = "arm")]
{
use libseccomp_sys::SCMP_ARCH_ARM;
if arch != SCMP_ARCH_ARM {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<ArmPtRegs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmPtRegs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
let val = (regs.uregs[0] as i32) as i64;
if let Some(e) = check_negated_errno(val) {
Ok(Some(e))
} else {
Ok(None)
}
}
#[cfg(target_arch = "m68k")]
{
use libc::PTRACE_GETREGS;
use libseccomp_sys::SCMP_ARCH_M68K;
if arch != SCMP_ARCH_M68K {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<M68KUserRegsStruct>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void) }?;
let regs = unsafe { regs.assume_init() };
if let Some(e) = check_negated_errno(regs.d0 as i64) {
Ok(Some(e))
} else {
Ok(None)
}
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
{
use libc::PTRACE_GETREGS;
use libseccomp_sys::{
SCMP_ARCH_MIPS, SCMP_ARCH_MIPS64, SCMP_ARCH_MIPS64N32, SCMP_ARCH_MIPSEL,
SCMP_ARCH_MIPSEL64, SCMP_ARCH_MIPSEL64N32,
};
use nix::errno::Errno;
if !matches!(
arch,
SCMP_ARCH_MIPS
| SCMP_ARCH_MIPS64
| SCMP_ARCH_MIPSEL
| SCMP_ARCH_MIPSEL64
| SCMP_ARCH_MIPS64N32
| SCMP_ARCH_MIPSEL64N32
) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<MipsPtRegs>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void) }?;
let regs = unsafe { regs.assume_init() };
if regs.regs[MIPS_REG_A3] != 0 {
Ok(Some(Errno::from_raw(regs.regs[MIPS_REG_V0] as i32)))
} else {
Ok(None)
}
}
#[cfg(target_arch = "riscv64")]
{
use libseccomp_sys::SCMP_ARCH_RISCV64;
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<Riscv64UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
let val = regs.a0 as i64;
if let Some(e) = check_negated_errno(val) {
Ok(Some(e))
} else {
Ok(None)
}
}
#[cfg(target_arch = "s390x")]
{
use libseccomp_sys::SCMP_ARCH_S390X;
if arch != SCMP_ARCH_S390X {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<S390Regs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<S390Regs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
let val = regs.gprs[2] as i64;
if let Some(e) = check_negated_errno(val) {
Ok(Some(e))
} else {
Ok(None)
}
}
#[cfg(target_arch = "powerpc")]
{
use libseccomp_sys::SCMP_ARCH_PPC;
if arch != SCMP_ARCH_PPC {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegs32>::uninit();
unsafe {
ptrace_getregs(
pid,
12,
regs.as_mut_ptr() as *mut c_void,
)
}?;
let regs = unsafe { regs.assume_init() };
let r3 = regs.gpr[3] as i64;
let scv = (regs.trap & 0xfff0) == 0x3000;
if scv {
if let Some(e) = check_negated_errno(r3) {
Ok(Some(e))
} else {
Ok(None)
}
} else {
if (regs.ccr & 0x10000000) != 0 {
let err = r3 as i32;
Ok(Some(Errno::from_raw(err)))
} else {
Ok(None)
}
}
}
#[cfg(target_arch = "powerpc64")]
{
use libseccomp_sys::{SCMP_ARCH_PPC, SCMP_ARCH_PPC64, SCMP_ARCH_PPC64LE};
if !matches!(arch, SCMP_ARCH_PPC | SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<PpcPtRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
#[expect(clippy::cast_possible_wrap)]
let (r3, scv, ccr) = match arch {
SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE => {
let regs = unsafe { ®s.ppc64 };
(
regs.gpr[3] as i64,
(regs.trap & 0xfff0) == 0x3000,
(regs.ccr & 0x10000000) != 0,
)
}
SCMP_ARCH_PPC => {
let regs = unsafe { ®s.ppc32 };
(
regs.gpr[3] as i64,
(regs.trap & 0xfff0) == 0x3000,
(regs.ccr & 0x10000000) != 0,
)
}
_ => return Err(Errno::EINVAL),
};
if scv {
if let Some(e) = check_negated_errno(r3) {
Ok(Some(e))
} else {
Ok(None)
}
} else {
if ccr {
let err = r3 as i32;
Ok(Some(Errno::from_raw(err)))
} else {
Ok(None)
}
}
}
#[cfg(target_arch = "loongarch64")]
{
use libc::user_regs_struct;
use libseccomp_sys::SCMP_ARCH_LOONGARCH64;
if arch != SCMP_ARCH_LOONGARCH64 {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<user_regs_struct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<user_regs_struct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
let val = regs.regs[4] as i64;
if let Some(e) = check_negated_errno(val) {
Ok(Some(e))
} else {
Ok(None)
}
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "s390x",
target_arch = "riscv64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "m68k",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "loongarch64",
)))]
{
compile_error!("BUG: ptrace_get_error is not implemented for this architecture!");
}
}
#[allow(unused)]
pub fn ptrace_set_syscall(pid: Pid, arch: u32, sysno: u64) -> Result<(), Errno> {
#[cfg(target_arch = "x86_64")]
{
use libseccomp_sys::{SCMP_ARCH_X32, SCMP_ARCH_X86, SCMP_ARCH_X86_64};
use crate::confine::X32_SYSCALL_BIT;
if !matches!(arch, SCMP_ARCH_X86_64 | SCMP_ARCH_X86 | SCMP_ARCH_X32) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<X86UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<X86UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
#[expect(clippy::cast_possible_truncation)]
match arch {
SCMP_ARCH_X86_64 => regs.x64.orig_rax = sysno,
SCMP_ARCH_X32 => regs.x64.orig_rax = sysno | (X32_SYSCALL_BIT as u64),
SCMP_ARCH_X86 => regs.x32.orig_eax = sysno as u32,
_ => return Err(Errno::EINVAL),
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "x86")]
{
unsafe { ptrace_write_user(pid, X86_ORIG_EAX_OFFSET as *mut c_void, sysno as c_long) }
}
#[cfg(target_arch = "aarch64")]
{
let mut sysno = sysno;
let io = iovec {
iov_base: std::ptr::addr_of_mut!(sysno) as *mut c_void,
iov_len: mem::size_of::<u64>(),
};
ptrace_setregset(pid, 0x404, &io)
}
#[cfg(target_arch = "arm")]
{
ptrace_set_syscall_arm(pid, sysno as usize).map(drop)
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
{
use libc::{PTRACE_GETREGS, PTRACE_SETREGS};
use libseccomp_sys::{
SCMP_ARCH_MIPS, SCMP_ARCH_MIPS64, SCMP_ARCH_MIPS64N32, SCMP_ARCH_MIPSEL,
SCMP_ARCH_MIPSEL64, SCMP_ARCH_MIPSEL64N32,
};
use nix::errno::Errno;
if !matches!(
arch,
SCMP_ARCH_MIPS
| SCMP_ARCH_MIPS64
| SCMP_ARCH_MIPSEL
| SCMP_ARCH_MIPSEL64
| SCMP_ARCH_MIPS64N32
| SCMP_ARCH_MIPSEL64N32
) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<MipsPtRegs>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void) }?;
let mut regs = unsafe { regs.assume_init() };
regs.regs[MIPS_REG_V0] = sysno;
unsafe {
ptrace_setregs(
pid,
PTRACE_SETREGS,
std::ptr::addr_of_mut!(regs) as *mut c_void,
)
}
.map(drop)
}
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
{
use libseccomp_sys::{SCMP_ARCH_PPC, SCMP_ARCH_PPC64, SCMP_ARCH_PPC64LE};
if !matches!(arch, SCMP_ARCH_PPC | SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE) {
return Err(Errno::EINVAL);
}
unsafe { ptrace_write_user(pid, std::ptr::null_mut(), sysno as c_long) }
}
#[cfg(target_arch = "riscv64")]
{
use libseccomp_sys::SCMP_ARCH_RISCV64;
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<Riscv64UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
regs.a7 = sysno;
regs.a0 = (-(Errno::ENOSYS as i64)) as u64;
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "s390x")]
{
use libseccomp_sys::SCMP_ARCH_S390X;
if arch != SCMP_ARCH_S390X {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<S390Regs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<S390Regs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
regs.gprs[2] = sysno;
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "loongarch64")]
{
use libc::user_regs_struct;
use libseccomp_sys::SCMP_ARCH_LOONGARCH64;
if arch != SCMP_ARCH_LOONGARCH64 {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<user_regs_struct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<user_regs_struct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
regs.regs[11] = sysno;
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "m68k")]
{
use libc::{PTRACE_GETREGS, PTRACE_SETREGS};
use libseccomp_sys::SCMP_ARCH_M68K;
if arch != SCMP_ARCH_M68K {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<M68KUserRegsStruct>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void) }?;
let mut regs = unsafe { regs.assume_init() };
regs.orig_d0 = sysno as c_long;
ptrace_setregs(
pid,
PTRACE_SETREGS,
std::ptr::addr_of_mut!(regs) as *mut c_void,
)
.map(drop)
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "s390x",
target_arch = "riscv64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "m68k",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "loongarch64",
)))]
{
compile_error!("BUG: ptrace_set_syscall is not implemented for this architecture!");
}
}
pub fn ptrace_get_arg(pid: Pid, arch: u32, idx: usize) -> Result<u64, Errno> {
if idx > 5 {
return Err(Errno::EINVAL);
}
#[cfg(target_arch = "x86_64")]
{
use libseccomp_sys::{SCMP_ARCH_X32, SCMP_ARCH_X86, SCMP_ARCH_X86_64};
match arch {
SCMP_ARCH_X86_64 | SCMP_ARCH_X32 => {
#[expect(clippy::cast_sign_loss)]
Ok(unsafe { ptrace_read_user(pid, X64_ARG_OFFSETS[idx] as *mut c_void)? } as u64)
}
SCMP_ARCH_X86 => {
#[expect(clippy::cast_sign_loss)]
Ok(unsafe { ptrace_read_user(pid, X86_ARG_OFFSETS[idx] as *mut c_void)? } as u64)
}
_ => Err(Errno::EINVAL),
}
}
#[cfg(target_arch = "x86")]
{
use libseccomp_sys::SCMP_ARCH_X86;
if arch != SCMP_ARCH_X86 {
return Err(Errno::EINVAL);
}
Ok(unsafe { ptrace_read_user(pid, X86_ARG_OFFSETS[idx] as *mut c_void)? } as u64)
}
#[cfg(target_arch = "aarch64")]
{
use libseccomp_sys::{SCMP_ARCH_AARCH64, SCMP_ARCH_ARM};
let mut regs = mem::MaybeUninit::<ArmRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
match arch {
SCMP_ARCH_AARCH64 => {
let regs = unsafe { regs.aarch64 };
Ok(regs.regs[idx])
}
SCMP_ARCH_ARM => {
let regs = unsafe { regs.arm };
Ok(u64::from(regs.uregs[idx]))
}
_ => Err(Errno::EINVAL),
}
}
#[cfg(target_arch = "arm")]
{
use libseccomp_sys::SCMP_ARCH_ARM;
if arch != SCMP_ARCH_ARM {
return Err(Errno::EINVAL);
}
#[expect(clippy::cast_sign_loss)]
Ok(unsafe { ptrace_read_user(pid, (idx as u64 * 4) as *mut c_void)? } as u64)
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
{
use libseccomp_sys::{
SCMP_ARCH_MIPS, SCMP_ARCH_MIPS64, SCMP_ARCH_MIPS64N32, SCMP_ARCH_MIPSEL,
SCMP_ARCH_MIPSEL64, SCMP_ARCH_MIPSEL64N32,
};
if !matches!(
arch,
SCMP_ARCH_MIPS
| SCMP_ARCH_MIPS64
| SCMP_ARCH_MIPSEL
| SCMP_ARCH_MIPSEL64
| SCMP_ARCH_MIPS64N32
| SCMP_ARCH_MIPSEL64N32
) {
return Err(Errno::EINVAL);
}
if matches!(arch, SCMP_ARCH_MIPS | SCMP_ARCH_MIPSEL) && idx >= 4 {
let sp = ptrace_get_stack_ptr(pid, Some(arch))?;
let (arg4, arg5) = ptrace_read_mips_o32_stack_args(pid, arch, sp)?;
return Ok(if idx == 4 { arg4 } else { arg5 });
}
#[expect(clippy::cast_sign_loss)]
Ok(unsafe { ptrace_read_user(pid, (MIPS_REG_A0 + idx) as *mut c_void)? } as u64)
}
#[cfg(target_arch = "riscv64")]
{
use libseccomp_sys::SCMP_ARCH_RISCV64;
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
if let Ok(info) = ptrace_get_syscall_info(pid) {
match info.op {
PTRACE_SYSCALL_INFO_ENTRY => return Ok(unsafe { info.data.entry }.args[idx]),
PTRACE_SYSCALL_INFO_SECCOMP => return Ok(unsafe { info.data.seccomp }.args[idx]),
_ => {} }
}
let mut regs = mem::MaybeUninit::<Riscv64UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
Ok(match idx {
0 => regs.a0,
1 => regs.a1,
2 => regs.a2,
3 => regs.a3,
4 => regs.a4,
5 => regs.a5,
_ => unreachable!(),
})
}
#[cfg(target_arch = "s390x")]
{
use libseccomp_sys::SCMP_ARCH_S390X;
if arch != SCMP_ARCH_S390X {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<S390Regs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<S390Regs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
Ok(if idx == 0 {
regs.orig_gpr2
} else {
regs.gprs[2 + idx]
})
}
#[cfg(target_arch = "powerpc")]
{
use libseccomp_sys::SCMP_ARCH_PPC;
if arch != SCMP_ARCH_PPC {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegs32>::uninit();
unsafe {
ptrace_getregs(
pid,
12,
regs.as_mut_ptr() as *mut c_void,
)
}?;
let regs = unsafe { regs.assume_init() };
Ok(match idx {
0 => regs.orig_gpr3 as u64,
n => regs.gpr[3 + n] as u64,
})
}
#[cfg(target_arch = "powerpc64")]
{
use libseccomp_sys::{SCMP_ARCH_PPC, SCMP_ARCH_PPC64, SCMP_ARCH_PPC64LE};
if !matches!(arch, SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE | SCMP_ARCH_PPC) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<PpcPtRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
match arch {
SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE => {
let regs = unsafe { regs.ppc64 };
Ok(match idx {
0 => regs.orig_gpr3,
n => regs.gpr[3 + n],
})
}
SCMP_ARCH_PPC => {
let regs = unsafe { regs.ppc32 };
Ok(match idx {
0 => regs.orig_gpr3 as u64,
n => regs.gpr[3 + n] as u64,
})
}
_ => Err(Errno::EINVAL),
}
}
#[cfg(target_arch = "m68k")]
{
use libc::PTRACE_GETREGS;
use libseccomp_sys::SCMP_ARCH_M68K;
if arch != SCMP_ARCH_M68K {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<M68KUserRegsStruct>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void) }?;
let regs = unsafe { regs.assume_init() };
Ok(match idx {
0 => regs.d1 as u64,
1 => regs.d2 as u64,
2 => regs.d3 as u64,
3 => regs.d4 as u64,
4 => regs.d5 as u64,
5 => regs.a0 as u64,
_ => unreachable!(),
})
}
#[cfg(target_arch = "loongarch64")]
{
use libseccomp_sys::SCMP_ARCH_LOONGARCH64;
if arch != SCMP_ARCH_LOONGARCH64 {
return Err(Errno::EINVAL);
}
let reg_idx: u64 = match idx {
0 => 32, n => 4 + n as u64,
};
#[expect(clippy::cast_sign_loss)]
Ok(unsafe { ptrace_read_user(pid, reg_idx as *mut c_void)? } as u64)
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "riscv64",
target_arch = "s390x",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "m68k",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "loongarch64",
)))]
{
compile_error!("BUG: ptrace_get_arg is not implemented for this architecture!");
}
}
pub fn ptrace_set_arg(pid: Pid, arch: u32, idx: usize, val: u64) -> Result<(), Errno> {
if idx > 5 {
return Err(Errno::EINVAL);
}
#[cfg(target_arch = "x86_64")]
{
use libseccomp_sys::{SCMP_ARCH_X32, SCMP_ARCH_X86, SCMP_ARCH_X86_64};
match arch {
#[expect(clippy::cast_possible_wrap)]
SCMP_ARCH_X86_64 | SCMP_ARCH_X32 => {
unsafe {
ptrace_write_user(pid, X64_ARG_OFFSETS[idx] as *mut c_void, val as c_long)
}
}
#[expect(clippy::cast_possible_wrap)]
SCMP_ARCH_X86 => {
unsafe {
ptrace_write_user(pid, X86_ARG_OFFSETS[idx] as *mut c_void, val as c_long)
}
}
_ => Err(Errno::EINVAL),
}
}
#[cfg(target_arch = "x86")]
{
use libseccomp_sys::SCMP_ARCH_X86;
if arch != SCMP_ARCH_X86 {
return Err(Errno::EINVAL);
}
unsafe { ptrace_write_user(pid, X86_ARG_OFFSETS[idx] as *mut c_void, val as c_long) }
}
#[cfg(target_arch = "aarch64")]
{
use libseccomp_sys::{SCMP_ARCH_AARCH64, SCMP_ARCH_ARM};
let mut regs = mem::MaybeUninit::<ArmRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
match arch {
SCMP_ARCH_AARCH64 => {
unsafe { regs.aarch64.regs[idx] = val };
}
SCMP_ARCH_ARM => {
#[expect(clippy::cast_possible_truncation)]
unsafe {
regs.arm.uregs[idx] = val as u32;
}
}
_ => return Err(Errno::EINVAL),
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "arm")]
{
use libseccomp_sys::SCMP_ARCH_ARM;
if arch != SCMP_ARCH_ARM {
return Err(Errno::EINVAL);
}
#[expect(clippy::cast_possible_wrap)]
unsafe {
ptrace_write_user(pid, (idx as u64 * 4) as *mut c_void, val as c_long)
}
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
{
use libseccomp_sys::{
SCMP_ARCH_MIPS, SCMP_ARCH_MIPS64, SCMP_ARCH_MIPS64N32, SCMP_ARCH_MIPSEL,
SCMP_ARCH_MIPSEL64, SCMP_ARCH_MIPSEL64N32,
};
if !matches!(
arch,
SCMP_ARCH_MIPS
| SCMP_ARCH_MIPS64
| SCMP_ARCH_MIPSEL
| SCMP_ARCH_MIPSEL64
| SCMP_ARCH_MIPS64N32
| SCMP_ARCH_MIPSEL64N32
) {
return Err(Errno::EINVAL);
}
#[expect(clippy::cast_possible_wrap)]
unsafe {
ptrace_write_user(pid, (MIPS_REG_A0 + idx) as *mut c_void, val as c_long)
}
}
#[cfg(target_arch = "riscv64")]
{
use libseccomp_sys::SCMP_ARCH_RISCV64;
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<Riscv64UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
match idx {
0 => regs.a0 = val,
1 => regs.a1 = val,
2 => regs.a2 = val,
3 => regs.a3 = val,
4 => regs.a4 = val,
5 => regs.a5 = val,
_ => unreachable!(),
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "s390x")]
{
use libseccomp_sys::SCMP_ARCH_S390X;
if arch != SCMP_ARCH_S390X {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<S390Regs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<S390Regs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
if idx == 0 {
regs.orig_gpr2 = val;
} else {
regs.gprs[2 + idx] = val;
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "powerpc")]
{
use libseccomp_sys::SCMP_ARCH_PPC;
if arch != SCMP_ARCH_PPC {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegs32>::uninit();
unsafe {
ptrace_getregs(
pid,
12,
regs.as_mut_ptr() as *mut c_void,
)
}?;
let mut regs = unsafe { regs.assume_init() };
#[expect(clippy::cast_possible_truncation)]
let val = val as u32;
match idx {
0 => {
regs.orig_gpr3 = val;
regs.gpr[3] = val;
}
n => regs.gpr[3 + n] = val,
}
unsafe {
ptrace_setregs(
pid,
13,
®s as *const _ as *mut c_void,
)
}
}
#[cfg(target_arch = "powerpc64")]
{
use libseccomp_sys::{SCMP_ARCH_PPC, SCMP_ARCH_PPC64, SCMP_ARCH_PPC64LE};
if !matches!(arch, SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE | SCMP_ARCH_PPC) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<PpcPtRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
match arch {
SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE => {
let regs = unsafe { &mut regs.ppc64 };
match idx {
0 => {
regs.orig_gpr3 = val;
regs.gpr[3] = val;
}
n => regs.gpr[3 + n] = val,
}
}
SCMP_ARCH_PPC => {
#[expect(clippy::cast_possible_truncation)]
let val = val as u32;
let regs = unsafe { &mut regs.ppc32 };
match idx {
0 => {
regs.orig_gpr3 = val;
regs.gpr[3] = val;
}
n => regs.gpr[3 + n] = val,
}
}
_ => return Err(Errno::EINVAL),
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "m68k")]
{
use libc::{PTRACE_GETREGS, PTRACE_SETREGS};
use libseccomp_sys::SCMP_ARCH_M68K;
if arch != SCMP_ARCH_M68K {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<M68KUserRegsStruct>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void) }?;
let mut regs = unsafe { regs.assume_init() };
#[expect(clippy::cast_possible_truncation)]
let val = val as c_long;
match idx {
0 => regs.d1 = val,
1 => regs.d2 = val,
2 => regs.d3 = val,
3 => regs.d4 = val,
4 => regs.d5 = val,
5 => regs.a0 = val,
_ => unreachable!(),
}
unsafe { ptrace_setregs(pid, PTRACE_SETREGS, ®s as *const _ as *mut c_void) }
}
#[cfg(target_arch = "loongarch64")]
{
use libseccomp_sys::SCMP_ARCH_LOONGARCH64;
if arch != SCMP_ARCH_LOONGARCH64 {
return Err(Errno::EINVAL);
}
let reg_idx: u64 = match idx {
0 => 32, n => 4 + n as u64,
};
#[expect(clippy::cast_possible_wrap)]
unsafe {
ptrace_write_user(pid, reg_idx as *mut c_void, val as c_long)
}
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "riscv64",
target_arch = "s390x",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "m68k",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "loongarch64",
)))]
{
compile_error!("BUG: ptrace_set_arg is not implemented for this architecture!");
}
}
pub fn ptrace_get_args(pid: Pid, arch: u32) -> Result<[u64; 6], Errno> {
#[cfg(target_arch = "x86_64")]
{
use libseccomp_sys::{SCMP_ARCH_X32, SCMP_ARCH_X86, SCMP_ARCH_X86_64};
if !matches!(arch, SCMP_ARCH_X86_64 | SCMP_ARCH_X86 | SCMP_ARCH_X32) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<X86UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<X86UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
match arch {
SCMP_ARCH_X86_64 | SCMP_ARCH_X32 => {
let r = unsafe { regs.x64 };
Ok([r.rdi, r.rsi, r.rdx, r.r10, r.r8, r.r9])
}
SCMP_ARCH_X86 => {
let r = unsafe { regs.x32 };
Ok([
u64::from(r.ebx),
u64::from(r.ecx),
u64::from(r.edx),
u64::from(r.esi),
u64::from(r.edi),
u64::from(r.ebp),
])
}
_ => Err(Errno::EINVAL),
}
}
#[cfg(target_arch = "x86")]
{
use libseccomp_sys::SCMP_ARCH_X86;
if arch != SCMP_ARCH_X86 {
return Err(Errno::EINVAL);
}
let mut args = [0u64; 6];
for (idx, &off) in X86_ARG_OFFSETS.iter().enumerate() {
args[idx] = unsafe { ptrace_read_user(pid, off as *mut c_void)? } as u64;
}
Ok(args)
}
#[cfg(target_arch = "aarch64")]
{
use libseccomp_sys::{SCMP_ARCH_AARCH64, SCMP_ARCH_ARM};
let mut regs = mem::MaybeUninit::<ArmRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
match arch {
SCMP_ARCH_AARCH64 => {
let r = unsafe { regs.aarch64 };
Ok([
r.regs[0], r.regs[1], r.regs[2], r.regs[3], r.regs[4], r.regs[5],
])
}
SCMP_ARCH_ARM => {
let r = unsafe { regs.arm };
Ok([
u64::from(r.uregs[0]),
u64::from(r.uregs[1]),
u64::from(r.uregs[2]),
u64::from(r.uregs[3]),
u64::from(r.uregs[4]),
u64::from(r.uregs[5]),
])
}
_ => Err(Errno::EINVAL),
}
}
#[cfg(target_arch = "arm")]
{
use libseccomp_sys::SCMP_ARCH_ARM;
if arch != SCMP_ARCH_ARM {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<ArmPtRegs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmPtRegs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
Ok([
u64::from(regs.uregs[0]),
u64::from(regs.uregs[1]),
u64::from(regs.uregs[2]),
u64::from(regs.uregs[3]),
u64::from(regs.uregs[4]),
u64::from(regs.uregs[5]),
])
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
{
use libc::PTRACE_GETREGS;
use libseccomp_sys::{
SCMP_ARCH_MIPS, SCMP_ARCH_MIPS64, SCMP_ARCH_MIPS64N32, SCMP_ARCH_MIPSEL,
SCMP_ARCH_MIPSEL64, SCMP_ARCH_MIPSEL64N32,
};
if !matches!(
arch,
SCMP_ARCH_MIPS
| SCMP_ARCH_MIPS64
| SCMP_ARCH_MIPSEL
| SCMP_ARCH_MIPSEL64
| SCMP_ARCH_MIPS64N32
| SCMP_ARCH_MIPSEL64N32
) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<MipsPtRegs>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void) }?;
let regs = unsafe { regs.assume_init() };
let mut args = [
regs.regs[MIPS_REG_A0],
regs.regs[MIPS_REG_A0 + 1],
regs.regs[MIPS_REG_A0 + 2],
regs.regs[MIPS_REG_A0 + 3],
regs.regs[MIPS_REG_A0 + 4],
regs.regs[MIPS_REG_A0 + 5],
];
if matches!(arch, SCMP_ARCH_MIPS | SCMP_ARCH_MIPSEL) {
let (arg4, arg5) = ptrace_read_mips_o32_stack_args(pid, arch, regs.regs[29])?;
args[4] = arg4;
args[5] = arg5;
}
Ok(args)
}
#[cfg(target_arch = "riscv64")]
{
use libseccomp_sys::SCMP_ARCH_RISCV64;
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
if let Ok(info) = ptrace_get_syscall_info(pid) {
match info.op {
PTRACE_SYSCALL_INFO_ENTRY => return Ok(unsafe { info.data.entry }.args),
PTRACE_SYSCALL_INFO_SECCOMP => return Ok(unsafe { info.data.seccomp }.args),
_ => {} }
}
let mut regs = mem::MaybeUninit::<Riscv64UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
Ok([regs.a0, regs.a1, regs.a2, regs.a3, regs.a4, regs.a5])
}
#[cfg(target_arch = "s390x")]
{
use libseccomp_sys::SCMP_ARCH_S390X;
if arch != SCMP_ARCH_S390X {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<S390Regs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<S390Regs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
Ok([
regs.orig_gpr2,
regs.gprs[3],
regs.gprs[4],
regs.gprs[5],
regs.gprs[6],
regs.gprs[7],
])
}
#[cfg(target_arch = "powerpc")]
{
use libseccomp_sys::SCMP_ARCH_PPC;
if arch != SCMP_ARCH_PPC {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegs32>::uninit();
unsafe {
ptrace_getregs(
pid,
12,
regs.as_mut_ptr() as *mut c_void,
)
}?;
let r = unsafe { regs.assume_init() };
Ok([
r.orig_gpr3 as u64,
r.gpr[4] as u64,
r.gpr[5] as u64,
r.gpr[6] as u64,
r.gpr[7] as u64,
r.gpr[8] as u64,
])
}
#[cfg(target_arch = "powerpc64")]
{
use libseccomp_sys::{SCMP_ARCH_PPC, SCMP_ARCH_PPC64, SCMP_ARCH_PPC64LE};
if !matches!(arch, SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE | SCMP_ARCH_PPC) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<PpcPtRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
match arch {
SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE => {
let r = unsafe { regs.ppc64 };
Ok([
r.orig_gpr3,
r.gpr[4],
r.gpr[5],
r.gpr[6],
r.gpr[7],
r.gpr[8],
])
}
SCMP_ARCH_PPC => {
let r = unsafe { regs.ppc32 };
Ok([
r.orig_gpr3 as u64,
r.gpr[4] as u64,
r.gpr[5] as u64,
r.gpr[6] as u64,
r.gpr[7] as u64,
r.gpr[8] as u64,
])
}
_ => Err(Errno::EINVAL),
}
}
#[cfg(target_arch = "m68k")]
{
use libc::PTRACE_GETREGS;
use libseccomp_sys::SCMP_ARCH_M68K;
if arch != SCMP_ARCH_M68K {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<M68KUserRegsStruct>::uninit();
ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void)?;
let r = unsafe { regs.assume_init() };
Ok([
r.d1 as u64,
r.d2 as u64,
r.d3 as u64,
r.d4 as u64,
r.d5 as u64,
r.a0 as u64,
])
}
#[cfg(target_arch = "loongarch64")]
{
use libseccomp_sys::SCMP_ARCH_LOONGARCH64;
if arch != SCMP_ARCH_LOONGARCH64 {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<LoongarchUserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<LoongarchUserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
Ok([
regs.orig_a0,
regs.regs[5],
regs.regs[6],
regs.regs[7],
regs.regs[8],
regs.regs[9],
])
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "riscv64",
target_arch = "s390x",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "m68k",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "loongarch64",
)))]
{
compile_error!("BUG: ptrace_get_args is not implemented for this architecture!");
}
}
pub fn ptrace_set_args(pid: Pid, arch: u32, args: [u64; 6]) -> Result<(), Errno> {
#[cfg(target_arch = "x86_64")]
{
use libseccomp_sys::{SCMP_ARCH_X32, SCMP_ARCH_X86, SCMP_ARCH_X86_64};
if !matches!(arch, SCMP_ARCH_X86_64 | SCMP_ARCH_X86 | SCMP_ARCH_X32) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<X86UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<X86UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
match arch {
SCMP_ARCH_X86_64 | SCMP_ARCH_X32 => {
let r = unsafe { &mut regs.x64 };
r.rdi = args[0];
r.rsi = args[1];
r.rdx = args[2];
r.r10 = args[3];
r.r8 = args[4];
r.r9 = args[5];
}
SCMP_ARCH_X86 => {
#[expect(clippy::cast_possible_truncation)]
{
let r = unsafe { &mut regs.x32 };
r.ebx = args[0] as u32;
r.ecx = args[1] as u32;
r.edx = args[2] as u32;
r.esi = args[3] as u32;
r.edi = args[4] as u32;
r.ebp = args[5] as u32;
}
}
_ => return Err(Errno::EINVAL),
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "x86")]
{
use libseccomp_sys::SCMP_ARCH_X86;
if arch != SCMP_ARCH_X86 {
return Err(Errno::EINVAL);
}
for (idx, &off) in X86_ARG_OFFSETS.iter().enumerate() {
unsafe { ptrace_write_user(pid, off as *mut c_void, args[idx] as c_long) }?;
}
Ok(())
}
#[cfg(target_arch = "aarch64")]
{
use libseccomp_sys::{SCMP_ARCH_AARCH64, SCMP_ARCH_ARM};
let mut regs = mem::MaybeUninit::<ArmRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
match arch {
SCMP_ARCH_AARCH64 => {
let r = unsafe { &mut regs.aarch64 };
for idx in 0..6 {
r.regs[idx] = args[idx];
}
}
SCMP_ARCH_ARM => {
let r = unsafe { &mut regs.arm };
#[expect(clippy::cast_possible_truncation)]
for idx in 0..6 {
r.uregs[idx] = args[idx] as u32;
}
}
_ => return Err(Errno::EINVAL),
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "arm")]
{
use libseccomp_sys::SCMP_ARCH_ARM;
if arch != SCMP_ARCH_ARM {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<ArmPtRegs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmPtRegs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
#[expect(clippy::cast_possible_truncation)]
for idx in 0..6 {
regs.uregs[idx] = args[idx] as u32;
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
{
use libc::{PTRACE_GETREGS, PTRACE_SETREGS};
use libseccomp_sys::{
SCMP_ARCH_MIPS, SCMP_ARCH_MIPS64, SCMP_ARCH_MIPS64N32, SCMP_ARCH_MIPSEL,
SCMP_ARCH_MIPSEL64, SCMP_ARCH_MIPSEL64N32,
};
if !matches!(
arch,
SCMP_ARCH_MIPS
| SCMP_ARCH_MIPS64
| SCMP_ARCH_MIPSEL
| SCMP_ARCH_MIPSEL64
| SCMP_ARCH_MIPS64N32
| SCMP_ARCH_MIPSEL64N32
) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<MipsPtRegs>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void) }?;
let mut regs = unsafe { regs.assume_init() };
for idx in 0..6 {
regs.regs[MIPS_REG_A0 + idx] = args[idx];
}
unsafe { ptrace_setregs(pid, PTRACE_SETREGS, ®s as *const _ as *mut c_void) }
}
#[cfg(target_arch = "riscv64")]
{
use libseccomp_sys::SCMP_ARCH_RISCV64;
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<Riscv64UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
regs.a0 = args[0];
regs.a1 = args[1];
regs.a2 = args[2];
regs.a3 = args[3];
regs.a4 = args[4];
regs.a5 = args[5];
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "s390x")]
{
use libseccomp_sys::SCMP_ARCH_S390X;
if arch != SCMP_ARCH_S390X {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<S390Regs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<S390Regs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
regs.orig_gpr2 = args[0];
for idx in 1..6 {
regs.gprs[2 + idx] = args[idx];
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "powerpc")]
{
use libseccomp_sys::SCMP_ARCH_PPC;
if arch != SCMP_ARCH_PPC {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegs32>::uninit();
unsafe {
ptrace_getregs(
pid,
12,
regs.as_mut_ptr() as *mut c_void,
)
}?;
let mut r = unsafe { regs.assume_init() };
#[expect(clippy::cast_possible_truncation)]
{
r.orig_gpr3 = args[0] as u32;
r.gpr[3] = args[0] as u32;
r.gpr[4] = args[1] as u32;
r.gpr[5] = args[2] as u32;
r.gpr[6] = args[3] as u32;
r.gpr[7] = args[4] as u32;
r.gpr[8] = args[5] as u32;
}
unsafe { ptrace_setregs(pid, 13, &r as *const _ as *mut c_void) }
}
#[cfg(target_arch = "powerpc64")]
{
use libseccomp_sys::{SCMP_ARCH_PPC, SCMP_ARCH_PPC64, SCMP_ARCH_PPC64LE};
if !matches!(arch, SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE | SCMP_ARCH_PPC) {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<PpcPtRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
match arch {
SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE => {
let r = unsafe { &mut regs.ppc64 };
r.orig_gpr3 = args[0];
r.gpr[3] = args[0];
r.gpr[4] = args[1];
r.gpr[5] = args[2];
r.gpr[6] = args[3];
r.gpr[7] = args[4];
r.gpr[8] = args[5];
}
SCMP_ARCH_PPC => {
let r = unsafe { &mut regs.ppc32 };
r.orig_gpr3 = args[0] as u32;
r.gpr[3] = args[0] as u32;
r.gpr[4] = args[1] as u32;
r.gpr[5] = args[2] as u32;
r.gpr[6] = args[3] as u32;
r.gpr[7] = args[4] as u32;
r.gpr[8] = args[5] as u32;
}
_ => return Err(Errno::EINVAL),
}
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(target_arch = "m68k")]
{
use libc::{PTRACE_GETREGS, PTRACE_SETREGS};
use libseccomp_sys::SCMP_ARCH_M68K;
if arch != SCMP_ARCH_M68K {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<M68KUserRegsStruct>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void) }?;
let mut r = unsafe { regs.assume_init() };
#[expect(clippy::cast_possible_truncation)]
{
r.d1 = args[0] as c_long;
r.d2 = args[1] as c_long;
r.d3 = args[2] as c_long;
r.d4 = args[3] as c_long;
r.d5 = args[4] as c_long;
r.a0 = args[5] as c_long;
}
unsafe { ptrace_setregs(pid, PTRACE_SETREGS, &r as *const _ as *mut c_void) }
}
#[cfg(target_arch = "loongarch64")]
{
use libseccomp_sys::SCMP_ARCH_LOONGARCH64;
if arch != SCMP_ARCH_LOONGARCH64 {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<LoongarchUserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<LoongarchUserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_mut() };
regs.orig_a0 = args[0];
regs.regs[5] = args[1];
regs.regs[6] = args[2];
regs.regs[7] = args[3];
regs.regs[8] = args[4];
regs.regs[9] = args[5];
ptrace_setregset(pid, NT_PRSTATUS, &io)
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "riscv64",
target_arch = "s390x",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "m68k",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "loongarch64",
)))]
{
compile_error!("BUG: ptrace_set_args is not implemented for this architecture!");
}
}
pub fn ptrace_getsiginfo(pid: Pid) -> Result<siginfo_t, Errno> {
let mut info = mem::MaybeUninit::<siginfo_t>::uninit();
Errno::result(unsafe {
safe_ptrace(
PTRACE_GETSIGINFO,
pid.as_raw(),
ptr::null_mut(),
info.as_mut_ptr() as *mut c_void,
)
})?;
Ok(unsafe { info.assume_init() })
}
pub fn ptrace_get_stack_ptr(pid: Pid, arch: Option<u32>) -> Result<u64, Errno> {
#[cfg(target_arch = "x86_64")]
{
use libseccomp_sys::{SCMP_ARCH_X32, SCMP_ARCH_X86, SCMP_ARCH_X86_64};
const RSP_OFFSET: u64 = 19 * 8;
match arch {
Some(SCMP_ARCH_X86_64 | SCMP_ARCH_X32) => {
#[expect(clippy::cast_sign_loss)]
Ok(unsafe { ptrace_read_user(pid, RSP_OFFSET as *mut c_void)? } as u64)
}
Some(SCMP_ARCH_X86) => {
#[expect(clippy::cast_sign_loss)]
let esp = unsafe { ptrace_read_user(pid, RSP_OFFSET as *mut c_void)? } as u64;
Ok(esp & 0xFFFF_FFFF)
}
Some(_) => Err(Errno::EINVAL),
None => {
let mut regs = mem::MaybeUninit::<X86UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<X86UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
if io.iov_len == mem::size_of::<I386UserRegsStruct>() {
return Ok(u64::from(unsafe { regs.x32 }.esp));
}
Ok(unsafe { regs.x64 }.rsp)
}
}
}
#[cfg(target_arch = "x86")]
{
use libseccomp_sys::SCMP_ARCH_X86;
if let Some(arch) = arch {
if arch != SCMP_ARCH_X86 {
return Err(Errno::EINVAL);
}
}
const ESP_OFFSET: u64 = 15 * 4;
#[expect(clippy::cast_sign_loss)]
let esp = unsafe { ptrace_read_user(pid, ESP_OFFSET as *mut c_void)? } as u64;
Ok(esp & 0xFFFF_FFFF)
}
#[cfg(target_arch = "aarch64")]
{
use libseccomp_sys::{SCMP_ARCH_AARCH64, SCMP_ARCH_ARM};
if let Some(arch) = arch {
if !matches!(arch, SCMP_ARCH_AARCH64 | SCMP_ARCH_ARM) {
return Err(Errno::EINVAL);
}
}
let mut regs = mem::MaybeUninit::<ArmRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
match arch {
Some(SCMP_ARCH_AARCH64) => {
Ok(unsafe { regs.aarch64 }.sp)
}
Some(SCMP_ARCH_ARM) => {
Ok(u64::from(unsafe { regs.arm }.uregs[13]))
}
Some(_) => Err(Errno::EINVAL),
None => {
if io.iov_len == mem::size_of::<ArmPtRegs>() {
Ok(u64::from(unsafe { regs.arm }.uregs[13]))
} else {
Ok(unsafe { regs.aarch64 }.sp)
}
}
}
}
#[cfg(target_arch = "arm")]
{
use libseccomp_sys::SCMP_ARCH_ARM;
if let Some(arch) = arch {
if arch != SCMP_ARCH_ARM {
return Err(Errno::EINVAL);
}
}
const SP_OFFSET: u64 = 13 * 4;
#[expect(clippy::cast_sign_loss)]
let sp = unsafe { ptrace_read_user(pid, SP_OFFSET as *mut c_void)? } as u64;
Ok(sp & 0xFFFF_FFFF)
}
#[cfg(target_arch = "m68k")]
{
use libseccomp_sys::SCMP_ARCH_M68K;
if let Some(arch) = arch {
if arch != SCMP_ARCH_M68K {
return Err(Errno::EINVAL);
}
}
const USP_OFFSET: u64 = 15 * 4;
#[expect(clippy::cast_sign_loss)]
let usp = unsafe { ptrace_read_user(pid, USP_OFFSET as *mut c_void)? } as u64;
Ok(usp & 0xFFFF_FFFF)
}
#[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
{
use libseccomp_sys::{SCMP_ARCH_MIPS, SCMP_ARCH_MIPSEL};
if let Some(arch) = arch {
if !matches!(arch, SCMP_ARCH_MIPS | SCMP_ARCH_MIPSEL) {
return Err(Errno::EINVAL);
}
}
const SP_REG: u64 = 29;
#[expect(clippy::cast_sign_loss)]
let sp = unsafe { ptrace_read_user(pid, SP_REG as *mut c_void)? } as u64;
Ok(sp & 0xFFFF_FFFF)
}
#[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))]
{
use libseccomp_sys::{
SCMP_ARCH_MIPS, SCMP_ARCH_MIPS64, SCMP_ARCH_MIPS64N32, SCMP_ARCH_MIPSEL,
SCMP_ARCH_MIPSEL64, SCMP_ARCH_MIPSEL64N32,
};
if let Some(arch) = arch {
if !matches!(
arch,
SCMP_ARCH_MIPS
| SCMP_ARCH_MIPS64
| SCMP_ARCH_MIPS64N32
| SCMP_ARCH_MIPSEL
| SCMP_ARCH_MIPSEL64
| SCMP_ARCH_MIPSEL64N32
) {
return Err(Errno::EINVAL);
}
}
const SP_REG: u64 = 29;
#[expect(clippy::cast_sign_loss)]
Ok(unsafe { ptrace_read_user(pid, SP_REG as *mut c_void)? } as u64)
}
#[cfg(target_arch = "powerpc")]
{
use libseccomp_sys::SCMP_ARCH_PPC;
if let Some(arch) = arch {
if arch != SCMP_ARCH_PPC {
return Err(Errno::EINVAL);
}
}
const SP_OFFSET: u64 = 1 * 4;
#[expect(clippy::cast_sign_loss)]
let sp = unsafe { ptrace_read_user(pid, SP_OFFSET as *mut c_void)? } as u64;
Ok(sp & 0xFFFF_FFFF)
}
#[cfg(target_arch = "powerpc64")]
{
use libseccomp_sys::{SCMP_ARCH_PPC, SCMP_ARCH_PPC64, SCMP_ARCH_PPC64LE};
if let Some(arch) = arch {
if !matches!(arch, SCMP_ARCH_PPC | SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE) {
return Err(Errno::EINVAL);
}
}
const SP_OFFSET: u64 = 1 * 8;
#[expect(clippy::cast_sign_loss)]
Ok(unsafe { ptrace_read_user(pid, SP_OFFSET as *mut c_void)? } as u64)
}
#[cfg(target_arch = "s390x")]
{
use libseccomp_sys::{SCMP_ARCH_S390, SCMP_ARCH_S390X};
if let Some(arch) = arch {
if !matches!(arch, SCMP_ARCH_S390 | SCMP_ARCH_S390X) {
return Err(Errno::EINVAL);
}
}
const SP_OFFSET: u64 = 16 + 15 * 8;
#[expect(clippy::cast_sign_loss)]
Ok(unsafe { ptrace_read_user(pid, SP_OFFSET as *mut c_void)? } as u64)
}
#[cfg(target_arch = "riscv64")]
{
use libseccomp_sys::SCMP_ARCH_RISCV64;
if let Some(arch) = arch {
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
}
let mut regs = mem::MaybeUninit::<Riscv64UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
Ok(regs.sp)
}
#[cfg(target_arch = "loongarch64")]
{
use libseccomp_sys::SCMP_ARCH_LOONGARCH64;
if let Some(arch) = arch {
if arch != SCMP_ARCH_LOONGARCH64 {
return Err(Errno::EINVAL);
}
}
let mut regs = mem::MaybeUninit::<LoongarchUserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<LoongarchUserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
Ok(regs.regs[3])
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "m68k",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "riscv64",
target_arch = "loongarch64",
)))]
{
compile_error!("BUG: ptrace_stack_ptr is not implemented for this architecture!");
}
}
pub fn ptrace_get_link_register(pid: Pid) -> Result<u64, Errno> {
#[cfg(target_arch = "aarch64")]
{
let mut regs = mem::MaybeUninit::<ArmRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
let is_arm = io.iov_len == mem::size_of::<ArmPtRegs>();
if is_arm {
return Ok(u64::from(unsafe { regs.arm }.uregs[14]));
}
return Ok(unsafe { regs.aarch64 }.regs[30]);
}
#[cfg(target_arch = "arm")]
{
let mut regs = mem::MaybeUninit::<ArmPtRegs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmPtRegs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
return Ok(u64::from(regs.uregs[14]));
}
#[cfg(target_arch = "riscv64")]
{
let mut regs = mem::MaybeUninit::<Riscv64UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
return Ok(regs.ra);
}
#[cfg(target_arch = "loongarch64")]
{
let mut regs = mem::MaybeUninit::<LoongarchUserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<LoongarchUserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
return Ok(regs.regs[1]);
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
{
use libc::PTRACE_GETREGS;
let mut regs = mem::MaybeUninit::<MipsPtRegs>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void)? };
let regs = unsafe { regs.assume_init() };
return Ok(regs.regs[31] as u64);
}
#[cfg(target_arch = "powerpc")]
{
use libc::PTRACE_GETREGS;
let mut regs = mem::MaybeUninit::<PpcPtRegs32>::uninit();
unsafe { ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void)? };
let regs = unsafe { regs.assume_init() };
return Ok(u64::from(regs.link));
}
#[cfg(target_arch = "powerpc64")]
{
const MSR_SF: libc::c_ulong = 1 << 63;
let mut regs = mem::MaybeUninit::<PpcPtRegs64>::uninit();
unsafe { ptrace_getregs(pid, 12, regs.as_mut_ptr() as *mut c_void)? };
let regs = unsafe { regs.assume_init() };
return if regs.msr & MSR_SF == 0 {
Ok(regs.link)
} else {
Ok(regs.nip)
};
}
#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "m68k"))]
{
let _ = pid;
Err(Errno::ENOSYS)
}
#[cfg(target_arch = "s390x")]
{
let mut regs = mem::MaybeUninit::<S390Regs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<S390Regs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
return Ok(regs.gprs[14]);
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "m68k",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "riscv64",
target_arch = "loongarch64",
)))]
{
compile_error!("BUG: ptrace_get_link_register is not implemented for this architecture!");
}
}
pub fn ptrace_get_arch(pid: Pid) -> Result<u32, Errno> {
#[cfg(target_arch = "x86_64")]
{
use libseccomp_sys::{SCMP_ARCH_X32, SCMP_ARCH_X86, SCMP_ARCH_X86_64};
const X32_BIT: u64 = 0x4000_0000;
let mut regs = mem::MaybeUninit::<X86UserRegsStruct>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<X86UserRegsStruct>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
if io.iov_len == mem::size_of::<I386UserRegsStruct>() {
return Ok(SCMP_ARCH_X86);
}
let orig_rax = unsafe { regs.x64 }.orig_rax;
#[expect(clippy::cast_possible_wrap)]
if (orig_rax as i64) != -1 && (orig_rax & X32_BIT) != 0 {
return Ok(SCMP_ARCH_X32);
}
Ok(SCMP_ARCH_X86_64)
}
#[cfg(target_arch = "x86")]
{
use libseccomp_sys::SCMP_ARCH_X86;
let _ = pid;
Ok(SCMP_ARCH_X86)
}
#[cfg(target_arch = "aarch64")]
{
use libseccomp_sys::{SCMP_ARCH_AARCH64, SCMP_ARCH_ARM};
let mut regs = mem::MaybeUninit::<ArmRegsUnion>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<ArmRegsUnion>(),
};
ptrace_getregset(pid, &mut io)?;
if io.iov_len == mem::size_of::<ArmPtRegs>() {
Ok(SCMP_ARCH_ARM)
} else {
Ok(SCMP_ARCH_AARCH64)
}
}
#[cfg(target_arch = "arm")]
{
use libseccomp_sys::SCMP_ARCH_ARM;
let _ = pid;
Ok(SCMP_ARCH_ARM)
}
#[cfg(target_arch = "m68k")]
{
use libseccomp_sys::SCMP_ARCH_M68K;
let _ = pid;
Ok(SCMP_ARCH_M68K)
}
#[cfg(all(
any(target_arch = "mips", target_arch = "mips32r6"),
target_endian = "big"
))]
{
use libseccomp_sys::SCMP_ARCH_MIPS;
let _ = pid;
Ok(SCMP_ARCH_MIPS)
}
#[cfg(all(
any(target_arch = "mips", target_arch = "mips32r6"),
target_endian = "little"
))]
{
use libseccomp_sys::SCMP_ARCH_MIPSEL;
let _ = pid;
Ok(SCMP_ARCH_MIPSEL)
}
#[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))]
{
use libc::PTRACE_GETREGS;
use libseccomp_sys::{
SCMP_ARCH_MIPS, SCMP_ARCH_MIPS64, SCMP_ARCH_MIPS64N32, SCMP_ARCH_MIPSEL,
SCMP_ARCH_MIPSEL64, SCMP_ARCH_MIPSEL64N32,
};
const IS_LE: bool = cfg!(target_endian = "little");
const ST0_UX: u64 = 0x0000_0020;
let mut regs = mem::MaybeUninit::<MipsPtRegs>::uninit();
unsafe {
ptrace_getregs(pid, PTRACE_GETREGS, regs.as_mut_ptr() as *mut c_void)?;
}
let regs = unsafe { regs.assume_init_ref() };
if regs.cp0_status & ST0_UX == 0 {
return Ok(if IS_LE {
SCMP_ARCH_MIPSEL
} else {
SCMP_ARCH_MIPS
});
}
Ok(if IS_LE {
SCMP_ARCH_MIPSEL64
} else {
SCMP_ARCH_MIPS64
})
}
#[cfg(target_arch = "powerpc")]
{
use libseccomp_sys::SCMP_ARCH_PPC;
let _ = pid;
Ok(SCMP_ARCH_PPC)
}
#[cfg(target_arch = "powerpc64")]
{
use libseccomp_sys::{SCMP_ARCH_PPC, SCMP_ARCH_PPC64, SCMP_ARCH_PPC64LE};
const MSR_SF: libc::c_ulong = 1 << 63;
let mut regs = mem::MaybeUninit::<PpcPtRegs64>::uninit();
unsafe {
ptrace_getregs(
pid,
12,
regs.as_mut_ptr() as *mut c_void,
)
}?;
let regs = unsafe { regs.assume_init() };
if regs.msr & MSR_SF == 0 {
return Ok(SCMP_ARCH_PPC);
}
Ok(if cfg!(target_endian = "little") {
SCMP_ARCH_PPC64LE
} else {
SCMP_ARCH_PPC64
})
}
#[cfg(target_arch = "s390x")]
{
use libseccomp_sys::{SCMP_ARCH_S390, SCMP_ARCH_S390X};
const PSW_MASK_EA: u64 = 1 << 32;
const PSW_MASK_BA: u64 = 1 << 31;
let mut regs = mem::MaybeUninit::<S390Regs>::uninit();
let mut io = iovec {
iov_base: regs.as_mut_ptr() as *mut c_void,
iov_len: mem::size_of::<S390Regs>(),
};
ptrace_getregset(pid, &mut io)?;
let regs = unsafe { regs.assume_init_ref() };
if (regs.psw.mask & (PSW_MASK_EA | PSW_MASK_BA)) == (PSW_MASK_EA | PSW_MASK_BA) {
Ok(SCMP_ARCH_S390X)
} else {
Ok(SCMP_ARCH_S390)
}
}
#[cfg(target_arch = "riscv64")]
{
use libseccomp_sys::SCMP_ARCH_RISCV64;
let _ = pid;
Ok(SCMP_ARCH_RISCV64)
}
#[cfg(target_arch = "loongarch64")]
{
use libseccomp_sys::SCMP_ARCH_LOONGARCH64;
let _ = pid;
Ok(SCMP_ARCH_LOONGARCH64)
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "m68k",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "riscv64",
target_arch = "loongarch64",
)))]
{
compile_error!("BUG: ptrace_get_arch is not implemented for this architecture!");
}
}
#[allow(unused)] fn check_negated_errno(val: i64) -> Option<Errno> {
const MIN_ERRNO: i64 = -4095;
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::cast_possible_truncation)]
if (MIN_ERRNO..0).contains(&val) {
Some(Errno::from_raw((-val) as i32))
} else {
None
}
}
pub const PTRACE_SYSCALL_INFO_NONE: u8 = 0;
pub const PTRACE_SYSCALL_INFO_ENTRY: u8 = 1;
pub const PTRACE_SYSCALL_INFO_EXIT: u8 = 2;
pub const PTRACE_SYSCALL_INFO_SECCOMP: u8 = 3;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ptrace_syscall_info {
pub op: u8,
pub reserved: u8,
pub flags: u16,
pub arch: u32,
pub instruction_pointer: u64,
pub stack_pointer: u64,
pub data: ptrace_syscall_info_data,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub union ptrace_syscall_info_data {
pub entry: ptrace_syscall_info_entry,
pub exit: ptrace_syscall_info_exit,
pub seccomp: ptrace_syscall_info_seccomp,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct ptrace_syscall_info_entry {
pub nr: u64,
pub args: [u64; 6],
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct ptrace_syscall_info_exit {
pub rval: i64,
pub is_error: u8,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct ptrace_syscall_info_seccomp {
pub nr: u64,
pub args: [u64; 6],
pub ret_data: u32,
pub reserved2: u32,
}
impl std::fmt::Debug for ptrace_syscall_info {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ptrace_syscall_info")
.field("op", &self.op)
.field("arch", &self.arch)
.field("ip", &self.instruction_pointer)
.field("sp", &self.stack_pointer)
.field("data", unsafe {
match self.op {
PTRACE_SYSCALL_INFO_ENTRY => &self.data.entry,
PTRACE_SYSCALL_INFO_EXIT => &self.data.exit,
PTRACE_SYSCALL_INFO_SECCOMP => &self.data.seccomp,
_ => &"Unknown op",
}
})
.finish()
}
}
impl ptrace_syscall_info {
pub fn is_none(&self) -> bool {
self.op == PTRACE_SYSCALL_INFO_NONE
}
pub fn entry(&self) -> Option<ptrace_syscall_info_entry> {
if self.op != PTRACE_SYSCALL_INFO_ENTRY {
return None;
}
Some(unsafe { self.data.entry })
}
pub fn exit(&self) -> Option<ptrace_syscall_info_exit> {
if self.op != PTRACE_SYSCALL_INFO_EXIT {
return None;
}
Some(unsafe { self.data.exit })
}
pub fn seccomp(&self) -> Option<ptrace_syscall_info_seccomp> {
if self.op != PTRACE_SYSCALL_INFO_SECCOMP {
return None;
}
Some(unsafe { self.data.seccomp })
}
pub fn syscall(&self) -> Option<&'static XPath> {
let nr = if let Some(info) = self.entry() {
info.nr
} else if let Some(info) = self.seccomp() {
info.nr
} else {
return None;
};
#[expect(clippy::cast_possible_truncation)]
let ptr = unsafe { seccomp_syscall_resolve_num_arch(self.arch, nr as i32) };
if ptr.is_null() {
return None;
}
Some(XPath::from_bytes(unsafe { CStr::from_ptr(ptr) }.to_bytes()))
}
}
#[inline(always)]
pub fn ptrace_cont(pid: Pid, sig: Option<i32>) -> Result<(), Errno> {
let data = match sig {
Some(s) => s as *mut c_void,
None => ptr::null_mut(),
};
Errno::result(unsafe { safe_ptrace(PTRACE_CONT, pid.as_raw(), ptr::null_mut(), data) })
.map(drop)
}
#[inline(always)]
pub fn ptrace_listen(pid: Pid) -> Result<(), Errno> {
Errno::result(unsafe {
safe_ptrace(
PTRACE_LISTEN,
pid.as_raw(),
ptr::null_mut(),
ptr::null_mut(),
)
})
.map(drop)
}
#[inline(always)]
pub fn ptrace_syscall(pid: Pid, sig: Option<i32>) -> Result<(), Errno> {
let data = match sig {
Some(s) => s as *mut c_void,
None => ptr::null_mut(),
};
Errno::result(unsafe { safe_ptrace(PTRACE_SYSCALL, pid.as_raw(), ptr::null_mut(), data) })
.map(drop)
}
#[inline(always)]
pub fn ptrace_getevent(pid: Pid) -> Result<c_long, Errno> {
let mut data: c_long = 0;
Errno::result(unsafe {
safe_ptrace(
PTRACE_GETEVENTMSG,
pid.as_raw(),
ptr::null_mut(),
(&raw mut data) as *mut c_void,
)
})?;
Ok(data)
}
#[inline(always)]
pub unsafe fn ptrace_write_user(pid: Pid, addr: *mut c_void, data: c_long) -> Result<(), Errno> {
Errno::result(unsafe { safe_ptrace(PTRACE_POKEUSER, pid.as_raw(), addr, data as *mut c_void) })
.map(drop)
}
#[inline(always)]
pub unsafe fn ptrace_read_user(pid: Pid, addr: *mut c_void) -> Result<c_long, Errno> {
let mut data: c_long = 0;
Errno::result(unsafe {
safe_ptrace(
PTRACE_PEEKUSER,
pid.as_raw(),
addr,
(&raw mut data) as *mut c_void,
)
})?;
Ok(data)
}
#[inline(always)]
pub unsafe fn ptrace_getregs(
pid: Pid,
request: PtraceRequest,
data: *mut c_void,
) -> Result<(), Errno> {
Errno::result(unsafe { safe_ptrace(request, pid.as_raw(), ptr::null_mut(), data) }).map(drop)
}
#[inline(always)]
pub unsafe fn ptrace_setregs(
pid: Pid,
request: PtraceRequest,
data: *mut c_void,
) -> Result<(), Errno> {
Errno::result(unsafe { safe_ptrace(request, pid.as_raw(), ptr::null_mut(), data) }).map(drop)
}
#[inline(always)]
pub fn ptrace_getregset(pid: Pid, iov: &mut iovec) -> Result<(), Errno> {
Errno::result(unsafe {
safe_ptrace(
PTRACE_GETREGSET,
pid.as_raw(),
NT_PRSTATUS as usize as *mut c_void,
iov as *mut _ as *mut c_void,
)
})
.map(drop)
}
#[inline(always)]
pub fn ptrace_setregset(pid: Pid, regset: c_int, iov: &iovec) -> Result<(), Errno> {
Errno::result(unsafe {
safe_ptrace(
libc::PTRACE_SETREGSET,
pid.as_raw(),
regset as *mut c_void,
iov as *const _ as *mut c_void,
)
})
.map(drop)
}
#[inline(always)]
pub fn ptrace_get_syscall_info(pid: Pid) -> Result<ptrace_syscall_info, Errno> {
let mut info = mem::MaybeUninit::<ptrace_syscall_info>::uninit();
let info_size = mem::size_of::<ptrace_syscall_info>();
Errno::result(unsafe {
safe_ptrace(
0x420e, pid.as_raw(),
info_size as *mut c_void,
info.as_mut_ptr() as *mut c_void,
)
})?;
#[allow(unused_mut)]
let mut info = unsafe { info.assume_init() };
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
ptrace_fixup_mips32_args(pid, &mut info)?;
Ok(info)
}
const PTRACE_SET_SYSCALL_INFO: PtraceRequest = 0x4212;
#[inline(always)]
pub fn ptrace_set_syscall_info(pid: Pid, info: &ptrace_syscall_info) -> Result<(), Errno> {
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
let info = ppc_fixup_set_syscall_info_exit_error(pid, info)?;
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
let info = &info;
let info_size = mem::size_of::<ptrace_syscall_info>();
Errno::result(unsafe {
safe_ptrace(
PTRACE_SET_SYSCALL_INFO,
pid.as_raw(),
info_size as *mut c_void,
info as *const _ as *mut c_void,
)
})
.map(drop)
}
#[cfg(target_arch = "powerpc")]
fn ppc_fixup_set_syscall_info_exit_error(
_pid: Pid,
info: &ptrace_syscall_info,
) -> Result<ptrace_syscall_info, Errno> {
let mut info = *info;
if info.op == PTRACE_SYSCALL_INFO_EXIT {
let exit = unsafe { &mut info.data.exit };
if exit.is_error != 0 && exit.rval < 0 {
exit.rval = -exit.rval;
}
}
Ok(info)
}
#[cfg(target_arch = "powerpc64")]
fn ppc_fixup_set_syscall_info_exit_error(
pid: Pid,
info: &ptrace_syscall_info,
) -> Result<ptrace_syscall_info, Errno> {
let mut info = *info;
if info.op != PTRACE_SYSCALL_INFO_EXIT {
return Ok(info);
}
let exit = unsafe { &mut info.data.exit };
if exit.is_error == 0 || exit.rval >= 0 {
return Ok(info);
}
let mut regs = mem::MaybeUninit::<PpcPtRegs64>::uninit();
unsafe { ptrace_getregs(pid, 12, regs.as_mut_ptr() as *mut c_void)? };
let regs = unsafe { regs.assume_init() };
const MSR_SF: libc::c_ulong = 1 << 63;
let is_scv = regs.msr & MSR_SF != 0 && (regs.trap & !0xF) == 0x3000;
if !is_scv {
exit.rval = -exit.rval;
}
Ok(info)
}
#[cfg(target_arch = "arm")]
#[inline(always)]
fn ptrace_set_syscall_arm(pid: Pid, sysno: usize) -> Result<(), Errno> {
Errno::result(unsafe {
safe_ptrace(
23, pid.as_raw(),
ptr::null_mut(),
sysno as *mut c_void,
)
})
.map(drop)
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
fn ptrace_fixup_mips32_args(pid: Pid, info: &mut ptrace_syscall_info) -> Result<(), Errno> {
if !matches!(
info.op,
PTRACE_SYSCALL_INFO_ENTRY | PTRACE_SYSCALL_INFO_SECCOMP
) {
return Ok(());
}
let arch = if let Ok(arch) = crate::confine::scmp_arch(info.arch) {
arch
} else {
return Ok(());
};
if !crate::confine::scmp_arch_is_mips(arch) || crate::confine::scmp_arch_bits(arch) != 32 {
return Ok(());
}
let (arg4, arg5) = ptrace_read_mips_o32_stack_args(pid, info.arch, info.stack_pointer)?;
unsafe {
if info.op == PTRACE_SYSCALL_INFO_ENTRY {
info.data.entry.args[4] = arg4;
info.data.entry.args[5] = arg5;
} else {
info.data.seccomp.args[4] = arg4;
info.data.seccomp.args[5] = arg5;
}
}
Ok(())
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
fn ptrace_read_mips_o32_stack_args(pid: Pid, arch: u32, sp: u64) -> Result<(u64, u64), Errno> {
let scmp = crate::confine::scmp_arch(arch).map_err(|_| Errno::EINVAL)?;
let sp = (sp & !0xF).saturating_add(16);
let process = crate::req::RemoteProcess::new(pid);
let mut buf = [0u8; 8];
if unsafe { process.read_mem(scmp, &mut buf, sp, 8) }? != 8 {
return Err(Errno::EFAULT);
}
let mut arg4 = [0u8; 4];
arg4.copy_from_slice(&buf[0..4]);
let mut arg5 = [0u8; 4];
arg5.copy_from_slice(&buf[4..8]);
Ok((
u64::from(u32::from_ne_bytes(arg4)),
u64::from(u32::from_ne_bytes(arg5)),
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_check_negated_errno_1() {
let result = check_negated_errno(-1i64);
assert!(result.is_some());
assert_eq!(result.unwrap(), Errno::EPERM);
}
#[test]
fn test_check_negated_errno_2() {
let result = check_negated_errno(-4095i64);
assert!(result.is_some());
}
#[test]
fn test_check_negated_errno_3() {
assert!(check_negated_errno(0i64).is_none());
}
#[test]
fn test_check_negated_errno_4() {
assert!(check_negated_errno(1i64).is_none());
}
#[test]
fn test_check_negated_errno_5() {
assert!(check_negated_errno(-4096i64).is_none());
}
}