From f9ebb234479f32477b944f3eede31446de6f1013 Mon Sep 17 00:00:00 2001 From: 1ndahous3 <9205230+1ndahous3@users.noreply.github.com> Date: Wed, 7 Oct 2026 17:39:26 +0300 Subject: [PATCH] Preserve Unix signal dispositions when probing handlers (#8961) Assisted-by: Codex:gpt-6 --- crates/host_env/src/signal.rs | 72 ++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/crates/host_env/src/signal.rs b/crates/host_env/src/signal.rs index 70d32a9fd..67336eb03 100644 --- a/crates/host_env/src/signal.rs +++ b/crates/host_env/src/signal.rs @@ -175,12 +175,26 @@ mod ffi { /// The caller must ensure `signalnum` is a valid platform signal number. #[cfg(any(unix, windows))] pub unsafe fn probe_handler(signalnum: i32) -> Option { - let handler = unsafe { libc::signal(signalnum, libc::SIG_IGN) }; - if handler == libc::SIG_ERR as sighandler_t { - None - } else { - unsafe { libc::signal(signalnum, handler) }; - Some(handler) + #[cfg(unix)] + { + // Query without changing disposition: even temporarily ignoring SIGCHLD + // can discard a child's exit status, and signal() also replaces masks. + let mut action = core::mem::MaybeUninit::::uninit(); + if unsafe { libc::sigaction(signalnum, core::ptr::null(), action.as_mut_ptr()) } == 0 { + Some(unsafe { action.assume_init() }.sa_sigaction) + } else { + None + } + } + #[cfg(windows)] + { + let handler = unsafe { libc::signal(signalnum, libc::SIG_IGN) }; + if handler == libc::SIG_ERR as sighandler_t { + None + } else { + unsafe { libc::signal(signalnum, handler) }; + Some(handler) + } } } @@ -450,3 +464,49 @@ pub fn sigset_contains(mask: libc::sigset_t, signum: i32) -> bool { pub fn valid_signals(_max_signum: usize) -> io::Result> { Ok(VALID_SIGNALS.to_vec()) } + +#[cfg(test)] +#[cfg(unix)] +mod tests { + #[test] + fn probing_preserves_signal_flags_and_mask() { + unsafe extern "C" fn handler(_: libc::c_int) {} + + struct Restore(libc::sigaction); + impl Drop for Restore { + fn drop(&mut self) { + unsafe { libc::sigaction(libc::SIGUSR2, &self.0, core::ptr::null_mut()) }; + } + } + + // No other host_env test uses SIGUSR2; retain the host disposition even + // when an assertion fails. Do not deliver any signal to the process. + unsafe { + let mut action: libc::sigaction = core::mem::zeroed(); + action.sa_sigaction = handler as *const () as libc::sighandler_t; + action.sa_flags = libc::SA_NODEFER; + assert_eq!(libc::sigemptyset(&mut action.sa_mask), 0); + assert_eq!(libc::sigaddset(&mut action.sa_mask, libc::SIGUSR1), 0); + let mut original = core::mem::MaybeUninit::uninit(); + assert_eq!( + libc::sigaction(libc::SIGUSR2, &action, original.as_mut_ptr()), + 0 + ); + let _restore = Restore(original.assume_init()); + + assert_eq!( + super::probe_handler(libc::SIGUSR2), + Some(action.sa_sigaction) + ); + let mut observed = core::mem::MaybeUninit::::uninit(); + assert_eq!( + libc::sigaction(libc::SIGUSR2, core::ptr::null(), observed.as_mut_ptr()), + 0 + ); + let observed = observed.assume_init(); + assert_eq!(observed.sa_sigaction, action.sa_sigaction); + assert_ne!(observed.sa_flags & libc::SA_NODEFER, 0); + assert_eq!(libc::sigismember(&observed.sa_mask, libc::SIGUSR1), 1); + } + } +}