astrid-runtime/astrid · info

mount source has NUL

Error message

mount source has NUL

What it means

mount_overlay converts its mount(2) arguments to CStrings before the FFI call. The literal source "overlay" is a constant and can never contain NUL, so this arm is a defensive invariant check that fires only if the constant were changed; the library fails closed with InvalidInput instead of passing a bad pointer to mount(2).

Solutions

  1. Leave as-is; this is a defensive check on a compile-time constant
  2. If it ever fires, inspect recent edits to the "overlay" literal for accidental NUL injection
Defensive patterns

Strategy: validation

Validate before calling

// Unreachable for the hard-coded "overlay" literal; no caller-side check needed.

Try / catch

// On InvalidInput from mount_overlay, treat as an internal bug and log/report it.

Prevention

When it happens

Trigger: Practically unreachable: the source string is the hard-coded literal "overlay". It would only fire if the literal were edited to a value containing an embedded NUL byte.

Common situations: Reviewers/maintainers modifying the constant; static-analysis tooling flagging the unreachable error path.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/e035eefa3f9701e4. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-vfs/src/workspace_cow/overlayfs.rs:494

        Some(libc::ENODATA | libc::ENOTSUP) => Ok(false),
        _ => Err(err),
    }
}

/// 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(),
        )
    };

View on GitHub (pinned to affd8760f4)