{"record":{"id":"a63ca385a0548574","repo":"astrid-runtime/astrid","slug":"winfsp-service-parent-start-identity-is-invalid","errorCode":null,"errorMessage":"WinFsp service parent start identity is invalid","messagePattern":"WinFsp service parent start identity is invalid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-provider-winfsp/src/win.rs","lineNumber":252,"sourceCode":"}\n\nfn validate_service_launch(launch: &StorageProviderServiceLaunchV1) -> Result<()> {\n    if launch.schema != STORAGE_FILESYSTEM_SERVICE_LAUNCH_SCHEMA_V1 {\n        bail!(\"unsupported WinFsp service launch schema {}\", launch.schema);\n    }\n    if launch.parent.pid <= 1 || launch.parent.pid == std::process::id() {\n        bail!(\"WinFsp service parent PID is invalid\");\n    }\n    if launch.parent.token.len() < 16\n        || launch.parent.token.len() > 512\n        || launch.parent.token.chars().any(char::is_control)\n    {\n        bail!(\"WinFsp service parent token is invalid\");\n    }\n    if let Some(identity) = launch.parent.start_identity.as_deref()\n        && (identity.is_empty() || identity.len() > 512 || identity.chars().any(char::is_control))\n    {\n        bail!(\"WinFsp service parent start identity is invalid\");\n    }\n    if launch.parent.start_identity.is_none() {\n        bail!(\"WinFsp service parent start identity is required on Windows\");\n    }\n    let lease = &launch.lease;\n    let now = SystemTime::now()\n        .duration_since(UNIX_EPOCH)\n        .context(\"read system clock\")?\n        .as_secs();\n    if lease.expires_at_epoch_secs < now {\n        bail!(\"WinFsp lease is expired\");\n    }\n    if lease.lease_token.len() < 16 || lease.lease_token.len() > 4096 {\n        bail!(\"WinFsp lease callback token is invalid\");\n    }\n    if !lease.resource_path.is_absolute()\n        || !lease.callback_path.is_absolute()\n        || lease.callback_path != lease.resource_path.join(\"control.endpoint\")","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-provider-winfsp/src/win.rs#L234-L270","documentation":"The WinFsp mount service validates that the launching parent supplied a start_identity string. This error fires when start_identity is present (Some) but fails sanity checks: it is empty, longer than 512 bytes, or contains Unicode control characters. The library rejects it before starting the filesystem because the identity is used for service-attribution/audit and an unbounded or control-character-laden value is treated as a malformed or hostile launch descriptor.","triggerScenarios":"service_main -> validate_service_launch receives a StorageProviderServiceLaunchV1 whose launch.parent.start_identity is Some(s) where s.is_empty(), s.len() > 512, or s.chars().any(char::is_control). I.e., the parent process serialized a non-empty but invalid identity string into the launch descriptor.","commonSituations":"A launcher passes \"\" (empty string) instead of None when the identity is unknown; a config/registry field for the mount identity accumulates whitespace/newlines or other control chars; a truncated or concatenated identity from an IPC buffer exceeds 512 chars; templates that interpolate an account name into the identity pick up stray \\0 or \\r\\n.","solutions":["In the launching parent, set start_identity to a non-empty, trimmed string no longer than 512 chars; if there is no identity, serialize None rather than an empty string.","Strip control characters and trim the identity before building the launch descriptor (retain char::is_control filter).","Log/inspect the exact identity bytes being written to the launch file to find where control characters or overlong values originate.","If the identity legitimately needs more room, shorten it (e.g. store an ID/reference, not a full description) since 512 is a hard limit in this validator."],"exampleFix":"// before\nparent.start_identity = Some(format!(\"{}\\n{}\", user, host));\n// after\nlet identity: String = format!(\"{} {}\", user, host).chars().filter(|c| !c.is_control()).collect();\nparent.start_identity = if identity.is_empty() || identity.len() > 512 { None } else { Some(identity) };","handlingStrategy":"validation","validationCode":"fn valid_start_identity(s: &Option<String>) -> bool {\n    match s {\n        Some(s) => !s.is_empty() && s.len() <= 512 && !s.chars().any(char::is_control),\n        None => false,\n    }\n}\nassert!(valid_start_identity(&launch.parent.start_identity), \"invalid start_identity\");","typeGuard":"fn is_ok_identity(s: Option<&str>) -> bool {\n    matches!(s, Some(v) if !v.is_empty() && v.len() <= 512 && !v.chars().any(char::is_control))\n}","tryCatchPattern":null,"preventionTips":["Trim and filter control characters from identity strings at config load time","Never serialize an empty string where None is meant","Enforce the 512-char limit in the launcher with a unit test","Log the identity length/content type (not value) when building launch descriptors"],"tags":["winfsp","windows","validation","ipc"],"backgroundTag":"invalid-argument-value","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}