use std::{ffi::CStr, mem};
use libseccomp_sys::seccomp_syscall_resolve_num_arch;
use nix::{errno::Errno, unistd::Pid};
use crate::path::XPath;
#[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: libc::c_long,
d2: libc::c_long,
d3: libc::c_long,
d4: libc::c_long,
d5: libc::c_long,
d6: libc::c_long,
d7: libc::c_long,
a0: libc::c_long,
a1: libc::c_long,
a2: libc::c_long,
a3: libc::c_long,
a4: libc::c_long,
a5: libc::c_long,
a6: libc::c_long,
d0: libc::c_long,
usp: libc::c_long,
orig_d0: libc::c_long,
stkadj: libc::c_short,
sr: libc::c_short,
pc: libc::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,
}
#[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 libc::{c_void, iovec, ptrace, NT_PRSTATUS, PTRACE_GETREGSET, PTRACE_SETREGSET};
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 = X86UserRegsStruct {
x64: unsafe { mem::zeroed() },
};
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<X86UserRegsStruct>(),
};
Errno::result(unsafe { ptrace(PTRACE_GETREGSET, pid.as_raw(), NT_PRSTATUS, &mut io) })?;
#[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 => {
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),
}
Errno::result(unsafe { ptrace(PTRACE_SETREGSET, pid.as_raw(), NT_PRSTATUS, &mut io) })
.map(drop)
}
#[cfg(target_arch = "m68k")]
{
use libc::{c_long, c_void, 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();
Errno::result(unsafe {
libc::ptrace(
PTRACE_GETREGS,
pid.as_raw(),
std::ptr::null_mut::<c_void>(),
regs.as_mut_ptr(),
)
})?;
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);
Errno::result(unsafe {
libc::ptrace(
PTRACE_SETREGS,
pid.as_raw(),
std::ptr::null_mut::<c_void>(),
std::ptr::addr_of!(regs) as *const c_void,
)
})
.map(drop)
}
#[cfg(target_arch = "riscv64")]
{
use libc::{c_void, iovec, PTRACE_GETREGSET, PTRACE_SETREGSET};
use libseccomp_sys::SCMP_ARCH_RISCV64;
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
let mut regs: Riscv64UserRegsStruct = unsafe { mem::zeroed() };
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
regs.a7 = u64::MAX;
regs.a0 = (-(errno.map(|err| err as i32).unwrap_or(0) as i64)) as u64;
Errno::result(unsafe {
libc::ptrace(PTRACE_SETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &io)
})
.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_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 libc::{c_void, iovec, ptrace, NT_PRSTATUS, PTRACE_GETREGSET, PTRACE_SETREGSET};
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 = X86UserRegsStruct {
x64: unsafe { mem::zeroed() },
};
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<X86UserRegsStruct>(),
};
Errno::result(unsafe { ptrace(PTRACE_GETREGSET, pid.as_raw(), NT_PRSTATUS, &mut io) })?;
#[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),
}
Errno::result(unsafe { ptrace(PTRACE_SETREGSET, pid.as_raw(), NT_PRSTATUS, &mut io) })
.map(drop)
}
#[cfg(target_arch = "x86")]
{
use libseccomp_sys::SCMP_ARCH_X86;
use nix::{errno::Errno, sys::ptrace};
const EAX_OFFSET: u64 = 6 * 4;
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
};
ptrace::write_user(pid, EAX_OFFSET as ptrace::AddressType, rval.into())
}
#[cfg(target_arch = "aarch64")]
{
use libc::{c_void, iovec, PTRACE_GETREGSET, PTRACE_SETREGSET};
use libseccomp_sys::{SCMP_ARCH_AARCH64, SCMP_ARCH_ARM};
#[repr(C)]
#[derive(Copy, Clone)]
struct ArmPtRegs {
uregs: [u32; 18], }
#[repr(C)]
union ArmRegsUnion {
aarch64: Aarch64UserRegsStruct,
arm: ArmPtRegs,
}
let mut regs = ArmRegsUnion {
aarch64: unsafe { mem::zeroed() },
};
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<ArmRegsUnion>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
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),
}
Errno::result(unsafe {
libc::ptrace(PTRACE_SETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})
.map(drop)
}
#[cfg(target_arch = "arm")]
{
use libc::{c_void, iovec, PTRACE_GETREGSET, PTRACE_SETREGSET};
use libseccomp_sys::SCMP_ARCH_ARM;
use nix::errno::Errno;
#[repr(C)]
struct ArmPtRegs {
uregs: [u32; 18],
}
if arch != SCMP_ARCH_ARM {
return Err(Errno::EINVAL);
}
let mut regs: ArmPtRegs = unsafe { mem::zeroed() };
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<ArmPtRegs>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
#[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;
}
Errno::result(unsafe {
libc::ptrace(PTRACE_SETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})
.map(drop)
}
#[cfg(target_arch = "m68k")]
{
use libc::{c_long, c_void, 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();
Errno::result(unsafe {
libc::ptrace(
PTRACE_GETREGS,
pid.as_raw(),
std::ptr::null_mut::<c_void>(),
regs.as_mut_ptr(),
)
})?;
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;
Errno::result(unsafe {
libc::ptrace(
PTRACE_SETREGS,
pid.as_raw(),
std::ptr::null_mut::<c_void>(),
std::ptr::addr_of!(regs) as *const c_void,
)
})
.map(drop)
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
{
use libc::{c_void, 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();
Errno::result(unsafe {
libc::ptrace(
PTRACE_GETREGS,
pid.as_raw(),
std::ptr::null_mut::<c_void>(),
regs.as_mut_ptr(),
)
})?;
let mut regs = unsafe { regs.assume_init() };
const REG_V0: usize = 2;
const REG_A0: usize = 4;
const REG_A3: usize = REG_A0 + 3;
#[expect(clippy::arithmetic_side_effects)]
if matches!(arch, SCMP_ARCH_MIPS | SCMP_ARCH_MIPSEL) {
if let Some(e) = errno {
regs.regs[REG_V0] = (e as u32) as u64;
regs.regs[REG_A3] = u32::MAX as u64; } else {
regs.regs[REG_V0] = 0;
regs.regs[REG_A3] = 0;
}
} else {
if let Some(e) = errno {
regs.regs[REG_V0] = e as u64;
regs.regs[REG_A3] = u64::MAX; } else {
regs.regs[REG_V0] = 0;
regs.regs[REG_A3] = 0;
}
}
Errno::result(unsafe {
libc::ptrace(
PTRACE_SETREGS,
pid.as_raw(),
std::ptr::null_mut::<c_void>(),
std::ptr::addr_of!(regs) as *const c_void,
)
})
.map(drop)
}
#[cfg(target_arch = "riscv64")]
{
use libc::{c_void, iovec, PTRACE_GETREGSET, PTRACE_SETREGSET};
use libseccomp_sys::SCMP_ARCH_RISCV64;
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
let mut regs: Riscv64UserRegsStruct = unsafe { mem::zeroed() };
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
#[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;
}
Errno::result(unsafe {
libc::ptrace(PTRACE_SETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})
.map(drop)
}
#[cfg(target_arch = "s390x")]
{
use libc::{c_void, iovec, PTRACE_GETREGSET, PTRACE_SETREGSET};
use libseccomp_sys::SCMP_ARCH_S390X;
#[repr(C, align(8))]
struct psw_t {
mask: u64,
addr: u64,
}
#[repr(C)]
struct s390_regs {
psw: psw_t,
gprs: [u64; 16],
acrs: [u32; 16],
orig_gpr2: u64,
}
if arch != SCMP_ARCH_S390X {
return Err(Errno::EINVAL);
}
let mut regs: s390_regs = unsafe { mem::zeroed() };
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<s390_regs>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
#[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;
}
Errno::result(unsafe {
libc::ptrace(PTRACE_SETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &io)
})
.map(drop)
}
#[cfg(target_arch = "powerpc")]
{
use libc::c_void;
use libseccomp_sys::SCMP_ARCH_PPC;
if arch != SCMP_ARCH_PPC {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegs32>::uninit();
Errno::result(unsafe {
libc::ptrace(
12, pid.as_raw(),
std::ptr::null_mut::<c_void>(),
regs.as_mut_ptr(),
)
})?;
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;
}
}
Errno::result(unsafe {
libc::ptrace(
13, pid.as_raw(),
std::ptr::null_mut::<c_void>(),
std::ptr::addr_of!(regs) as *const c_void,
)
})
.map(drop)
}
#[cfg(target_arch = "powerpc64")]
{
use libc::{c_void, iovec, PTRACE_GETREGSET, PTRACE_SETREGSET};
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 = PpcPtRegsUnion {
ppc64: unsafe { mem::zeroed() },
};
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<PpcPtRegsUnion>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
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),
}
Errno::result(unsafe {
libc::ptrace(PTRACE_SETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})
.map(drop)
}
#[cfg(target_arch = "loongarch64")]
{
use libc::{c_void, iovec, user_regs_struct, PTRACE_GETREGSET, PTRACE_SETREGSET};
use libseccomp_sys::SCMP_ARCH_LOONGARCH64;
if arch != SCMP_ARCH_LOONGARCH64 {
return Err(Errno::EINVAL);
}
let mut regs: user_regs_struct = unsafe { mem::zeroed() };
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<user_regs_struct>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
#[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;
}
Errno::result(unsafe {
libc::ptrace(PTRACE_SETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})
.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_return is not implemented for this architecture!");
}
}
#[allow(unused)]
pub fn ptrace_get_error(pid: Pid, arch: u32) -> Result<Option<Errno>, Errno> {
#[cfg(target_arch = "x86_64")]
{
use libc::{c_void, iovec, ptrace, NT_PRSTATUS, PTRACE_GETREGSET};
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 = X86UserRegsStruct {
x64: unsafe { mem::zeroed() },
};
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<X86UserRegsStruct>(),
};
Errno::result(unsafe { ptrace(PTRACE_GETREGSET, pid.as_raw(), NT_PRSTATUS, &mut io) })?;
#[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;
use nix::sys::ptrace;
const EAX_OFFSET: u64 = 6 * 4;
if arch != SCMP_ARCH_X86 {
return Err(Errno::EINVAL);
}
let raw_eax = ptrace::read_user(pid, EAX_OFFSET as ptrace::AddressType)? as i32;
let val = raw_eax as i64;
if let Some(e) = check_negated_errno(val) {
Ok(Some(e))
} else {
Ok(None)
}
}
#[cfg(target_arch = "aarch64")]
{
use libc::{c_void, iovec, PTRACE_GETREGSET};
use libseccomp_sys::{SCMP_ARCH_AARCH64, SCMP_ARCH_ARM};
#[repr(C)]
#[derive(Copy, Clone)]
struct ArmPtRegs {
uregs: [u32; 18],
}
#[repr(C)]
union ArmRegsUnion {
aarch64: Aarch64UserRegsStruct,
arm: ArmPtRegs,
}
let mut regs = ArmRegsUnion {
aarch64: unsafe { mem::zeroed() },
};
let mut io = iovec {
iov_base: (&mut regs) as *mut _ as *mut c_void,
iov_len: mem::size_of::<ArmRegsUnion>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
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 libc::{c_void, iovec, PTRACE_GETREGSET};
use libseccomp_sys::SCMP_ARCH_ARM;
#[repr(C)]
struct ArmPtRegs {
uregs: [u32; 18],
}
if arch != SCMP_ARCH_ARM {
return Err(Errno::EINVAL);
}
let mut regs: ArmPtRegs = unsafe { mem::zeroed() };
let mut io = iovec {
iov_base: (&mut regs) as *mut _ as *mut c_void,
iov_len: mem::size_of::<ArmPtRegs>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
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::{c_void, PTRACE_GETREGS};
use libseccomp_sys::SCMP_ARCH_M68K;
if arch != SCMP_ARCH_M68K {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<M68KUserRegsStruct>::uninit();
Errno::result(unsafe {
libc::ptrace(
PTRACE_GETREGS,
pid.as_raw(),
std::ptr::null_mut::<c_void>(),
regs.as_mut_ptr(),
)
})?;
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::{c_void, 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();
Errno::result(unsafe {
libc::ptrace(
PTRACE_GETREGS,
pid.as_raw(),
std::ptr::null_mut::<c_void>(),
regs.as_mut_ptr(),
)
})?;
let regs = unsafe { regs.assume_init() };
const REG_V0: usize = 2;
const REG_A0: usize = 4;
const REG_A3: usize = REG_A0 + 3;
if regs.regs[REG_A3] != 0 {
Ok(Some(Errno::from_raw(regs.regs[REG_V0] as i32)))
} else {
Ok(None)
}
}
#[cfg(target_arch = "riscv64")]
{
use libc::{c_void, iovec, PTRACE_GETREGSET};
use libseccomp_sys::SCMP_ARCH_RISCV64;
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
let mut regs: Riscv64UserRegsStruct = unsafe { mem::zeroed() };
let mut io = iovec {
iov_base: (&mut regs) as *mut _ as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
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 libc::{c_void, iovec, PTRACE_GETREGSET};
use libseccomp_sys::SCMP_ARCH_S390X;
#[repr(C, align(8))]
struct psw_t {
mask: u64,
addr: u64,
}
#[repr(C)]
struct s390_regs {
psw: psw_t,
gprs: [u64; 16],
acrs: [u32; 16],
orig_gpr2: u64,
}
if arch != SCMP_ARCH_S390X {
return Err(Errno::EINVAL);
}
let mut regs: s390_regs = unsafe { mem::zeroed() };
let mut io = iovec {
iov_base: (&mut regs) as *mut _ as *mut c_void,
iov_len: mem::size_of::<s390_regs>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
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 libc::c_void;
use libseccomp_sys::SCMP_ARCH_PPC;
if arch != SCMP_ARCH_PPC {
return Err(Errno::EINVAL);
}
let mut regs = mem::MaybeUninit::<PpcPtRegs32>::uninit();
Errno::result(unsafe {
libc::ptrace(
12, pid.as_raw(),
std::ptr::null_mut::<c_void>(),
regs.as_mut_ptr(),
)
})?;
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 libc::{c_void, iovec, PTRACE_GETREGSET};
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 = PpcPtRegsUnion {
ppc64: unsafe { mem::zeroed() },
};
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<PpcPtRegsUnion>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
#[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::{c_void, iovec, user_regs_struct, PTRACE_GETREGSET};
use libseccomp_sys::SCMP_ARCH_LOONGARCH64;
if arch != SCMP_ARCH_LOONGARCH64 {
return Err(Errno::EINVAL);
}
let mut regs: user_regs_struct = unsafe { mem::zeroed() };
let mut io = iovec {
iov_base: (&mut regs) as *mut _ as *mut c_void,
iov_len: mem::size_of::<user_regs_struct>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
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 libc::{c_void, iovec, ptrace, NT_PRSTATUS, PTRACE_GETREGSET, PTRACE_SETREGSET};
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 = X86UserRegsStruct {
x64: unsafe { mem::zeroed() },
};
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<X86UserRegsStruct>(),
};
Errno::result(unsafe { ptrace(PTRACE_GETREGSET, pid.as_raw(), NT_PRSTATUS, &mut io) })?;
#[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),
}
Errno::result(unsafe { ptrace(PTRACE_SETREGSET, pid.as_raw(), NT_PRSTATUS, &mut io) })
.map(drop)
}
#[cfg(target_arch = "x86")]
{
use nix::sys::ptrace;
const ORIG_EAX_OFFSET: u64 = 11 * 4;
ptrace::write_user(
pid,
ORIG_EAX_OFFSET as ptrace::AddressType,
sysno as libc::c_long,
)
}
#[cfg(target_arch = "aarch64")]
{
use libc::{c_void, iovec, PTRACE_SETREGSET};
let io = iovec {
iov_base: std::ptr::addr_of!(sysno) as *mut c_void,
iov_len: mem::size_of::<u64>(),
};
Errno::result(unsafe { libc::ptrace(PTRACE_SETREGSET, pid.as_raw(), 0x404, &io) }).map(drop)
}
#[cfg(target_arch = "arm")]
{
Errno::result(unsafe { libc::ptrace(23, pid.as_raw(), 0, sysno as libc::c_uint) }).map(drop)
}
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
{
use libc::{c_void, 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();
Errno::result(unsafe {
libc::ptrace(
PTRACE_GETREGS,
pid.as_raw(),
std::ptr::null_mut::<c_void>(),
regs.as_mut_ptr(),
)
})?;
let mut regs = unsafe { regs.assume_init() };
const REG_V0: usize = 2;
regs.regs[REG_V0] = sysno;
Errno::result(unsafe {
libc::ptrace(
PTRACE_SETREGS,
pid.as_raw(),
std::ptr::null_mut::<c_void>(),
std::ptr::addr_of!(regs) as *const c_void,
)
})
.map(drop)
}
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
{
use libseccomp_sys::{SCMP_ARCH_PPC, SCMP_ARCH_PPC64, SCMP_ARCH_PPC64LE};
use nix::sys::ptrace;
if !matches!(arch, SCMP_ARCH_PPC | SCMP_ARCH_PPC64 | SCMP_ARCH_PPC64LE) {
return Err(Errno::EINVAL);
}
ptrace::write_user(pid, std::ptr::null_mut(), sysno as libc::c_long)
}
#[cfg(target_arch = "riscv64")]
{
use libc::{c_void, iovec, PTRACE_GETREGSET, PTRACE_SETREGSET};
use libseccomp_sys::SCMP_ARCH_RISCV64;
if arch != SCMP_ARCH_RISCV64 {
return Err(Errno::EINVAL);
}
let mut regs: Riscv64UserRegsStruct = unsafe { mem::zeroed() };
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<Riscv64UserRegsStruct>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
regs.a7 = sysno;
regs.a0 = (-(Errno::ENOSYS as i64)) as u64;
Errno::result(unsafe {
libc::ptrace(PTRACE_SETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &io)
})
.map(drop)
}
#[cfg(target_arch = "s390x")]
{
use libc::{c_void, iovec, PTRACE_GETREGSET, PTRACE_SETREGSET};
use libseccomp_sys::SCMP_ARCH_S390X;
#[repr(C, align(8))]
struct psw_t {
mask: u64,
addr: u64,
}
#[repr(C)]
struct s390_regs {
psw: psw_t,
gprs: [u64; 16],
acrs: [u32; 16],
orig_gpr2: u64,
}
if arch != SCMP_ARCH_S390X {
return Err(Errno::EINVAL);
}
let mut regs: s390_regs = unsafe { mem::zeroed() };
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<s390_regs>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
regs.gprs[2] = sysno;
Errno::result(unsafe {
libc::ptrace(PTRACE_SETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &io)
})
.map(drop)
}
#[cfg(target_arch = "loongarch64")]
{
use libc::{c_void, iovec, user_regs_struct, PTRACE_GETREGSET, PTRACE_SETREGSET};
use libseccomp_sys::SCMP_ARCH_LOONGARCH64;
if arch != SCMP_ARCH_LOONGARCH64 {
return Err(Errno::EINVAL);
}
let mut regs: user_regs_struct = unsafe { mem::zeroed() };
let mut io = iovec {
iov_base: std::ptr::addr_of_mut!(regs) as *mut c_void,
iov_len: mem::size_of::<user_regs_struct>(),
};
Errno::result(unsafe {
libc::ptrace(PTRACE_GETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &mut io)
})?;
regs.regs[11] = sysno;
Errno::result(unsafe {
libc::ptrace(PTRACE_SETREGSET, pid.as_raw(), libc::NT_PRSTATUS, &io)
})
.map(drop)
}
#[cfg(target_arch = "m68k")]
{
use libc::{c_long, c_void, 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();
Errno::result(unsafe {
libc::ptrace(
PTRACE_GETREGS,
pid.as_raw(),
std::ptr::null_mut::<c_void>(),
regs.as_mut_ptr(),
)
})?;
let mut regs = unsafe { regs.assume_init() };
regs.orig_d0 = sysno as c_long;
Errno::result(unsafe {
libc::ptrace(
PTRACE_SETREGS,
pid.as_raw(),
std::ptr::null_mut::<c_void>(),
std::ptr::addr_of!(regs) as *const 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_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 {
libc::ptrace(
0x420e, pid.as_raw(),
info_size,
info.as_mut_ptr() as *mut libc::c_void,
)
})?;
Ok(unsafe { info.assume_init() })
}
#[inline]
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 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,
}
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()))
}
}