Skip to content

bwrap sandbox bind fails with symlinked TMPDIR #14672

@tomstitt

Description

@tomstitt

What version of Codex CLI is running?

main

What subscription do you have?

API User (Azure)

Which model were you using?

No response

What platform is your computer?

No response

What terminal emulator and version are you using (if applicable)?

No response

What issue are you seeing?

With the linux bwrap sandbox on systems where TMPDIR is a symlink, bwrap fails to actually bind TMPDIR when it's a writable root. This causes downstream errors when codex tries to do anything with TMPDIR resulting in (usually unnecessary) requests for elevated permissions.

What steps can reproduce the bug?

For example, mktemp fails because TMPDIR isn't actually bound in:

> codex sandbox linux --full-auto mktemp
bwrap: Can't mkdir {TMPDIR}: No such file or directory

What is the expected behavior?

With a patched codex, that resolves directories before --bind-ing, it works:

>  codex-patched sandbox linux --full-auto mktemp
{TMPDIR}/tmp.BzOpNFXRw7

Or, with TMPDIR as an absolute path which is probably most common, it works:

> TMPDIR={...} codex sandbox linux --full-auto mktemp
{...}/tmp.OPmu0IajVm

Additional information

Here's my patch:

diff --git a/codex-rs/linux-sandbox/src/bwrap.rs b/codex-rs/linux-sandbox/src/bwrap.rs
index e93bb80f1..2980717d5 100644
--- a/codex-rs/linux-sandbox/src/bwrap.rs
+++ b/codex-rs/linux-sandbox/src/bwrap.rs
@@ -11,6 +11,7 @@
 //! - bubblewrap used to construct the filesystem view before exec.
 use std::collections::BTreeSet;
 use std::collections::HashSet;
+use std::fs;
 use std::fs::File;
 use std::os::fd::AsRawFd;
 use std::path::Path;
@@ -300,7 +301,9 @@ fn create_filesystem_args(
     }

     for writable_root in &sorted_writable_roots {
-        let root = writable_root.root.as_path();
+        let relative_root = writable_root.root.as_path();
+        let real_root = fs::canonicalize(relative_root)?;
+        let root = real_root.as_path();
         // If a denied ancestor was already masked, recreate any missing mount
         // target parents before binding the narrower writable descendant.
         if let Some(masking_root) = unreadable_roots

Also just want to say thank you for adding a bubblewrap-based sandbox!! Landlock was causing some headaches

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLIbugSomething isn't workingsandboxIssues related to permissions or sandboxing

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions