use std::sync::{Arc, RwLock};
use nix::{
errno::Errno,
sys::signal::{kill, Signal},
unistd::Pid,
};
use crate::{
compat::{readlinkat, WaitStatus},
confine::is_coredump,
error,
fd::PROC_FILE,
info,
path::XPathBuf,
ptrace::{ptrace_cont, ptrace_getevent},
sandbox::{Sandbox, SandboxGuard},
workers::WorkerCache,
};
#[expect(clippy::cognitive_complexity)]
pub(crate) fn sysevent_exit(
pid: Pid,
cpid: Pid,
wait_all: bool,
cache: &Arc<WorkerCache>,
sandbox: &Arc<RwLock<Sandbox>>,
) -> Option<u8> {
#[cfg(feature = "kcov")]
{
crate::kcov::abi::kcov_attach(pid);
crate::kcov::abi::kcov_set_syscall(libc::SYS_exit);
let _ = crate::kcov::abi::kcov_enter_for(pid);
crate::kcov_edge!();
}
#[expect(clippy::cast_possible_truncation)]
let status = match ptrace_getevent(pid) {
Ok(status) => WaitStatus::from_raw(pid, status as i32),
Err(Errno::ESRCH) => return None,
Err(errno) => {
error!("ctx": "exit", "op": "ptrace_getevent",
"msg": format!("error reading exit status with ptrace: {errno}"),
"err": errno as i32, "pid": pid.as_raw(),
"tip": "check with SYD_LOG=debug and/or submit a bug report");
let _ = kill(pid, Some(Signal::SIGKILL));
return None;
}
};
let mut xcode = None;
let is_child = pid == cpid;
match status {
WaitStatus::Exited(_, exit_code) => {
cache.del_tgid(pid);
if is_child {
xcode = Some(exit_code.try_into().unwrap_or(127));
}
}
WaitStatus::Signaled(_, signal, _) => {
cache.del_tgid(pid);
if is_child {
xcode = Some(128_i32.saturating_add(signal).try_into().unwrap_or(128));
}
}
_ => {}
}
let has_segvguard = if is_child && !wait_all {
false
} else {
let my_sandbox = SandboxGuard::Read(sandbox.read().unwrap_or_else(|err| err.into_inner()));
!my_sandbox.get_segvguard_expiry().is_zero()
};
if has_segvguard {
let sig = match status {
WaitStatus::Signaled(_, sig, true) => Some(sig),
WaitStatus::Signaled(_, sig, _) if is_coredump(sig) => Some(sig),
_ => None, };
if let Some(sig) = sig {
let path = match XPathBuf::from_exe(pid).and_then(|exe| readlinkat(PROC_FILE(), &exe)) {
Ok(path) => path,
Err(_) => return xcode,
};
let mut my_sandbox =
SandboxGuard::Write(sandbox.write().unwrap_or_else(|err| err.into_inner()));
let (was_suspended, is_suspended, num_crashes) = my_sandbox.add_segvguard_crash(&path);
drop(my_sandbox);
let signal = Signal::try_from(sig).unwrap_or(Signal::SIGKILL);
let crashes = if num_crashes > 1 { "crashes" } else { "crash" };
if is_suspended {
error!("ctx": "segvguard",
"msg": format!("suspending after {signal} due to {num_crashes} {crashes}"),
"tip": "increase `segvguard/maxcrashes'",
"pid": pid.as_raw(), "path": path, "sig": sig);
} else {
info!("ctx": "segvguard",
"msg": format!("{num_crashes} {crashes} recorded after {signal}{}",
if was_suspended { " (suspended)" } else { "" }),
"pid": pid.as_raw(), "path": path, "sig": sig);
}
}
}
#[cfg(feature = "kcov")]
{
crate::kcov_edge!();
let _ = crate::kcov::abi::kcov_exit_for(pid);
}
let _ = ptrace_cont(pid, None);
xcode
}