Linux沙箱降级与诊断
Linux 沙箱的“降级”是若干个彼此独立的决策:启动时可以提示缺少系统 bwrap,兼容旧版 bwrap 时可以改写参数,受限容器不能挂载新的 /proc 时可以只关闭这一个挂载;但 helper 路径、工作目录、命令构造或完整的 Bubblewrap 启动失败,都不会把原命令放回宿主机执行。理解这些分支,关键是沿着“请求转换 → helper 两阶段启动 → bwrap launcher → 诊断消费者”阅读,而不是把所有 warning 都看成同一种 fallback。
阅读前可先查看Bubblewrap命令构造和Linux Seccomp与Namespace,前者解释 mount argv 如何生成,后者解释 namespace、capability 与 Seccomp 如何在 inner stage 生效。
本文从 Linux 实现和对应测试出发,重点覆盖:
- bwrap 的 PATH 搜索、工作目录排除和 user namespace 探测;
- system/bundled launcher 的选择、能力探测与摘要校验;
/proc预检的最小权限模型和单点降级;- WSL1、helper 缺失、cwd 无效、构造失败的错误映射;
- 启动 warning 与运行时命令失败分别由谁消费。
1. 请求先决定模式
run_main 先解析权限配置。默认路径先准备可选的代理路由,再构造 inner helper 参数,最后进入 run_bwrap_with_proc_fallback;只有显式传入 --use-legacy-landlock 才执行 Landlock 分支。因此,bwrap 的失败不是“自动换成 Landlock”的信号。
源码位置:codex-rs/linux-sandbox/src/linux_run_main.rs :: run_main
if !use_legacy_landlock {
// Outer stage: bubblewrap first, then re-enter this binary in the
// sandboxed environment to apply seccomp. This path never falls back
// to legacy Landlock on failure.
let proxy_route_spec = if allow_network_for_proxy {
let (proxy_route_spec, socket_dir) = prepare_host_proxy_route_spec()
.unwrap_or_else(|err| panic!("failed to prepare host proxy routing bridge: {err}"));
file_system_sandbox_policy = file_system_sandbox_policy.with_additional_readable_roots(
&sandbox_policy_cwd,
std::slice::from_ref(&socket_dir),
);
Some(proxy_route_spec)
} else {
None
};
let inner = build_inner_seccomp_command(InnerSeccompCommandArgs {
sandbox_policy_cwd: &sandbox_policy_cwd,
command_cwd: command_cwd.as_deref(),
permission_profile: &permission_profile,
allow_network_for_proxy,
proxy_route_spec,
command,
});
run_bwrap_with_proc_fallback(
&sandbox_policy_cwd,
command_cwd.as_deref(),
&file_system_sandbox_policy,
network_sandbox_policy,
inner,
!no_proc,
allow_network_for_proxy,
);
}
// Legacy path: Landlock enforcement only, when bwrap sandboxing is not enabled.
if let Err(e) = apply_permission_profile_to_current_thread(
&permission_profile,
&sandbox_policy_cwd,
/*apply_landlock_fs*/ true,
allow_network_for_proxy,
/*proxy_routed_network*/ false,
) {
panic!("error applying legacy Linux sandbox restrictions: {e:?}");
}
exec_or_panic(command);ensure_legacy_landlock_mode_supports_policy 还会拒绝需要直接运行时约束、却被要求走 legacy Landlock 的权限组合。降级入口本身仍受策略约束,不是无条件的逃生通道。
2. PATH搜索提示
系统 bwrap 的发现函数把当前工作目录作为排除边界。它先用 which_in_all 找到所有候选,再 canonicalize;只要候选路径位于非根 cwd 内,就跳过。这防止项目目录中名为 bwrap 的文件被当作系统 launcher。
源码位置:codex-rs/sandboxing/src/bwrap.rs :: find_system_bwrap_in_path、find_system_bwrap_in_search_paths
pub fn find_system_bwrap_in_path() -> Option<PathBuf> {
let search_path = std::env::var_os("PATH")?;
let cwd = std::env::current_dir().ok()?;
find_system_bwrap_in_search_paths(std::env::split_paths(&search_path), &cwd)
}
fn find_system_bwrap_in_search_paths(
search_paths: impl IntoIterator<Item = PathBuf>,
cwd: &Path,
) -> Option<PathBuf> {
let search_path = std::env::join_paths(search_paths).ok()?;
let cwd = std::fs::canonicalize(cwd).unwrap_or_else(|_| cwd.to_path_buf());
let cwd_is_root = cwd.parent().is_none();
which::which_in_all(SYSTEM_BWRAP_PROGRAM, Some(search_path), &cwd)
.ok()?
.find_map(|path| {
let path = std::fs::canonicalize(path).ok()?;
if !cwd_is_root && path.starts_with(&cwd) {
None
} else {
Some(path)
}
})
}warning 只在需要平台沙箱的权限配置下产生。WSL1 先于 PATH 结果被判断;没有系统 bwrap 时则提示安装,并明确说明接下来会使用 bundled bwrap;探测到 user namespace 失败时给出能力提示。
源码位置:codex-rs/sandboxing/src/bwrap.rs :: system_bwrap_warning
pub fn system_bwrap_warning(permission_profile: &PermissionProfile) -> Option<String> {
if !should_warn_about_system_bwrap(permission_profile) {
return None;
}
let system_bwrap_path = find_system_bwrap_in_path();
system_bwrap_warning_for_path(system_bwrap_path.as_deref())
}源码位置:codex-rs/sandboxing/src/bwrap.rs :: system_bwrap_warning_for_path
fn system_bwrap_warning_for_path(system_bwrap_path: Option<&Path>) -> Option<String> {
if is_wsl1() {
return Some(WSL1_BWRAP_WARNING.to_string());
}
let Some(system_bwrap_path) = system_bwrap_path else {
return Some(MISSING_BWRAP_WARNING.to_string());
};
if !system_bwrap_has_user_namespace_access(system_bwrap_path, SYSTEM_BWRAP_PROBE_TIMEOUT) {
return Some(USER_NAMESPACE_WARNING.to_string());
}
None
}3. Launcher能力探测
exec_bwrap 总是先插入 --as-pid-1。系统 launcher 必须从 --help 输出中确认该能力和 --perms;--argv0 与 --ro-bind-fd 则分别记录为可选能力。系统 bwrap 不满足必需能力时,选择逻辑才会继续寻找 bundled 版本。
源码位置:codex-rs/linux-sandbox/src/launcher.rs :: exec_bwrap
pub(crate) fn exec_bwrap(mut argv: Vec<String>, preserved_files: Vec<File>) -> ! {
argv.insert(1, "--as-pid-1".to_string());
match preferred_bwrap_launcher() {
BubblewrapLauncher::System(launcher) => {
if !launcher.supports_ro_bind_fd {
translate_legacy_bwrap_fd_mounts(&mut argv)
.unwrap_or_else(|error| panic!("invalid legacy bubblewrap fd mount: {error}"));
}
exec_system_bwrap(&launcher.program, argv, preserved_files)
}
BubblewrapLauncher::Bundled(launcher) => launcher.exec(argv, preserved_files),
BubblewrapLauncher::Unavailable => {
panic!(
"bubblewrap is unavailable: no system bwrap was found on PATH and no bundled \
codex-resources/bwrap binary was found next to the Codex executable"
)
}
}
}源码位置:codex-rs/linux-sandbox/src/launcher.rs :: preferred_bwrap_launcher
fn preferred_bwrap_launcher() -> BubblewrapLauncher {
static LAUNCHER: OnceLock<BubblewrapLauncher> = OnceLock::new();
LAUNCHER
.get_or_init(|| {
if let Some(path) = find_system_bwrap_in_path()
&& let Some(launcher) = system_bwrap_launcher_for_path(&path)
{
return BubblewrapLauncher::System(launcher);
}
match bundled_bwrap::launcher() {
Some(launcher) => BubblewrapLauncher::Bundled(launcher),
None => BubblewrapLauncher::Unavailable,
}
})
.clone()
}源码位置:codex-rs/linux-sandbox/src/launcher.rs :: system_bwrap_launcher_for_path_with_probe
fn system_bwrap_launcher_for_path_with_probe(
system_bwrap_path: &Path,
system_bwrap_capabilities: impl FnOnce(&Path) -> Option<SystemBwrapCapabilities>,
) -> Option<SystemBwrapLauncher> {
if !system_bwrap_path.is_file() {
return None;
}
let Some(SystemBwrapCapabilities {
supports_argv0,
supports_perms: true,
supports_ro_bind_fd,
}) = system_bwrap_capabilities(system_bwrap_path)
else {
return None;
};
let system_bwrap_path = match AbsolutePathBuf::from_absolute_path(system_bwrap_path) {
Ok(path) => path,
Err(err) => panic!(
"failed to normalize system bubblewrap path {}: {err}",
system_bwrap_path.display()
),
};
Some(SystemBwrapLauncher {
program: system_bwrap_path,
supports_argv0,
supports_ro_bind_fd,
})
}源码位置:codex-rs/linux-sandbox/src/launcher.rs :: system_bwrap_capabilities
fn system_bwrap_capabilities(system_bwrap_path: &Path) -> Option<SystemBwrapCapabilities> {
// bubblewrap added `--argv0` in v0.9.0:
// https://github.com/containers/bubblewrap/releases/tag/v0.9.0
// Older distro packages (for example Ubuntu 20.04/22.04) ship builds that
// reject `--argv0`, so use the system binary's no-argv0 compatibility path
// in that case.
let output = match Command::new(system_bwrap_path).arg("--help").output() {
Ok(output) => output,
Err(_) => return None,
};
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
if !stdout.contains("--as-pid-1") && !stderr.contains("--as-pid-1") {
return None;
}
Some(SystemBwrapCapabilities {
supports_argv0: stdout.contains("--argv0") || stderr.contains("--argv0"),
supports_perms: stdout.contains("--perms") || stderr.contains("--perms"),
supports_ro_bind_fd: stdout.contains("--ro-bind-fd") || stderr.contains("--ro-bind-fd"),
})
}bundled 版本也不是“找到文件就执行”。它从安装上下文或兼容布局查找可执行文件,打开文件后先校验可选的 CODEX_BWRAP_SHA256 摘要;校验失败使用专用退出码,避免把未认证的二进制当作沙箱边界。
源码位置:codex-rs/linux-sandbox/src/bundled_bwrap.rs :: launcher、BundledBwrapLauncher::exec
pub(crate) fn launcher() -> Option<BundledBwrapLauncher> {
let current_exe = std::env::current_exe().ok()?;
find_for_install_context(InstallContext::current())
.or_else(|| find_legacy_for_exe(¤t_exe))
.map(|program| BundledBwrapLauncher { program })
}
impl BundledBwrapLauncher {
pub(crate) fn exec(&self, argv: Vec<String>, preserved_files: Vec<File>) -> ! {
let bwrap_file = File::open(self.program.as_path()).unwrap_or_else(|err| {
panic!(
"failed to open bundled bubblewrap {}: {err}",
self.program.as_path().display()
)
});
if let Err(err) = verify_digest(&bwrap_file, expected_sha256(), self.program.as_path()) {
eprintln!("{err}");
std::process::exit(crate::BUNDLED_BWRAP_DIGEST_VERIFICATION_FAILURE_EXIT_CODE);
}
make_files_inheritable(&preserved_files);
let fd_path = format!("/proc/self/fd/{}", bwrap_file.as_raw_fd());
let program_cstring = CString::new(fd_path.as_str())
.unwrap_or_else(|err| panic!("invalid bundled bubblewrap fd path: {err}"));
let cstrings = argv_to_cstrings(&argv);
let mut argv_ptrs: Vec<*const c_char> = cstrings
.iter()
.map(CString::as_c_str)
.map(CStr::as_ptr)
.collect();
argv_ptrs.push(std::ptr::null());
// SAFETY: `program_cstring` and every entry in `argv_ptrs` are valid C
// strings for the duration of the call. On success `execv` does not return.
unsafe {
libc::execv(program_cstring.as_ptr(), argv_ptrs.as_ptr());
}
let err = std::io::Error::last_os_error();
panic!(
"failed to exec bundled bubblewrap {} via {fd_path}: {err}",
self.program.as_path().display()
);
}
}4. /proc降级边界
run_bwrap_with_proc_fallback 先根据网络策略选择 FullAccess、Isolated 或 ProxyOnly,再运行一个最小预检。预检失败只有在 stderr 命中 proc mount 特征时才把 mount_proc 设为 false;预检构造错误和正式 argv 构造错误都会进入 exit_with_bwrap_build_error。
源码位置:codex-rs/linux-sandbox/src/linux_run_main.rs :: run_bwrap_with_proc_fallback
fn run_bwrap_with_proc_fallback(
sandbox_policy_cwd: &Path,
command_cwd: Option<&Path>,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
inner: Vec<String>,
mount_proc: bool,
allow_network_for_proxy: bool,
) -> ! {
let network_mode = bwrap_network_mode(network_sandbox_policy, allow_network_for_proxy);
let mut mount_proc = mount_proc;
let command_cwd = command_cwd.unwrap_or(sandbox_policy_cwd);
if mount_proc
&& !preflight_proc_mount_support(network_mode)
.unwrap_or_else(|err| exit_with_bwrap_build_error(err))
{
// Keep the retry silent so sandbox-internal diagnostics do not leak into the
// child process stderr stream.
mount_proc = false;
}
let options = BwrapOptions {
mount_proc,
network_mode,
..Default::default()
};
let mut bwrap_args = build_bwrap_argv(
inner,
file_system_sandbox_policy,
sandbox_policy_cwd,
command_cwd,
options,
)
.unwrap_or_else(|err| exit_with_bwrap_build_error(err));
apply_inner_command_argv0(&mut bwrap_args.args);
run_or_exec_bwrap(bwrap_args);
}源码位置:codex-rs/linux-sandbox/src/linux_run_main.rs :: preflight_proc_mount_support、build_preflight_bwrap_argv
fn preflight_proc_mount_support(network_mode: BwrapNetworkMode) -> CodexResult<bool> {
let preflight_argv = build_preflight_bwrap_argv(network_mode)?;
let stderr = run_bwrap_in_child_capture_stderr(preflight_argv);
Ok(!is_proc_mount_failure(stderr.as_str()))
}
fn build_preflight_bwrap_argv(
network_mode: BwrapNetworkMode,
) -> CodexResult<crate::bwrap::BwrapArgs> {
let file_system_sandbox_policy =
FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
let preflight_command = vec![resolve_true_command()];
build_bwrap_argv(
preflight_command,
&file_system_sandbox_policy,
Path::new("/"),
Path::new("/"),
BwrapOptions {
mount_proc: true,
network_mode,
..Default::default()
},
)
}预检用最小文件系统策略,不把整个宿主根目录 bind 进去;因此“关闭 fresh proc”不会被误解为“用一次全盘 bind 的实验替代正式沙箱”。linux_run_main_tests.rs 对 --tmpfs /、--proc /proc 以及不存在的 --ro-bind / / 都有直接断言。
源码位置:codex-rs/linux-sandbox/src/linux_run_main.rs :: is_proc_mount_failure
fn is_proc_mount_failure(stderr: &str) -> bool {
stderr.contains("Can't mount proc")
&& stderr.contains("/newroot/proc")
&& (stderr.contains("Invalid argument")
|| stderr.contains("Operation not permitted")
|| stderr.contains("Permission denied"))
}匹配条件同时要求 mount 对象、目标路径和三种已知错误文本,普通 bind mount 的 Operation not permitted 不会触发降级。
5. 兼容旧版 bwrap
当系统 bwrap 没有 --argv0 时,helper 会把 -- 后的第一个命令替换为当前 helper 路径;有 --argv0 时则插入固定的 codex-linux-sandbox argv0。没有 --ro-bind-fd 时,launcher 把 descriptor mount 改写为 /proc/self/fd/<fd>,同时插入 --verify-fd-mount,并要求 inner stage 存在 --apply-seccomp-then-exec。这些改写都是兼容旧版能力,不是放宽认证。
源码位置:codex-rs/linux-sandbox/src/linux_run_main.rs :: apply_inner_command_argv0_for_launcher
fn apply_inner_command_argv0_for_launcher(
argv: &mut Vec<String>,
supports_argv0: bool,
argv0_fallback_command: String,
) {
let command_separator_index = argv
.iter()
.position(|arg| arg == "--")
.unwrap_or_else(|| panic!("bubblewrap argv is missing command separator '--'"));
if supports_argv0 {
argv.splice(
command_separator_index..command_separator_index,
["--argv0".to_string(), CODEX_LINUX_SANDBOX_ARG0.to_string()],
);
return;
}
let command_index = command_separator_index + 1;
let Some(command) = argv.get_mut(command_index) else {
panic!("bubblewrap argv is missing inner command after '--'");
};
*command = argv0_fallback_command;
}6. 错误映射与消费者
Sandbox transform 阶段把错误分成不同语义:无效 cwd 是 InvalidRequest,helper 缺失保留 LandlockSandboxExecutableNotProvided 这一历史错误名,WSL1 是 UnsupportedOperation。这一步发生在命令真正启动前。
源码位置:codex-rs/sandboxing/src/manager.rs :: SandboxTransformError、SandboxType::LinuxSeccomp
#[derive(Debug)]
pub enum SandboxTransformError {
InvalidCommandCwd {
cwd: PathUri,
source: io::Error,
},
InvalidSandboxPolicyCwd {
cwd: PathUri,
source: io::Error,
},
MissingLinuxSandboxExecutable,
EnvironmentNetworkProxy(String),
#[cfg(target_os = "macos")]
SeatbeltPreparation(String),
#[cfg(target_os = "linux")]
Wsl1UnsupportedForBubblewrap,
#[cfg(not(target_os = "macos"))]
SeatbeltUnavailable,
#[cfg(target_os = "windows")]
WindowsSandboxPreparation(String),
}源码位置:codex-rs/sandboxing/src/manager.rs :: SandboxType::LinuxSeccomp
SandboxType::LinuxSeccomp => {
let pending = pending_sandboxed_request?;
let exe = codex_linux_sandbox_exe
.ok_or(SandboxTransformError::MissingLinuxSandboxExecutable)?;
let allow_proxy_network = allow_network_for_proxy(enforce_managed_network);
#[cfg(target_os = "linux")]
ensure_linux_bubblewrap_is_supported(
&pending
.effective_permission_profile
.file_system_sandbox_policy(),
use_legacy_landlock,
allow_proxy_network,
is_wsl1(),
)?;
let mut args = create_linux_sandbox_command_args_for_permission_profile(
os_argv_to_strings(argv),
pending.native_command_cwd.as_path(),
&pending.effective_permission_profile,
pending.native_sandbox_policy_cwd.as_path(),
use_legacy_landlock,
allow_proxy_network,
);
let mut full_command = Vec::with_capacity(1 + args.len());
full_command.push(os_string_to_command_component(exe.as_os_str().to_owned()));
full_command.append(&mut args);
(
full_command,
Some(linux_sandbox_arg0_override(exe)),
Some(pending),
)
}源码位置:codex-rs/sandboxing/src/lib.rs :: From<SandboxTransformError> for CodexErr
impl From<SandboxTransformError> for CodexErr {
fn from(err: SandboxTransformError) -> Self {
match err {
error @ SandboxTransformError::InvalidCommandCwd { .. }
| error @ SandboxTransformError::InvalidSandboxPolicyCwd { .. } => {
CodexErr::InvalidRequest(error.to_string())
}
SandboxTransformError::MissingLinuxSandboxExecutable => {
CodexErr::LandlockSandboxExecutableNotProvided
}
SandboxTransformError::EnvironmentNetworkProxy(message) => {
CodexErr::UnsupportedOperation(message)
}
#[cfg(target_os = "macos")]
SandboxTransformError::SeatbeltPreparation(message) => {
CodexErr::UnsupportedOperation(message)
}
#[cfg(target_os = "linux")]
SandboxTransformError::Wsl1UnsupportedForBubblewrap => {
CodexErr::UnsupportedOperation(crate::bwrap::WSL1_BWRAP_WARNING.to_string())
}
#[cfg(not(target_os = "macos"))]
SandboxTransformError::SeatbeltUnavailable => CodexErr::UnsupportedOperation(
"seatbelt sandbox is only available on macOS".to_string(),
),
#[cfg(target_os = "windows")]
SandboxTransformError::WindowsSandboxPreparation(message) => {
CodexErr::UnsupportedOperation(message)
}
}
}
}运行时 warning 则由 exec 模式在 session 配置完成后展示给用户,并且只在非 JSON 模式处理;它不是把失败写成“命令已被阻止”的事件。
源码位置:codex-rs/exec/src/lib.rs :: run_exec 内的启动提示路径
// Print the effective configuration and initial request so users can see what Codex
// is using.
event_processor.print_config_summary(&config, &prompt_summary, &session_configured);
if !json_mode
&& let Some(message) =
codex_core::config::system_bwrap_warning(config.permissions.permission_profile())
{
event_processor.process_warning(message);
}相反,exit_with_bwrap_build_error 会打印构造错误并以非零状态退出;它没有调用 legacy Landlock,也没有执行未包装的用户命令。
源码位置:codex-rs/linux-sandbox/src/linux_run_main.rs :: exit_with_bwrap_build_error
fn exit_with_bwrap_build_error(err: codex_protocol::error::CodexErr) -> ! {
eprintln!("error building bubblewrap command: {err}");
std::process::exit(1);
}7. 测试与边界
测试把诊断输入做成可控 fixture:假的 bwrap 输出指定 stderr,验证 WSL1 字符串识别、无关错误不触发 warning、probe timeout 不制造确定性 warning;proc 预检则检查生成的 argv,而不是在不具备 Linux namespace 的主机上假装执行成功。
源码位置:codex-rs/sandboxing/src/bwrap_tests.rs :: system_bwrap_warning_reports_user_namespace_failures
#[test]
fn system_bwrap_warning_reports_user_namespace_failures() {
for failure in USER_NAMESPACE_FAILURES {
let fake_bwrap = write_fake_bwrap(&format!(
r#"#!/bin/sh
echo '{failure}' >&2
exit 1
"#
));
let fake_bwrap_path: &Path = fake_bwrap.as_ref();
assert_eq!(
system_bwrap_warning_for_path(Some(fake_bwrap_path)),
Some(USER_NAMESPACE_WARNING.to_string()),
"{failure}",
);
}
}源码位置:codex-rs/sandboxing/src/bwrap_tests.rs :: system_bwrap_probe_times_out_without_reporting_a_warning
#[test]
fn system_bwrap_probe_times_out_without_reporting_a_warning() {
let fake_bwrap = write_fake_bwrap(
r#"#!/bin/sh
sleep 1
exit 0
"#,
);
let fake_bwrap_path: &Path = fake_bwrap.as_ref();
let started_at = Instant::now();
assert!(system_bwrap_has_user_namespace_access(
fake_bwrap_path,
Duration::from_millis(10),
));
assert!(started_at.elapsed() < Duration::from_millis(500));
}源码位置:codex-rs/linux-sandbox/src/linux_run_main_tests.rs :: detects_proc_mount_operation_not_permitted_failure
#[test]
fn detects_proc_mount_operation_not_permitted_failure() {
let stderr = "bwrap: Can't mount proc on /newroot/proc: Operation not permitted";
assert!(is_proc_mount_failure(stderr));
}源码位置:codex-rs/linux-sandbox/src/linux_run_main_tests.rs :: ignores_non_proc_mount_errors
#[test]
fn ignores_non_proc_mount_errors() {
let stderr = "bwrap: Can't bind mount /dev/null: Operation not permitted";
assert!(!is_proc_mount_failure(stderr));
}源码位置:codex-rs/linux-sandbox/src/linux_run_main_tests.rs :: proc_mount_preflight_does_not_bind_the_full_filesystem
#[test]
fn proc_mount_preflight_does_not_bind_the_full_filesystem() {
let argv = build_preflight_bwrap_argv(BwrapNetworkMode::FullAccess)
.expect("build preflight argv")
.args;
assert!(argv.windows(2).any(|window| window == ["--tmpfs", "/"]));
assert!(argv.windows(2).any(|window| window == ["--proc", "/proc"]));
assert!(!argv.windows(3).any(|window| window == ["--ro-bind", "/", "/"]));
}这些断言能证明“哪些 stderr 允许关闭 proc”“预检不会把全盘暴露给 probe”以及“timeout 不等价于 user namespace 失败”。它们不能证明当前宿主一定能创建 namespace;Linux-only 的执行、Seccomp、capability 和 signal 测试仍需 Linux target 及相应内核配置。
在源码仓库中可按下面的粒度运行相关测试:
cd codex-rs
cargo test -p codex-sandboxing bwrap -- --test-threads=1
cargo test -p codex-linux-sandbox linux_run_main -- --test-threads=18. 阅读闭环
阅读顺序建议是:先看 run_main 的默认/legacy 分叉,再看 PATH 排除和 warning,再进入 system/bundled launcher 的能力验证,最后追 /proc 预检和错误消费者。读完后应能回答三个问题:为什么 bundled bwrap 是“候选执行器”而不是无条件 fallback;为什么 proc 失败只影响 fresh proc mount;为什么构造错误与 warning 的用户可见结果不同。
下一篇进入 Windows sandbox 的 token 与可读路径授权。
