From 0638857fd28429f0a167bcb17c1f15da87ca99f5 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Mon, 23 Feb 2026 12:42:56 -0800 Subject: [PATCH] fix(linux-sandbox): skip nested proxy netns setup --- codex-rs/linux-sandbox/src/linux_run_main.rs | 73 +++++++++++++++++++ .../linux-sandbox/src/linux_run_main_tests.rs | 20 +++++ 2 files changed, 93 insertions(+) diff --git a/codex-rs/linux-sandbox/src/linux_run_main.rs b/codex-rs/linux-sandbox/src/linux_run_main.rs index c9fdd06a32..2f017a2b5c 100644 --- a/codex-rs/linux-sandbox/src/linux_run_main.rs +++ b/codex-rs/linux-sandbox/src/linux_run_main.rs @@ -90,6 +90,22 @@ pub fn run_main() -> ! { } ensure_inner_stage_mode_is_valid(apply_seccomp_then_exec, use_bwrap_sandbox); + let disable_nested_managed_proxy = should_disable_managed_proxy_for_nested_bwrap( + allow_network_for_proxy, + apply_seccomp_then_exec, + ); + if disable_nested_managed_proxy { + eprintln!( + "codex-linux-sandbox: nested Codex bubblewrap sandbox detected; \ + skipping inner managed-proxy network namespace setup and relying on outer sandbox network restrictions" + ); + } + let allow_network_for_proxy = if disable_nested_managed_proxy { + false + } else { + allow_network_for_proxy + }; + // Inner stage: apply seccomp/no_new_privs after bubblewrap has already // established the filesystem view. if apply_seccomp_then_exec { @@ -175,6 +191,63 @@ fn ensure_inner_stage_mode_is_valid(apply_seccomp_then_exec: bool, use_bwrap_san } } +fn should_disable_managed_proxy_for_nested_bwrap( + allow_network_for_proxy: bool, + apply_seccomp_then_exec: bool, +) -> bool { + should_disable_managed_proxy_for_nested_bwrap_with_ancestor_bwrap( + allow_network_for_proxy, + apply_seccomp_then_exec, + has_bwrap_ancestor(), + ) +} + +fn should_disable_managed_proxy_for_nested_bwrap_with_ancestor_bwrap( + allow_network_for_proxy: bool, + apply_seccomp_then_exec: bool, + has_bwrap_ancestor: bool, +) -> bool { + allow_network_for_proxy && !apply_seccomp_then_exec && has_bwrap_ancestor +} + +fn has_bwrap_ancestor() -> bool { + const MAX_ANCESTOR_DEPTH: usize = 32; + let mut pid = match parent_pid_of(std::process::id()) { + Some(parent_pid) => parent_pid, + None => return false, + }; + + for _ in 0..MAX_ANCESTOR_DEPTH { + if pid == 0 { + return false; + } + if process_comm(pid).as_deref() == Some("bwrap") { + return true; + } + pid = match parent_pid_of(pid) { + Some(parent_pid) if parent_pid != pid => parent_pid, + _ => return false, + }; + } + + false +} + +fn process_comm(pid: u32) -> Option { + let path = format!("/proc/{pid}/comm"); + let comm = std::fs::read_to_string(path).ok()?; + Some(comm.trim().to_string()) +} + +fn parent_pid_of(pid: u32) -> Option { + let path = format!("/proc/{pid}/status"); + let status = std::fs::read_to_string(path).ok()?; + status + .lines() + .find_map(|line| line.strip_prefix("PPid:")) + .and_then(|value| value.trim().parse::().ok()) +} + fn run_bwrap_with_proc_fallback( sandbox_policy_cwd: &Path, sandbox_policy: &codex_protocol::protocol::SandboxPolicy, diff --git a/codex-rs/linux-sandbox/src/linux_run_main_tests.rs b/codex-rs/linux-sandbox/src/linux_run_main_tests.rs index 11471afbe7..2abffc1b0c 100644 --- a/codex-rs/linux-sandbox/src/linux_run_main_tests.rs +++ b/codex-rs/linux-sandbox/src/linux_run_main_tests.rs @@ -157,3 +157,23 @@ fn valid_inner_stage_modes_do_not_panic() { ensure_inner_stage_mode_is_valid(false, true); ensure_inner_stage_mode_is_valid(true, true); } + +#[test] +fn nested_managed_proxy_fallback_only_applies_to_outer_stage() { + assert_eq!( + should_disable_managed_proxy_for_nested_bwrap_with_ancestor_bwrap(true, false, true), + true + ); + assert_eq!( + should_disable_managed_proxy_for_nested_bwrap_with_ancestor_bwrap(true, true, true), + false + ); + assert_eq!( + should_disable_managed_proxy_for_nested_bwrap_with_ancestor_bwrap(true, false, false), + false + ); + assert_eq!( + should_disable_managed_proxy_for_nested_bwrap_with_ancestor_bwrap(false, false, true), + false + ); +}