agent/rustjail: Fix double close on pipes

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 <abombo@microsoft.com>
This commit is contained in:
Aurélien Bombo
2026-07-27 20:48:52 -05:00
parent 3c56d0441f
commit aeb18daf62

View File

@@ -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<LinuxNamespace> {
.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();