astrid-runtime/astrid · error

WinFsp service mountpoint is malformed

Error message

WinFsp service mountpoint is malformed

What it means

The mountpoint where the WinFsp filesystem will be attached must be a safe, absolute path. This error fires when the mountpoint is relative or contains a ParentDir ("..") component — i.e., the path could resolve somewhere unintended. Because a filesystem mount is a privileged operation, path-traversal-shaped mountpoints are rejected outright.

Source

Thrown at crates/astrid-storage-provider-winfsp/src/win.rs:296

    let manifest_path = lease.resource_path.join("lease.json");
    platform_fs::validate_private_file(&manifest_path)
        .context("validate private WinFsp lease manifest")?;
    let manifest = std::fs::read(&manifest_path).context("read WinFsp lease manifest")?;
    if manifest.len() > 64 * 1024 {
        bail!("WinFsp lease manifest exceeds the bounded size");
    }
    let admitted: StorageMountLeaseV1 =
        serde_json::from_slice(&manifest).context("decode WinFsp lease manifest")?;
    if admitted != *lease {
        bail!("WinFsp launch lease does not match the kernel manifest");
    }
    if !launch.mountpoint.is_absolute()
        || launch
            .mountpoint
            .components()
            .any(|component| matches!(component, std::path::Component::ParentDir))
    {
        bail!("WinFsp service mountpoint is malformed");
    }
    if is_public_mountpoint(&launch.mountpoint)
        || launch.mountpoint.parent().is_none()
        || launch.mountpoint == lease.resource_path
        || launch.mountpoint.starts_with(&lease.resource_path)
        || lease.resource_path.starts_with(&launch.mountpoint)
    {
        bail!("WinFsp service mountpoint is public or overlaps the lease resource");
    }
    platform_fs::validate_private_directory(&launch.mountpoint)
        .context("validate private WinFsp mountpoint")?;
    platform_fs::verify_no_redirects(&launch.mountpoint)
        .context("reject redirected WinFsp mountpoint")?;
    if std::fs::read_dir(&launch.mountpoint)?.next().is_some() {
        bail!("WinFsp service mountpoint is not empty");
    }
    if !launch.control_path.is_absolute()
        || launch

View on GitHub (pinned to affd8760f4)

Solutions

  1. Canonicalize the mountpoint in the launcher (fs::canonicalize / dunce::canonicalize) before embedding it in the launch descriptor.
  2. Reject or sanitize user input: ensure the mountpoint is absolute and free of ".." components at configuration time.
  3. Construct the mountpoint with PathBuf::join from a known root instead of string concatenation.
  4. If traversal is legitimate, resolve it in the trusted launcher (base.join(user_rel) then canonicalize) and pass the resolved absolute path.

Example fix

// before
mountpoint: PathBuf::from(format!("{}\\..\\mounts\\{}", base, id)),
// after
let mp = base.join("mounts").join(&id).canonicalize()?; // absolute, no ParentDir
mountpoint: mp,
Defensive patterns

Strategy: validation

Validate before calling

let mp = dunce::canonicalize(&mountpoint)?;
if !mp.is_absolute() || mp.components().any(|c| matches!(c, std::path::Component::ParentDir)) {
    return Err(anyhow!("mountpoint must be absolute with no '..' components"));
}

Type guard

fn safe_mountpoint(p: &std::path::Path) -> bool {
    p.is_absolute()
        && !p.components().any(|c| matches!(c, std::path::Component::ParentDir | std::path::Component::CurDir))
}

Prevention

When it happens

Trigger: service_main -> validate_service_launch when !launch.mountpoint.is_absolute() or mountpoint.components() contains std::path::Component::ParentDir (e.g. "..\\mount", "C:\dir\..\mount", "mnt\\..\\x").

Common situations: Config files specifying relative mount directories resolved against an unexpected CWD; user-supplied mountpoints concatenated with ".." to escape a base dir; templates that build the mountpoint by string formatting instead of PathBuf::join/canonicalize; Windows drive-relative paths like "C:mount" which are not fully absolute.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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