astrid-runtime/astrid · error
mount target has NUL
Error message
mount target has NUL
What it means
mount_overlay converts the mount target directory path to a CString for mount(2). Paths on Unix may contain any byte except NUL; if the target path contains an embedded NUL byte, CString::new fails and the function returns InvalidInput 'mount target has NUL' instead of calling mount with a truncated path.
Solutions
- Check where the target Path is built and strip/reject interior NUL bytes
- Validate the target with target.as_os_str().as_bytes().contains(&0) before calling mount
- Ensure path buffers are truncated at the first NUL before conversion to Path
- Use only paths from Rust string APIs (fs APIs, clap, env), which cannot contain NUL
Example fix
// before mount_overlay(&Path::from_raw_bytes(buf), &data)?; // after let bytes = buf.split(|b| *b == 0).next().unwrap(); let target = Path::new(std::str::from_utf8(bytes)?); mount_overlay(target, &data)?;
Defensive patterns
Strategy: validation
Validate before calling
fn assert_nul_free(p: &Path) -> io::Result<()> { if p.as_os_str().as_bytes().contains(&0) { Err(io::Error::new(io::ErrorKind::InvalidInput, "mount target contains NUL")) } else { Ok(()) } } Type guard
fn nul_free_path(p: &Path) -> bool { !p.as_os_str().as_bytes().contains(&0) } Try / catch
if let Err(e) = mount(target, &layers) { if e.kind() == io::ErrorKind::InvalidInput { /* reject the corrupted target path */ } return Err(e); } Prevention
- Source paths from Rust std APIs, which forbid interior NULs
- Truncate byte buffers at the first NUL before converting to Path
- Validate paths at the configuration boundary
When it happens
Trigger: Calling mount (→ mount_overlay) with a target directory Path whose OsStr bytes include an interior '\0' — e.g. a path assembled from raw byte buffers or FFI-originated data.
Common situations: Paths constructed from C-FFI data or binary protocols; a bug where a path buffer was not trimmed at its terminator, keeping trailing/garbage NUL bytes.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- mount data has NUL
- umount target has NUL
- cannot unmount a relative mountpoint
- clone dest path has NUL
- clone source path has NUL
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/0fcfd27410f894c7.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-vfs/src/workspace_cow/overlayfs.rs:498
/// A short, deterministic hex digest of a path, used only as a directory name.
fn path_hash(path: &Path) -> String {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let key = std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf());
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
format!("{:016x}", hasher.finish())
}
/// `mount("overlay", target, "overlay", 0, data)`.
fn mount_overlay(target: &Path, data: &str) -> io::Result<()> {
let src = CString::new("overlay")
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "mount source has NUL"))?;
let fstype = CString::new("overlay")
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "mount fstype has NUL"))?;
let target_c = CString::new(target.as_os_str().as_bytes())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "mount target has NUL"))?;
let data_c = CString::new(data)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "mount data has NUL"))?;
// SAFETY: all four pointers are valid, NUL-terminated C strings that outlive
// the call; `mount` reads them and returns a status code, retaining no
// pointers. `data` is the overlayfs option string.
let rc = unsafe {
libc::mount(
src.as_ptr(),
target_c.as_ptr(),
fstype.as_ptr(),
0,
data_c.as_ptr().cast(),
)
};
if rc != 0 {
return Err(io::Error::last_os_error());
}
Ok(())View on GitHub (pinned to affd8760f4)