astrid-runtime/astrid · error
umount target has NUL
Error message
umount target has NUL
What it means
umount converts the mountpoint path to a CString for umount2(2) with MNT_DETACH. If the target path contains an embedded NUL byte, CString::new fails and the function returns InvalidInput 'umount target has NUL' rather than calling the syscall with a truncated path.
Solutions
- Validate the stored mountpoint for NUL bytes before calling unmount_path
- Ensure paths are truncated at the first NUL when converted from byte buffers
- Prefer paths sourced from Rust std APIs, which forbid interior NULs
- Fix the record-keeping that saved the corrupted mountpoint path
Example fix
// before let mp = Path::new(std::ffi::OsStr::from_bytes(&padded_buf)); unmount_path(mp)?; // after let trimmed: &[u8] = padded_buf.split(|b| *b == 0).next().unwrap(); let mp = Path::new(std::ffi::OsStr::from_bytes(trimmed)); unmount_path(mp)?;
Defensive patterns
Strategy: validation
Validate before calling
fn assert_umount_target(p: &Path) -> io::Result<()> { if p.as_os_str().as_bytes().contains(&0) { Err(io::Error::new(io::ErrorKind::InvalidInput, "mountpoint contains NUL")) } else { Ok(()) } } Type guard
fn mountpoint_ok(p: &Path) -> bool { !p.as_os_str().as_bytes().contains(&0) } Try / catch
if let Err(e) = unmount_path(mp) { if e.kind() == io::ErrorKind::InvalidInput { /* repair/re-derive the stored mountpoint */ } return Err(e.into()); } Prevention
- Store mountpoints as PathBuf from Rust APIs, not raw byte arrays
- Trim byte buffers at the first NUL before path conversion
- Re-derive mountpoints from the mount table instead of persisted blobs when possible
When it happens
Trigger: Calling unmount_path (→ umount) with a mountpoint Path whose OsStr bytes contain an interior '\0', typically from untrimmed byte buffers or FFI-derived data.
Common situations: Mountpoint paths reconstructed from binary IPC or config blobs; bookkeeping bugs where a stored path kept trailing NUL padding.
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 target has NUL
- clone dest path has NUL
- clone source path has NUL
- mount data has NUL
- private directory has no Unix root
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/dfd5543db40c949b.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-vfs/src/workspace_cow/overlayfs.rs:523
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(())
}
/// `umount2(target, MNT_DETACH)` — lazy unmount so a busy mountpoint still
/// detaches.
fn umount(target: &Path) -> io::Result<()> {
let target_c = CString::new(target.as_os_str().as_bytes())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "umount target has NUL"))?;
// SAFETY: `target_c` is a valid, NUL-terminated C string outliving the call.
let rc = unsafe { libc::umount2(target_c.as_ptr(), libc::MNT_DETACH) };
if rc != 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
View on GitHub (pinned to affd8760f4)