{"record":{"id":"99248c7c5d86d5e6","repo":"astrid-runtime/astrid","slug":"winfsp-launch-lease-does-not-match-the-kernel-mani","errorCode":null,"errorMessage":"WinFsp launch lease does not match the kernel manifest","messagePattern":"WinFsp launch lease does not match the kernel manifest","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-provider-winfsp/src/win.rs","lineNumber":288,"sourceCode":"        || lease.callback_path != lease.resource_path.join(\"control.endpoint\")\n    {\n        bail!(\"WinFsp lease paths are malformed\");\n    }\n    platform_fs::validate_private_directory(&lease.resource_path)\n        .context(\"validate private WinFsp lease resource\")?;\n    platform_fs::verify_no_redirects(&lease.resource_path)\n        .context(\"reject redirected WinFsp lease resource\")?;\n    let manifest_path = lease.resource_path.join(\"lease.json\");\n    platform_fs::validate_private_file(&manifest_path)\n        .context(\"validate private WinFsp lease manifest\")?;\n    let manifest = std::fs::read(&manifest_path).context(\"read WinFsp lease manifest\")?;\n    if manifest.len() > 64 * 1024 {\n        bail!(\"WinFsp lease manifest exceeds the bounded size\");\n    }\n    let admitted: StorageMountLeaseV1 =\n        serde_json::from_slice(&manifest).context(\"decode WinFsp lease manifest\")?;\n    if admitted != *lease {\n        bail!(\"WinFsp launch lease does not match the kernel manifest\");\n    }\n    if !launch.mountpoint.is_absolute()\n        || launch\n            .mountpoint\n            .components()\n            .any(|component| matches!(component, std::path::Component::ParentDir))\n    {\n        bail!(\"WinFsp service mountpoint is malformed\");\n    }\n    if is_public_mountpoint(&launch.mountpoint)\n        || launch.mountpoint.parent().is_none()\n        || launch.mountpoint == lease.resource_path\n        || launch.mountpoint.starts_with(&lease.resource_path)\n        || lease.resource_path.starts_with(&launch.mountpoint)\n    {\n        bail!(\"WinFsp service mountpoint is public or overlaps the lease resource\");\n    }\n    platform_fs::validate_private_directory(&launch.mountpoint)","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-provider-winfsp/src/win.rs#L270-L306","documentation":"This is an anti-tampering check: the lease manifest persisted on disk (lease.json, decoded into StorageMountLeaseV1) must exactly equal the lease embedded in the launch descriptor passed to the service. If they differ, the launch is aborted because the parent is presenting a lease that was not the one admitted to the private resource directory — indicating stale files, concurrent re-issues, or attempted privilege escalation.","triggerScenarios":"service_main -> validate_service_launch where serde_json parses lease.json successfully but admitted != *launch.lease — any differing field (mount_id, lease_token, paths, access, expires_at_epoch_secs, schema) triggers it.","commonSituations":"Reusing a launch descriptor from a previous mount while a new lease.json was issued for the same directory; two mounts sharing/overwriting the same resource_path; the issuer renewed the lease (new expiry/token) after the launch descriptor snapshot was taken; hand-editing one side but not the other; comparing structurally-equal but textually different JSON is fine (serde equality), so a mismatch is always a real field difference.","solutions":["Regenerate the launch descriptor and the lease.json together in one atomic step from the same StorageMountLeaseV1 value.","Ensure each mount uses a unique resource_path so concurrent leases never overwrite each other's lease.json.","If the lease was renewed, re-issue the launch with the updated lease rather than launching with the pre-renewal snapshot.","Verify the issuer writes lease.json after finalizing all fields (token, expiry) so the on-disk manifest matches what goes into the descriptor."],"exampleFix":"// before\nwrite_lease_json(&dir, &old_lease);\nspawn_service(launch_with(new_lease));\n// after\nwrite_lease_json(&dir, &lease);\nspawn_service(launch_with(lease.clone()));","handlingStrategy":"validation","validationCode":"let on_disk: StorageMountLeaseV1 = serde_json::from_slice(&std::fs::read(resource_path.join(\"lease.json\"))?)?;\nif on_disk != launch.lease {\n    return Err(anyhow!(\"launch lease differs from on-disk lease.json — regenerate both together\"));\n}","typeGuard":null,"tryCatchPattern":"match validate_launch(&launch) {\n    Err(e) if e.to_string().contains(\"does not match the kernel manifest\") => {\n        let fresh = reissue_lease_for(&launch.lease.mount_id)?;\n        start_with(fresh)\n    }\n    other => other.map(|_| ()),\n}","preventionTips":["Write lease.json and build the launch descriptor from the same cloned value in one atomic step","Never reuse launch descriptors across mounts or after lease renewal","Give every mount a unique resource_path to avoid cross-write races","Compare with struct equality (PartialEq) in tests after round-tripping the manifest"],"tags":["winfsp","lease","tampering","consistency"],"backgroundTag":"checksum-mismatch","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}