diff --git a/codex-rs/linux-sandbox/src/fd_cleanup.rs b/codex-rs/linux-sandbox/src/fd_cleanup.rs index 3908f9f8fb..ca916e0b17 100644 --- a/codex-rs/linux-sandbox/src/fd_cleanup.rs +++ b/codex-rs/linux-sandbox/src/fd_cleanup.rs @@ -1,49 +1,20 @@ //! File descriptor hygiene before entering the sandboxed command. -/// Mark helper-inherited descriptors close-on-exec unless they are standard -/// input/output/error. +/// Close helper-inherited descriptors unless they are standard input/output/error +/// or already close-on-exec. /// /// The sandboxed command can still create allowed local IPC after exec, but it /// must not inherit an already-connected network socket from the launcher. -pub(crate) fn mark_inherited_fds_cloexec() { - match set_cloexec_with_close_range() { - Ok(()) => return, - Err(err) if can_fallback_from_close_range(&err) => {} - Err(err) => panic!("failed to mark inherited file descriptors close-on-exec: {err}"), - } - +pub(crate) fn close_inherited_exec_fds() { let fds = match non_stdio_fds_from_proc() { Ok(fds) => fds, Err(err) => panic!("failed to enumerate inherited file descriptors: {err}"), }; for fd in fds { - set_fd_cloexec_ignoring_badf(fd); + close_fd_if_inheritable(fd); } } -fn set_cloexec_with_close_range() -> std::io::Result<()> { - let result = unsafe { - libc::syscall( - libc::SYS_close_range, - (libc::STDERR_FILENO + 1) as libc::c_uint, - u32::MAX as libc::c_uint, - libc::CLOSE_RANGE_CLOEXEC, - ) - }; - if result == 0 { - Ok(()) - } else { - Err(std::io::Error::last_os_error()) - } -} - -fn can_fallback_from_close_range(err: &std::io::Error) -> bool { - matches!( - err.raw_os_error(), - Some(code) if code == libc::ENOSYS || code == libc::EPERM || code == libc::EINVAL - ) -} - fn non_stdio_fds_from_proc() -> std::io::Result> { let mut fds = Vec::new(); for entry in std::fs::read_dir("/proc/self/fd")? { @@ -62,7 +33,7 @@ fn non_stdio_fds_from_proc() -> std::io::Result> { Ok(fds) } -fn set_fd_cloexec_ignoring_badf(fd: libc::c_int) { +fn close_fd_if_inheritable(fd: libc::c_int) { let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) }; if flags == -1 { let err = std::io::Error::last_os_error(); @@ -71,12 +42,15 @@ fn set_fd_cloexec_ignoring_badf(fd: libc::c_int) { } return; } + if flags & libc::FD_CLOEXEC != 0 { + return; + } - let result = unsafe { libc::fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC) }; - if result == -1 { + let result = unsafe { libc::close(fd) }; + if result != 0 { let err = std::io::Error::last_os_error(); if err.raw_os_error() != Some(libc::EBADF) { - panic!("failed to mark inherited file descriptor {fd} close-on-exec: {err}"); + panic!("failed to close inherited file descriptor {fd}: {err}"); } } } diff --git a/codex-rs/linux-sandbox/src/linux_run_main.rs b/codex-rs/linux-sandbox/src/linux_run_main.rs index 605aa3601b..a9197afb97 100644 --- a/codex-rs/linux-sandbox/src/linux_run_main.rs +++ b/codex-rs/linux-sandbox/src/linux_run_main.rs @@ -10,7 +10,7 @@ use std::path::PathBuf; use crate::bwrap::BwrapNetworkMode; use crate::bwrap::BwrapOptions; use crate::bwrap::create_bwrap_command_args; -use crate::fd_cleanup::mark_inherited_fds_cloexec; +use crate::fd_cleanup::close_inherited_exec_fds; use crate::landlock::apply_sandbox_policy_to_current_thread; use crate::launcher::exec_bwrap; use crate::launcher::preferred_bwrap_supports_argv0; @@ -730,7 +730,7 @@ fn exec_or_panic(command: Vec) -> ! { let mut c_args_ptrs: Vec<*const libc::c_char> = c_args.iter().map(|arg| arg.as_ptr()).collect(); c_args_ptrs.push(std::ptr::null()); - mark_inherited_fds_cloexec(); + close_inherited_exec_fds(); unsafe { libc::execvp(c_command.as_ptr(), c_args_ptrs.as_ptr());