Skip to content

Commit

Permalink
Fix SignalFd::set_mask
Browse files Browse the repository at this point in the history
In 0.27.0 it inadvertently closed the file descriptor, leaving the
SignalFd object accessing a stale file descriptor.

Fixes #2116
  • Loading branch information
asomers committed Oct 1, 2023
1 parent 57663c0 commit 670c284
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ This project adheres to [Semantic Versioning](https://semver.org/).
- Fix `SigSet` incorrect implementation of `Eq`, `PartialEq` and `Hash`
([#1946](https://github.com/nix-rust/nix/pull/1946))

- 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
Expand Down
13 changes: 12 additions & 1 deletion src/sys/signalfd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<siginfo>> {
Expand All @@ -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 {
Expand Down
29 changes: 29 additions & 0 deletions test/sys/test_signalfd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

0 comments on commit 670c284

Please sign in to comment.