use std::sync::{Arc, RwLock};
use nix::{errno::Errno, sys::signal::Signal, unistd::Pid};
use crate::{
compat::WaitStatus,
confine::is_coredump,
error, info,
lookup::{FileMapEntry, SysInfo},
magic::ProcMagic,
proc::proc_tgid,
ptrace::ptrace_cont,
sandbox::Sandbox,
workers::WorkerCache,
xfmt,
};
#[expect(clippy::cognitive_complexity)]
pub(crate) fn sysevent_exit(
pid: Pid,
status: WaitStatus,
wait_all: bool,
cache: &Arc<WorkerCache>,
sandbox: &Arc<RwLock<Sandbox>>,
) {
#[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!();
}
let (is_child, segvguard_expiry, segvguard_suspension, segvguard_maxcrashes, has_exit_moves) = {
let my_sandbox = sandbox.read().unwrap_or_else(|err| err.into_inner());
(
pid == my_sandbox.get_child_pid(),
my_sandbox.get_segvguard_expiry(),
my_sandbox.get_segvguard_suspension(),
my_sandbox.get_segvguard_maxcrashes(),
my_sandbox.has_exit_moves(),
)
}; let has_segvguard = !segvguard_expiry.is_zero();
let sig = if has_segvguard && !(is_child && !wait_all) {
match status {
WaitStatus::Signaled(_, sig, true) => Some(sig),
WaitStatus::Signaled(_, sig, _) if is_coredump(sig) => Some(sig),
_ => None, }
} else {
None
};
let move_on_exit = has_exit_moves && proc_tgid(pid) == Ok(pid);
let exe_path = if sig.is_some() || move_on_exit {
FileMapEntry::from_magic_link(
ProcMagic::Exe { pid },
false,
&SysInfo {
request: None,
sandbox: None,
},
)
.and_then(|(entry, _)| entry.target.unwrap_or(Err(Errno::ENOENT)))
} else {
Err(Errno::ENOSYS)
};
if let Some(sig) = sig {
let path = match exe_path.as_ref() {
Ok(path) => path,
Err(Errno::ESRCH) => return,
Err(_) => {
let _ = ptrace_cont(pid, None);
return;
}
};
let (was_suspended, is_suspended, num_crashes) = match cache.add_segvguard_crash(
path,
segvguard_expiry,
segvguard_suspension,
segvguard_maxcrashes,
) {
Ok(result) => result,
Err(_) => {
let _ = ptrace_cont(pid, None);
return;
}
};
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": xfmt!("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": xfmt!("{num_crashes} {crashes} recorded after {signal}{}",
if was_suspended { " (suspended)" } else { "" }),
"pid": pid.as_raw(), "path": path, "sig": sig);
}
}
if move_on_exit {
match exe_path.as_ref() {
Ok(path) => {
sandbox
.read()
.unwrap_or_else(|err| err.into_inner())
.move_on_exit(path);
}
Err(Errno::ESRCH) => return,
_ => {} }
}
#[cfg(feature = "kcov")]
{
crate::kcov_edge!();
let _ = crate::kcov::abi::kcov_exit_for(pid);
}
let _ = ptrace_cont(pid, None);
}