#[cfg(all(
target_os = "linux",
target_env = "gnu",
any(target_arch = "x86_64", target_arch = "x86")
))] use memoffset::offset_of; use nix::errno::Errno; use nix::sys::ptrace; #[cfg(linux_android)] use nix::sys::ptrace::Options; use nix::unistd::getpid;
#[cfg(linux_android)] use std::mem;
usecrate::*;
#[test] fn test_ptrace() { // Just make sure ptrace can be called at all, for now. // FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS
require_capability!("test_ptrace", CAP_SYS_PTRACE); let err = ptrace::attach(getpid()).unwrap_err();
assert!(
err == Errno::EPERM || err == Errno::EINVAL || err == Errno::ENOSYS
);
}
// Just make sure ptrace_setoptions can be called at all, for now. #[test] #[cfg(linux_android)] fn test_ptrace_setoptions() {
require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE); let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD)
.unwrap_err();
assert_ne!(err, Errno::EOPNOTSUPP);
}
// Just make sure ptrace_getevent can be called at all, for now. #[test] #[cfg(linux_android)] fn test_ptrace_getevent() {
require_capability!("test_ptrace_getevent", CAP_SYS_PTRACE); let err = ptrace::getevent(getpid()).unwrap_err();
assert_ne!(err, Errno::EOPNOTSUPP);
}
// Just make sure ptrace_getsiginfo can be called at all, for now. #[test] #[cfg(linux_android)] fn test_ptrace_getsiginfo() {
require_capability!("test_ptrace_getsiginfo", CAP_SYS_PTRACE); iflet Err(Errno::EOPNOTSUPP) = ptrace::getsiginfo(getpid()) {
panic!("ptrace_getsiginfo returns Errno::EOPNOTSUPP!");
}
}
// Just make sure ptrace_setsiginfo can be called at all, for now. #[test] #[cfg(linux_android)] fn test_ptrace_setsiginfo() {
require_capability!("test_ptrace_setsiginfo", CAP_SYS_PTRACE); let siginfo = unsafe { mem::zeroed() }; iflet Err(Errno::EOPNOTSUPP) = ptrace::setsiginfo(getpid(), &siginfo) {
panic!("ptrace_setsiginfo returns Errno::EOPNOTSUPP!");
}
}
#[test] fn test_ptrace_cont() { use nix::sys::ptrace; use nix::sys::signal::{raise, Signal}; use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus}; use nix::unistd::fork; use nix::unistd::ForkResult::*;
// FIXME: qemu-user doesn't implement ptrace on all architectures // and returns ENOSYS in this case. // We (ab)use this behavior to detect the affected platforms // and skip the test then. // On valid platforms the ptrace call should return Errno::EPERM, this // is already tested by `test_ptrace`. let err = ptrace::attach(getpid()).unwrap_err(); if err == Errno::ENOSYS { return;
}
matchunsafe { fork() }.expect("Error: Fork Failed") {
Child => {
ptrace::traceme().unwrap(); // As recommended by ptrace(2), raise SIGTRAP to pause the child // until the parent is ready to continue loop {
raise(Signal::SIGTRAP).unwrap();
}
}
Parent { child } => {
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
);
ptrace::cont(child, None).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
);
ptrace::cont(child, Some(Signal::SIGKILL)).unwrap(); match waitpid(child, None) {
Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child =>
{ // FIXME It's been observed on some systems (apple) the // tracee may not be killed but remain as a zombie process // affecting other wait based tests. Add an extra kill just // to make sure there are no zombies. let _ = waitpid(child, Some(WaitPidFlag::WNOHANG)); while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() { let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
}
}
_ => panic!("The process should have been killed"),
}
}
}
}
#[cfg(target_os = "linux")] #[test] fn test_ptrace_interrupt() { use nix::sys::ptrace; use nix::sys::signal::Signal; use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus}; use nix::unistd::fork; use nix::unistd::ForkResult::*; use std::thread::sleep; use std::time::Duration;
matchunsafe { fork() }.expect("Error: Fork Failed") {
Child => loop {
sleep(Duration::from_millis(1000));
},
Parent { child } => {
ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
.unwrap();
ptrace::interrupt(child).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128))
);
ptrace::syscall(child, None).unwrap();
assert_eq!(
waitpid(child, None),
Ok(WaitStatus::PtraceSyscall(child))
);
ptrace::detach(child, Some(Signal::SIGKILL)).unwrap(); match waitpid(child, None) {
Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child =>
{ let _ = waitpid(child, Some(WaitPidFlag::WNOHANG)); while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() { let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
}
}
_ => panic!("The process should have been killed"),
}
}
}
}
// ptrace::{setoptions, getregs} are only available in these platforms #[cfg(all(
target_os = "linux",
target_env = "gnu",
any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "riscv64",
)
))] #[test] fn test_ptrace_syscall() { use nix::sys::ptrace; use nix::sys::signal::kill; use nix::sys::signal::Signal; use nix::sys::wait::{waitpid, WaitStatus}; use nix::unistd::fork; use nix::unistd::getpid; use nix::unistd::ForkResult::*;
// set this option to recognize syscall-stops
ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
.unwrap();
#[cfg(target_arch = "x86_64")] let get_syscall_id =
|| ptrace::getregs(child).unwrap().orig_rax as libc::c_long;
#[cfg(target_arch = "x86")] let get_syscall_id =
|| ptrace::getregs(child).unwrap().orig_eax as libc::c_long;
#[cfg(target_arch = "aarch64")] let get_syscall_id =
|| ptrace::getregs(child).unwrap().regs[8] as libc::c_long;
#[cfg(target_arch = "riscv64")] let get_syscall_id =
|| ptrace::getregs(child).unwrap().a7 as libc::c_long;
// this duplicates `get_syscall_id` for the purpose of testing `ptrace::read_user`. #[cfg(target_arch = "x86_64")] let rax_offset = offset_of!(libc::user_regs_struct, orig_rax); #[cfg(target_arch = "x86")] let rax_offset = offset_of!(libc::user_regs_struct, orig_eax);
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))] let get_syscall_from_user_area = || { // Find the offset of `user.regs.rax` (or `user.regs.eax` for x86) let rax_offset = offset_of!(libc::user, regs) + rax_offset;
ptrace::read_user(child, rax_offset as _).unwrap() as libc::c_long
};
matchunsafe { fork() }.expect("Error: Fork Failed") {
Child => {
ptrace::traceme().unwrap(); // As recommended by ptrace(2), raise SIGTRAP to pause the child // until the parent is ready to continue loop {
raise(Signal::SIGTRAP).unwrap();
}
}
*reg = 0xdeadbeefu32 as _;
*fpreg = 0xfeedfaceu32 as _; let _ = setregset::<regset::NT_PRSTATUS>(child, regstruct);
regstruct = getregset::<regset::NT_PRSTATUS>(child).unwrap(); let _ = setregset::<regset::NT_PRFPREG>(child, fpregstruct);
fpregstruct = getregset::<regset::NT_PRFPREG>(child).unwrap();
#[cfg(target_arch = "x86_64")] let (reg, fpreg) = (regstruct.r15, fpregstruct.st_space[5]); #[cfg(target_arch = "x86")] let (reg, fpreg) = (regstruct.edx, fpregstruct.st_space[5]); #[cfg(target_arch = "aarch64")] let (reg, fpreg) = (regstruct.regs[16], fpregstruct.vregs[5]); #[cfg(target_arch = "riscv64")] let (reg, fpreg) = (regstruct.t1, fpregstruct.__f[5]);
assert_eq!(reg, 0xdeadbeefu32 as _);
assert_eq!(fpreg, 0xfeedfaceu32 as _);
ptrace::cont(child, Some(Signal::SIGKILL)).unwrap(); match waitpid(child, None) {
Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => {}
_ => panic!("The process should have been killed"),
}
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-06-20)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.