From bb2ea80b6c3de0c5b3e83d77231e36f2b6328d42 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sat, 30 Sep 2023 10:31:46 -0600 Subject: [PATCH] Fix SignalFd::set_mask In 0.27.0 it inadvertently closed the file descriptor, leaving the SignalFd object accessing a stale file descriptor. Fixes #2116 --- CHANGELOG.md | 4 ++++ src/sys/signalfd.rs | 13 ++++++++++++- test/sys/test_signalfd.rs | 29 +++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a4c441805..0d21cad4b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ This project adheres to [Semantic Versioning](https://semver.org/). - Fixed the function signature of `recvmmsg`, potentially causing UB ([#2119](https://github.com/nix-rust/nix/issues/2119)) +- Fix `SignalFd::set_mask`. In 0.27.0 it would actually close the file + descriptor. + ([#2141](https://github.com/nix-rust/nix/pull/2141)) + ### Changed - The following APIs now take an implementation of `AsFd` rather than a diff --git a/src/sys/signalfd.rs b/src/sys/signalfd.rs index 697bcf073d..36516074a6 100644 --- a/src/sys/signalfd.rs +++ b/src/sys/signalfd.rs @@ -101,7 +101,7 @@ impl SignalFd { } pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> { - _signalfd(Some(self.0.as_fd()), mask, SfdFlags::empty()).map(drop) + self.update(mask, SfdFlags::empty()) } pub fn read_signal(&mut self) -> Result> { @@ -119,6 +119,17 @@ impl SignalFd { Err(error) => Err(error), } } + + fn update(&self, mask: &SigSet, flags: SfdFlags) -> Result<()> { + let raw_fd = self.0.as_raw_fd(); + unsafe { + Errno::result(libc::signalfd( + raw_fd, + mask.as_ref(), + flags.bits(), + )).map(drop) + } + } } impl AsFd for SignalFd { diff --git a/test/sys/test_signalfd.rs b/test/sys/test_signalfd.rs index 87153c9572..6a279d1832 100644 --- a/test/sys/test_signalfd.rs +++ b/test/sys/test_signalfd.rs @@ -25,3 +25,32 @@ fn test_signalfd() { let signo = Signal::try_from(res.ssi_signo as i32).unwrap(); assert_eq!(signo, signal::SIGUSR1); } + +/// Update the signal mask of an already existing signalfd. +#[test] +fn test_signalfd_setmask() { + use nix::sys::signal::{self, raise, SigSet, Signal}; + use nix::sys::signalfd::SignalFd; + + // Grab the mutex for altering signals so we don't interfere with other tests. + let _m = crate::SIGNAL_MTX.lock(); + + // Block the SIGUSR1 signal from automatic processing for this thread + let mut mask = SigSet::empty(); + + let mut fd = SignalFd::new(&mask).unwrap(); + + mask.add(signal::SIGUSR1); + mask.thread_block().unwrap(); + fd.set_mask(&mask).unwrap(); + + // Send a SIGUSR1 signal to the current process. Note that this uses `raise` instead of `kill` + // because `kill` with `getpid` isn't correct during multi-threaded execution like during a + // cargo test session. Instead use `raise` which does the correct thing by default. + raise(signal::SIGUSR1).expect("Error: raise(SIGUSR1) failed"); + + // And now catch that same signal. + let res = fd.read_signal().unwrap().unwrap(); + let signo = Signal::try_from(res.ssi_signo as i32).unwrap(); + assert_eq!(signo, signal::SIGUSR1); +}