From aeb18daf62e75fd9dddbdfaf7a987a5bdf8b26fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Bombo?= Date: Mon, 27 Jul 2026 20:48:52 -0500 Subject: [PATCH] agent/rustjail: Fix double close on pipes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 531877f upgraded nix which changed unistd::pipe()'s return from RawFd (alias of i32) to OwnedFd (with a destructor that closes the underlying fd). This led to a double close since PipeStream would close an fd owned by OwnedFd. I fix this by transfering ownership of the pipe to PipeStream. The double close breaks debug builds only, similar to ecb22cb, with the following error: fatal runtime error: IO Safety violation: owned file descriptor already closed Before the nix upgrade: * PipeStream closes the RawFd. * RawFd doesn't have a destructor, no double close => no issue. After the nix upgrade: * PipeStream closes the underlying fd of OwnedFd. * OwnedFd double closes the same underlying fd => error. After this fix: * PipeStream takes ownership of the underlying fd of OwnedFd and closes it. * OwnedFd doesn't own the underlying fd anymore => no double close. Fixes: 531877f Generated-By: GitHub Copilot Signed-off-by: Aurélien Bombo --- src/agent/rustjail/src/container.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/agent/rustjail/src/container.rs b/src/agent/rustjail/src/container.rs index 78dde295c7..3cd440ef98 100644 --- a/src/agent/rustjail/src/container.rs +++ b/src/agent/rustjail/src/container.rs @@ -1002,7 +1002,8 @@ impl BaseContainer for LinuxContainer { .map_err(|e| warn!(logger, "fcntl pfd log FD_CLOEXEC {:?}", e)); let child_logger = logger.new(o!("action" => "child process log")); - let log_handler = setup_child_logger(pfd_log.as_fd().as_raw_fd(), child_logger); + let log_stream = PipeStream::new(pfd_log.into_raw_fd())?; + let log_handler = setup_child_logger(log_stream, child_logger); let (prfd, cwfd) = unistd::pipe().context("failed to create pipe")?; let (crfd, pwfd) = unistd::pipe().context("failed to create pipe")?; @@ -1013,8 +1014,8 @@ impl BaseContainer for LinuxContainer { let _ = fcntl::fcntl(&pwfd, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)) .map_err(|e| warn!(logger, "fcntl pwfd FD_COLEXEC {:?}", e)); - let mut pipe_r = PipeStream::from_fd(prfd.as_fd().as_raw_fd()); - let mut pipe_w = PipeStream::from_fd(pwfd.as_fd().as_raw_fd()); + let mut pipe_r = PipeStream::new(prfd.into_raw_fd())?; + let mut pipe_w = PipeStream::new(pwfd.into_raw_fd())?; let child_stdin: std::process::Stdio; let child_stdout: std::process::Stdio; @@ -1492,9 +1493,11 @@ fn get_namespaces(linux: &Linux) -> Vec { .collect() } -pub fn setup_child_logger(fd: RawFd, child_logger: Logger) -> tokio::task::JoinHandle<()> { +pub fn setup_child_logger( + log_file_stream: PipeStream, + child_logger: Logger, +) -> tokio::task::JoinHandle<()> { tokio::spawn(async move { - let log_file_stream = PipeStream::from_fd(fd); let buf_reader_stream = tokio::io::BufReader::new(log_file_stream); let mut lines = buf_reader_stream.lines();