denoland/deno · error
argv contains NUL
Error message
argv contains NUL
What it means
Same `flatten()` conversion for posix_spawn, applied to each argument in `cmd.get_args()`: any argv entry containing an interior NUL byte makes `CString::new` fail, mapped to `io::ErrorKind::InvalidInput` ("argv contains NUL"). C argv entries are NUL-terminated strings, so a 0x00 inside an argument is unrepresentable.
Source
Thrown at cli/tools/desktop.rs:5737
/// Flattened `(program, argv, envp, cwd)` for `posix_spawn`.
type SpawnArgs = (CString, Vec<CString>, Vec<CString>, Option<CString>);
/// Convert a std::process::Command into the argv/envp/cwd tuple posix_spawn
/// needs. Inherits the parent's env, then applies Command::env() overrides
/// (matching what std::process::Command does internally).
fn flatten(cmd: &std::process::Command) -> std::io::Result<SpawnArgs> {
let program = CString::new(cmd.get_program().as_bytes()).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"program path contains NUL",
)
})?;
let mut argv: Vec<CString> = Vec::with_capacity(cmd.get_args().len() + 1);
argv.push(program.clone());
for a in cmd.get_args() {
argv.push(CString::new(a.as_bytes()).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"argv contains NUL",
)
})?);
}
let mut env_map: std::collections::BTreeMap<OsString, OsString> =
std::env::vars_os().collect();
for (k, v) in cmd.get_envs() {
match v {
Some(v) => {
env_map.insert(k.to_os_string(), v.to_os_string());
}
None => {
env_map.remove(k);
}
}
}
let envp: Vec<CString> = env_mapView on GitHub (pinned to f7822238ca)
Solutions
- Run every externally sourced argument through a NUL check before adding it to the Command.
- Reject (preferred) or strip 0x00 at the input boundary — argument values containing NUL are almost always corrupt input.
- Add a unit test that feeds control bytes into your spawn wrapper to prove it fails closed.
Example fix
// before
for arg in user_args { cmd.arg(arg); }
// after
fn reject_nul(bytes: &[u8], what: &str) -> std::io::Result<()> {
if bytes.contains(&0) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("{what} contains NUL"),
));
}
Ok(())
}
for arg in &user_args {
reject_nul(arg.as_bytes(), "argv")?;
cmd.arg(arg);
} Defensive patterns
Strategy: validation
Validate before calling
fn reject_nul(bytes: &[u8], what: &str) -> std::io::Result<()> {
if bytes.contains(&0) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("{what} contains NUL"),
));
}
Ok(())
}
for a in cmd.get_args() {
reject_nul(a.as_bytes(), "argv")?;
} Type guard
fn is_nul_free(s: &std::ffi::OsStr) -> bool {
!s.as_bytes().contains(&0)
} Try / catch
match spawn(cmd) {
Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {
// identify which argument had 0x00 and reject the request
}
other => other,
} Prevention
- Validate externally sourced arguments before adding them to Command
- Encode binary payloads (base64/hex) instead of passing raw bytes as argv
- Fail closed on control bytes in user-supplied command data
When it happens
Trigger: A `Command::arg(...)` value containing 0x00 — commonly a string built from user input, a network payload, or a mis-decoded buffer — reaching the posix_spawn flattening path.
Common situations: CLI wrappers forwarding web/form input as arguments; data pipelines where a length-prefixed buffer was decoded as a plain string leaving trailing NULs; test fixtures with embedded control bytes.
Related errors
- program path contains NUL
- env contains NUL
- cwd has NUL
- nul byte found in provided data
- nul byte found in provided data
AI-assisted analysis of denoland/deno@f7822238ca (2026-08-20).
Data as JSON: /api/errors/d3e26d260deb1ec5.
Report an issue: GitHub.