{"record":{"id":"a0d583860163bd5c","repo":"astrid-runtime/astrid","slug":"named-pipe-has-a-null-or-missing-dacl","errorCode":null,"errorMessage":"named-pipe has a null or missing DACL","messagePattern":"named-pipe has a null or missing DACL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/local_transport/windows.rs","lineNumber":923,"sourceCode":"    if descriptor.is_null() {\n        return Err(io::Error::other(\n            \"Windows returned no named-pipe security descriptor\",\n        ));\n    }\n    let descriptor_allocation = LocalAllocation(descriptor);\n\n    // SAFETY: GetSecurityInfo returned this non-null descriptor, and\n    // `descriptor_allocation` keeps it live through validation.\n    unsafe { validate_descriptor_control(descriptor) }?;\n\n    if owner.is_null() || unsafe { EqualSid(owner, current.as_psid()) } == 0 {\n        return Err(io::Error::new(\n            io::ErrorKind::PermissionDenied,\n            \"named-pipe owner is not the current operating-system user\",\n        ));\n    }\n    if dacl.is_null() {\n        return Err(io::Error::new(\n            io::ErrorKind::PermissionDenied,\n            \"named-pipe has a null or missing DACL\",\n        ));\n    }\n\n    // SAFETY: `dacl` points into the descriptor allocation returned by\n    // GetSecurityInfo, which remains live and unmodified through\n    // `descriptor_allocation`. The parser validates and bounds the ACL before\n    // exposing any borrowed ACE or SID.\n    let dacl = unsafe {\n        ValidatedAcl::from_raw(\n            dacl,\n            &descriptor_allocation,\n            \"named-pipe security descriptor\",\n        )\n    }?;\n    let expected_aces = if current.equals(&system) { 1 } else { 2 };\n    let ace_count = dacl.ace_count();","sourceCodeStart":905,"sourceCodeEnd":941,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/local_transport/windows.rs#L905-L941","documentation":"validate_pipe_security (used by connect and accept) fetched the pipe's security descriptor and found the DACL pointer is null — the pipe has no discretionary access-control list. The library requires an explicit DACL so it can prove only the current user and Local System can access the pipe; a null DACL means everyone could be allowed access, so it fails closed with PermissionDenied.","triggerScenarios":"connect()/accept() on a pipe whose SECURITY_DESCRIPTOR has SE_DACL_PRESENT unset or a null ACL pointer, typically because the pipe was created with a NULL/no security attributes or SECURITY_DACL_PRESENT omitted.","commonSituations":"A third-party program or older library version created the pipe with default/null security attributes; someone recreated the pipe manually with `CreateNamedPipe` passing lpSecurityAttributes = NULL with permissive defaults; the pipe at the expected path is a different, foreign pipe.","solutions":["Recreate the pipe using this library's creation API, which installs an explicit protected DACL for the current user and Local System.","Inspect the existing pipe's ACL (`Get-Acl \\\\.\\pipe\\<name>`) to identify who created it, then stop that process or use a different pipe name.","If you create the pipe yourself with raw CreateNamedPipe, pass a SECURITY_ATTRIBUTES with an explicit DACL (SDDL like `D:P(A;;GA;;;current-sid)(A;;GA;;;SY)`) instead of NULL.","Confirm no other tool or wrapper replaced the pipe at that name."],"exampleFix":"// before: creating the pipe without security attributes\nCreateNamedPipeW(path, ..., std::ptr::null_mut(), ...);\n// after: supply SECURITY_ATTRIBUTES with a protected DACL\nlet sa = SECURITY_ATTRIBUTES { nLength: size_of::<SECURITY_ATTRIBUTES>() as u32, lpSecurityDescriptor: build_protected_dacl_descriptor(), bInheritHandle: 0 };\nCreateNamedPipeW(path, ..., &sa, ...);","handlingStrategy":"validation","validationCode":"// Preflight: ensure the pipe has an explicit DACL before connecting\n// powershell: if (-not (Get-Acl \\\\.\\pipe\\myapp).AreAccessRulesProtected) { ... }\n// or in Rust, call GetNamedPipeHandleState/GetSecurityInfo yourself and check dacl.is_some()","typeGuard":null,"tryCatchPattern":"match connect() {\n    Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied && e.to_string().contains(\"null or missing DACL\") => {\n        eprintln!(\"pipe has no DACL; it was not created by this library — recreate it\");\n    }\n    r => r?,\n}","preventionTips":["Always create the pipe through the library's own creation API","Never pass NULL SECURITY_ATTRIBUTES when creating pipes with raw CreateNamedPipe","Verify pipe ACLs with Get-Acl when connecting to externally created pipes","Use unique pipe names so a foreign process cannot replace your endpoint"],"tags":["windows","named-pipes","security","acl","ipc"],"backgroundTag":"permission-denied","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"}