From 6b3c4c227baaf0a7fdf8c2871a0a12027ac283f0 Mon Sep 17 00:00:00 2001 From: Noa Date: Fri, 13 Sep 2024 11:51:37 -0400 Subject: [PATCH] Better comments for set_aux_fd --- src/tools/run-make-support/src/command.rs | 35 ++++++++++++++++------- src/tools/run-make-support/src/macros.rs | 5 ++++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs index ee900d3cac484..9125ec3d3e60e 100644 --- a/src/tools/run-make-support/src/command.rs +++ b/src/tools/run-make-support/src/command.rs @@ -152,6 +152,11 @@ impl Command { } /// Set an auxiliary stream passed to the process, besides the stdio streams. + /// + /// Use with caution - ideally, only set one aux fd; if there are multiple, + /// their old `fd` may overlap with another's `newfd`, and thus will break. + /// If you need more than 1 auxiliary file descriptor, rewrite this function + /// to be able to support that. #[cfg(unix)] pub fn set_aux_fd>( &mut self, @@ -161,18 +166,28 @@ impl Command { use std::os::fd::AsRawFd; use std::os::unix::process::CommandExt; + let cvt = |x| if x == -1 { Err(std::io::Error::last_os_error()) } else { Ok(()) }; + let fd = fd.into(); - unsafe { - self.cmd.pre_exec(move || { - let fd = fd.as_raw_fd(); - let ret = if fd == newfd { - libc::fcntl(fd, libc::F_SETFD, 0) - } else { - libc::dup2(fd, newfd) - }; - if ret == -1 { Err(std::io::Error::last_os_error()) } else { Ok(()) } - }); + if fd.as_raw_fd() == newfd { + // if fd is already where we want it, just turn off FD_CLOEXEC + // SAFETY: io-safe: we have ownership over fd + cvt(unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, 0) }) + .expect("disabling CLOEXEC failed"); + // we still unconditionally set the pre_exec function, since it captures + // `fd`, and we want to ensure that it stays open until the fork } + let pre_exec = move || { + if fd.as_raw_fd() != newfd { + // set newfd to point to the same file as fd + // SAFETY: io-"safe": newfd is. not necessarily an unused fd. + // however, we're + cvt(unsafe { libc::dup2(fd.as_raw_fd(), newfd) })?; + } + Ok(()) + }; + // SAFETY: dup2 is pre-exec-safe + unsafe { self.cmd.pre_exec(pre_exec) }; self } diff --git a/src/tools/run-make-support/src/macros.rs b/src/tools/run-make-support/src/macros.rs index 322ee5314e239..43844edf4127d 100644 --- a/src/tools/run-make-support/src/macros.rs +++ b/src/tools/run-make-support/src/macros.rs @@ -81,6 +81,11 @@ macro_rules! impl_common_helpers { } /// Set an auxiliary stream passed to the process, besides the stdio streams. + /// + /// Use with caution - ideally, only set one aux fd; if there are multiple, + /// their old `fd` may overlap with another's `newfd`, and thus will break. + /// If you need more than 1 auxiliary file descriptor, rewrite this function + /// to be able to support that. #[cfg(unix)] pub fn set_aux_fd>( &mut self,