denoland/deno · error
cwd has NUL
Error message
cwd has NUL
What it means
The last NUL check in `flatten()`: when `Command::get_current_dir()` is set, the cwd path must also convert to a C string for `posix_chdir` inside spawn. An embedded 0x00 makes `CString::new` fail and the spawn is rejected with `io::ErrorKind::InvalidInput` ("cwd has NUL").
Source
Thrown at cli/tools/desktop.rs:5774
.into_iter()
.map(|(k, v)| {
let mut s =
Vec::with_capacity(k.as_bytes().len() + 1 + v.as_bytes().len());
s.extend_from_slice(k.as_bytes());
s.push(b'=');
s.extend_from_slice(v.as_bytes());
CString::new(s).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"env contains NUL",
)
})
})
.collect::<std::io::Result<_>>()?;
let cwd = match cmd.get_current_dir() {
Some(p) => {
Some(CString::new(p.as_os_str().as_bytes()).map_err(|_| {
std::io::Error::new(std::io::ErrorKind::InvalidInput, "cwd has NUL")
})?)
}
None => None,
};
Ok((program, argv, envp, cwd))
}
pub fn spawn(cmd: &std::process::Command) -> std::io::Result<Child> {
let (program, argv, envp, cwd) = flatten(cmd)?;
let mut argv_ptrs: Vec<*mut libc::c_char> =
argv.iter().map(|c| c.as_ptr() as *mut _).collect();
argv_ptrs.push(std::ptr::null_mut());
let mut envp_ptrs: Vec<*mut libc::c_char> =
envp.iter().map(|c| c.as_ptr() as *mut _).collect();
envp_ptrs.push(std::ptr::null_mut());
// SAFETY: posix_spawn FFI. We initialize attrs/actions before use,
// destroy them on every exit path, and keep argv/envp CString backingView on GitHub (pinned to f7822238ca)
Solutions
- NUL-check the cwd path before calling `current_dir`, same as for the program path.
- Reject directory names with control bytes at creation time so they never become cwd candidates.
- If the cwd comes from untrusted archive metadata, canonicalize and validate it is a real directory first.
Example fix
// before
let mut cmd = std::process::Command::new(prog);
cmd.current_dir(&user_dir); // user_dir contains 0x00
// after
if user_dir.as_os_str().as_bytes().contains(&0) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"cwd has NUL",
));
}
let mut cmd = std::process::Command::new(prog);
cmd.current_dir(&user_dir); Defensive patterns
Strategy: validation
Validate before calling
if let Some(cwd) = cmd.get_current_dir() {
if cwd.as_os_str().as_bytes().contains(&0) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"cwd has NUL",
));
}
} 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 => {
// cwd was invalid — fall back to spawning in the current directory
}
other => other,
} Prevention
- Validate user-supplied directory names at creation time
- Canonicalize cwd candidates with std::fs::canonicalize before use
- Reject control bytes in extracted archive entry names used as directories
When it happens
Trigger: Calling `Command::current_dir(p)` where `p` contains a NUL byte — user-controlled directory names, decoded buffers, or path joins with corrupt components — before the posix_spawn call.
Common situations: Sandbox/workspace tools that chdir into user-named directories; zip/tar extraction tools where entry names feed cwd; string truncation bugs producing trailing NULs.
Related errors
- program path contains NUL
- argv contains NUL
- env contains NUL
- nul byte found in provided data
- Windows file names may not contain `"` or end with `\`
AI-assisted analysis of denoland/deno@f7822238ca (2026-08-20).
Data as JSON: /api/errors/787d4bd770afb9d8.
Report an issue: GitHub.