Skip to content

Commit 6fa7358

Browse files
authored
Don't leak signal handler pipe into child processes (#3035)
1 parent f620cf8 commit 6fa7358

3 files changed

Lines changed: 31 additions & 3 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ unicode-width = "0.2.0"
5252
uuid = { version = "1.0.0", features = ["v4"] }
5353

5454
[target.'cfg(unix)'.dependencies]
55-
nix = { version = "0.30.1", features = ["signal", "user"] }
55+
nix = { version = "0.30.1", features = ["signal", "user", "fs"] }
5656

5757
[target.'cfg(windows)'.dependencies]
5858
ctrlc = { version = "3.1.1", features = ["termination"] }

src/error.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ pub(crate) enum Error<'src> {
194194
source: ctrlc::Error,
195195
},
196196
#[cfg(unix)]
197+
SignalHandlerPipeCloexec {
198+
io_error: io::Error,
199+
},
200+
#[cfg(unix)]
197201
SignalHandlerPipeOpen {
198202
io_error: io::Error,
199203
},
@@ -706,8 +710,12 @@ impl ColorDisplay for Error<'_> {
706710
write!(f, "Could not install signal handler: {source}")?;
707711
}
708712
#[cfg(unix)]
713+
SignalHandlerPipeCloexec { io_error } => {
714+
write!(f, "I/O error setting O_CLOEXEC on signal handler pipe: {io_error}")?;
715+
}
716+
#[cfg(unix)]
709717
SignalHandlerPipeOpen { io_error } => {
710-
write!(f, "I/O error opening pipe for signal handler: {io_error}")?;
718+
write!(f, "I/O error opening signal handler pipe: {io_error}")?;
711719
}
712720
#[cfg(unix)]
713721
SignalHandlerSigaction { io_error, signal } => {

src/signals.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ use {
22
super::*,
33
nix::{
44
errno::Errno,
5+
fcntl::{FcntlArg, FdFlag},
56
sys::signal::{SaFlags, SigAction, SigHandler, SigSet},
67
},
78
std::{
89
fs::File,
910
io::Read,
10-
os::fd::{BorrowedFd, IntoRawFd},
11+
os::fd::{BorrowedFd, IntoRawFd, OwnedFd},
1112
sync::atomic::{self, AtomicI32},
1213
},
1314
};
@@ -62,6 +63,22 @@ extern "C" fn handler(signal: libc::c_int) {
6263
errno.set();
6364
}
6465

66+
fn fcntl(fd: &OwnedFd, arg: FcntlArg) -> RunResult<'static, libc::c_int> {
67+
nix::fcntl::fcntl(fd, arg).map_err(|errno| Error::SignalHandlerPipeCloexec {
68+
io_error: errno.into(),
69+
})
70+
}
71+
72+
fn set_cloexec(fd: &OwnedFd) -> RunResult<'static> {
73+
// It would be better to use pipe2(O_CLOEXEC) rather than pipe-then-fcntl,
74+
// but it isn't supported on all platforms (most notably, not macos) and in
75+
// the atomicity guarantees that pipe2 provides aren't needed.
76+
let existing_flags = fcntl(fd, FcntlArg::F_GETFD)?;
77+
let combined_flags = FdFlag::from_bits_retain(existing_flags) | FdFlag::FD_CLOEXEC;
78+
fcntl(fd, FcntlArg::F_SETFD(combined_flags))?;
79+
Ok(())
80+
}
81+
6582
pub(crate) struct Signals(File);
6683

6784
impl Signals {
@@ -70,6 +87,9 @@ impl Signals {
7087
io_error: errno.into(),
7188
})?;
7289

90+
set_cloexec(&read)?;
91+
set_cloexec(&write)?;
92+
7393
if WRITE
7494
.compare_exchange(
7595
INVALID_FILENO,

0 commit comments

Comments
 (0)