#![allow(unused)]
use std::{
fmt::Display,
hint::black_box,
};
use nix::{
errno::Errno,
libc::{
self,
SIGRTMIN,
WSTOPSIG,
c_int,
pid_t,
},
sys::{
signal,
wait::WaitPidFlag,
},
unistd::Pid,
};
use tracexec_core::tracer::Signal;
use tracing::trace;
use super::{
PtraceInterruptStopGuard,
RecursivePtraceEngine,
guards::{
PtraceOpaqueStopGuard,
PtraceStopGuard,
PtraceSyscallStopGuard,
},
};
use crate::ptrace::{
PtraceSeccompStopGuard,
guards::{
PtraceCloneChildStopGuard,
PtraceCloneParentStopGuard,
PtraceExecStopGuard,
PtraceExitStopGuard,
PtraceGroupStopGuard,
PtraceSignalDeliveryStopGuard,
},
};
#[derive(Debug)]
pub enum PtraceWaitPidEvent<'a> {
Ptrace(PtraceStopGuard<'a>),
Signaled { pid: Pid, signal: Signal },
Exited { pid: Pid, code: i32 },
Continued(#[allow(unused)] Pid),
StillAlive,
}
impl<'a> PtraceWaitPidEvent<'a> {
pub(crate) fn from_raw(
engine: &'a RecursivePtraceEngine,
pid: Pid,
status: c_int,
) -> Result<Self, Errno> {
Ok(if libc::WIFEXITED(status) {
PtraceWaitPidEvent::Exited {
pid,
code: libc::WEXITSTATUS(status),
}
} else if libc::WIFSIGNALED(status) {
PtraceWaitPidEvent::Signaled {
pid,
signal: Signal::from_raw(libc::WTERMSIG(status)),
}
} else if libc::WIFSTOPPED(status) {
let stopsig = libc::WSTOPSIG(status);
if stopsig == libc::SIGTRAP | 0x80 {
PtraceWaitPidEvent::Ptrace(PtraceStopGuard::Syscall(PtraceSyscallStopGuard {
guard: PtraceOpaqueStopGuard::new(engine, pid),
}))
} else {
let additional = status >> 16;
if additional == 0 {
let signal = Signal::from_raw(stopsig);
match signal {
Signal::Standard(signal::SIGSTOP)
| Signal::Standard(signal::SIGTSTP)
| Signal::Standard(signal::SIGTTIN)
| Signal::Standard(signal::SIGTTOU) => {
let siginfo = nix::sys::ptrace::getsiginfo(pid);
match siginfo {
Ok(siginfo)
if signal == Signal::Standard(signal::SIGSTOP)
&& unsafe { siginfo.si_pid() == 0 } =>
{
trace!("clone child event as sigstop");
PtraceWaitPidEvent::Ptrace(PtraceStopGuard::CloneChild(
PtraceCloneChildStopGuard {
guard: PtraceOpaqueStopGuard::new(engine, pid),
},
))
}
Ok(_) => {
PtraceWaitPidEvent::Ptrace(PtraceStopGuard::SignalDelivery(
PtraceSignalDeliveryStopGuard {
signal,
guard: PtraceOpaqueStopGuard::new(engine, pid),
},
))
}
Err(Errno::EINVAL) => {
trace!("group stop, unseized");
PtraceWaitPidEvent::Ptrace(PtraceStopGuard::Group(PtraceGroupStopGuard {
signal,
guard: PtraceOpaqueStopGuard::new(engine, pid),
}))
}
Err(Errno::ESRCH) => PtraceWaitPidEvent::Ptrace(PtraceStopGuard::SignalDelivery(
PtraceSignalDeliveryStopGuard {
signal,
guard: PtraceOpaqueStopGuard::new(engine, pid),
},
)),
Err(other) => return Err(other),
}
}
_ => PtraceWaitPidEvent::Ptrace(PtraceStopGuard::SignalDelivery(
PtraceSignalDeliveryStopGuard {
signal,
guard: PtraceOpaqueStopGuard::new(engine, pid),
},
)),
}
} else {
match additional {
libc::PTRACE_EVENT_SECCOMP => {
PtraceWaitPidEvent::Ptrace(PtraceStopGuard::Seccomp(PtraceSeccompStopGuard {
guard: PtraceOpaqueStopGuard::new(engine, pid),
}))
}
libc::PTRACE_EVENT_EXEC => {
PtraceWaitPidEvent::Ptrace(PtraceStopGuard::Exec(PtraceExecStopGuard {
former_tid: nix::sys::ptrace::getevent(pid).map(|x| Pid::from_raw(x as pid_t)),
guard: PtraceOpaqueStopGuard::new(engine, pid),
}))
}
libc::PTRACE_EVENT_EXIT => {
PtraceWaitPidEvent::Ptrace(PtraceStopGuard::Exit(PtraceExitStopGuard {
status: nix::sys::ptrace::getevent(pid).map(|x| x as c_int),
guard: PtraceOpaqueStopGuard::new(engine, pid),
}))
}
libc::PTRACE_EVENT_CLONE | libc::PTRACE_EVENT_FORK | libc::PTRACE_EVENT_VFORK => {
PtraceWaitPidEvent::Ptrace(PtraceStopGuard::CloneParent(PtraceCloneParentStopGuard {
child: nix::sys::ptrace::getevent(pid).map(|x| Pid::from_raw(x as pid_t)),
guard: PtraceOpaqueStopGuard::new(engine, pid),
}))
}
libc::PTRACE_EVENT_STOP => {
let sig = Signal::from_raw(WSTOPSIG(status));
match sig {
Signal::Standard(signal::SIGTRAP) => {
if nix::sys::ptrace::getsiginfo(pid) == Err(Errno::EINVAL) {
PtraceWaitPidEvent::Ptrace(PtraceStopGuard::Interrupt(
PtraceInterruptStopGuard {
guard: PtraceOpaqueStopGuard::new(engine, pid),
},
))
} else {
trace!(
"unsure child {pid}, eventmsg: {:?}, siginfo: {:?}",
nix::sys::ptrace::getevent(pid),
nix::sys::ptrace::getsiginfo(pid)
);
PtraceWaitPidEvent::Ptrace(PtraceStopGuard::CloneChild(
PtraceCloneChildStopGuard {
guard: PtraceOpaqueStopGuard::new(engine, pid),
},
))
}
}
Signal::Standard(signal::SIGSTOP)
| Signal::Standard(signal::SIGTSTP)
| Signal::Standard(signal::SIGTTIN)
| Signal::Standard(signal::SIGTTOU) => {
PtraceWaitPidEvent::Ptrace(PtraceStopGuard::Group(PtraceGroupStopGuard {
signal: sig,
guard: PtraceOpaqueStopGuard::new(engine, pid),
}))
}
_ => unimplemented!("ptrace_interrupt"),
}
}
_ => unreachable!(),
}
}
}
} else {
assert!(libc::WIFCONTINUED(status));
Self::Continued(pid)
})
}
}
pub(super) fn waitpid<P: Into<Option<Pid>>>(
engine: &RecursivePtraceEngine,
pid: P,
options: Option<WaitPidFlag>,
) -> Result<PtraceWaitPidEvent<'_>, Errno> {
let mut status: i32 = black_box(0);
let option_bits = match options {
Some(bits) => bits.bits(),
None => 0,
};
let res = unsafe {
nix::libc::waitpid(
pid.into().unwrap_or_else(|| Pid::from_raw(-1)).into(),
&mut status as *mut c_int,
option_bits,
)
};
match Errno::result(res)? {
0 => Ok(PtraceWaitPidEvent::StillAlive),
res => PtraceWaitPidEvent::from_raw(engine, Pid::from_raw(res), status),
}
}