fix: mark inherited fds close-on-exec before sandbox exec

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
viyatb-oai
2026-04-08 15:40:09 -07:00
co-authored by Codex
parent f3da8367f7
commit 074b4a4a24
2 changed files with 25 additions and 16 deletions
+23 -14
View File
@@ -1,14 +1,15 @@
//! File descriptor hygiene before entering the sandboxed command.
/// Close helper-inherited descriptors that are not standard input/output/error.
/// Mark helper-inherited descriptors close-on-exec unless they are standard
/// input/output/error.
///
/// 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 close_inherited_fds() {
match close_fds_with_close_range() {
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 close inherited file descriptors: {err}"),
Err(err) => panic!("failed to mark inherited file descriptors close-on-exec: {err}"),
}
let fds = match non_stdio_fds_from_proc() {
@@ -16,17 +17,17 @@ pub(crate) fn close_inherited_fds() {
Err(err) => panic!("failed to enumerate inherited file descriptors: {err}"),
};
for fd in fds {
close_fd_ignoring_badf(fd);
set_fd_cloexec_ignoring_badf(fd);
}
}
fn close_fds_with_close_range() -> std::io::Result<()> {
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,
0 as libc::c_uint,
libc::CLOSE_RANGE_CLOEXEC,
)
};
if result == 0 {
@@ -39,7 +40,7 @@ fn close_fds_with_close_range() -> std::io::Result<()> {
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
Some(code) if code == libc::ENOSYS || code == libc::EPERM || code == libc::EINVAL
)
}
@@ -61,13 +62,21 @@ fn non_stdio_fds_from_proc() -> std::io::Result<Vec<libc::c_int>> {
Ok(fds)
}
fn close_fd_ignoring_badf(fd: libc::c_int) {
let result = unsafe { libc::close(fd) };
if result == 0 {
fn set_fd_cloexec_ignoring_badf(fd: libc::c_int) {
let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
if flags == -1 {
let err = std::io::Error::last_os_error();
if err.raw_os_error() != Some(libc::EBADF) {
panic!("failed to inspect inherited file descriptor {fd}: {err}");
}
return;
}
let err = std::io::Error::last_os_error();
if err.raw_os_error() != Some(libc::EBADF) {
panic!("failed to close inherited file descriptor {fd}: {err}");
let result = unsafe { libc::fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC) };
if result == -1 {
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}");
}
}
}
+2 -2
View File
@@ -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::close_inherited_fds;
use crate::fd_cleanup::mark_inherited_fds_cloexec;
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<String>) -> ! {
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());
close_inherited_fds();
mark_inherited_fds_cloexec();
unsafe {
libc::execvp(c_command.as_ptr(), c_args_ptrs.as_ptr());