openai/codex · critical

failed to convert argv to CString: {err}

Error message

failed to convert argv to CString: {err}

What it means

Before exec'ing bwrap, argv_to_cstrings converts every argument to a C string. Rust Strings may contain interior NUL bytes, but C argv entries cannot, so CString::new fails on an argument holding a NUL byte and the helper panics with the NulError, which reports the offending byte position.

Source

Thrown at codex-rs/linux-sandbox/src/exec_util.rs:10

use std::ffi::CString;
use std::fs::File;
use std::os::fd::AsRawFd;

pub(crate) fn argv_to_cstrings(argv: &[String]) -> Vec<CString> {
    let mut cstrings: Vec<CString> = Vec::with_capacity(argv.len());
    for arg in argv {
        match CString::new(arg.as_str()) {
            Ok(value) => cstrings.push(value),
            Err(err) => panic!("failed to convert argv to CString: {err}"),
        }
    }
    cstrings
}

pub(crate) fn make_files_inheritable(files: &[File]) {
    for file in files {
        clear_cloexec(file.as_raw_fd());
    }
}

fn clear_cloexec(fd: libc::c_int) {
    // SAFETY: `fd` is an owned descriptor kept alive by `files`.
    let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
    if flags < 0 {
        let err = std::io::Error::last_os_error();
        panic!("failed to read fd flags for preserved bubblewrap file descriptor {fd}: {err}");
    }

View on GitHub (pinned to 339751715c)

Solutions

  1. Locate the offending argument using the NUL position in the panic message and sanitize the data at its source.
  2. Reject or strip NUL bytes in user input before constructing the command line.
  3. If the data was NUL-separated by design, split it into separate arguments instead of one embedded-NUL string.

Example fix

// before
let argv = vec![program, user_input]; // user_input with a NUL byte panics in argv_to_cstrings

// after: validate before the sandbox call
for (i, arg) in argv.iter().enumerate() {
    if arg.contains('\u{0}') {
        return Err(format!("argument {i} contains a NUL byte"));
    }
}
Defensive patterns

Strategy: validation

Validate before calling

fn args_are_cstring_safe(argv: &[String]) -> Result<(), usize> {
    argv.iter().position(|a| a.contains('\u{0}')).map_or(Ok(()), Err)
}
args_are_cstring_safe(&argv)?;

Type guard

fn is_cstring_safe(s: &str) -> bool { !s.contains('\u{0}') }

Prevention

When it happens

Trigger: Passing any argument to the sandboxed command that contains a NUL byte: a filename, env value, or flag built from unvalidated input; a marshalling bug that converts length-prefixed or NUL-separated buffers into one String; binary data leaking into argv.

Common situations: User-supplied filenames or commands containing NUL; IPC layers that join NUL-separated fields instead of splitting them; unchecked UTF-8 conversions of OS strings; fuzzing.

Related errors


AI-assisted analysis of openai/codex@339751715c (2026-08-25). Data as JSON: /api/errors/3c33edca59394f1f. Report an issue: GitHub.