crowdsecurity/crowdsec · error

while getting ACE: %w

Error message

while getting ACE: %w

What it means

CheckPerms walks the plugin's ACL entries by calling the Win32 GetAce API via advapi32.dll (procGetAce.Call). If GetAce returns 0 (failure), this error wraps windows.GetLastError(), meaning the i-th ACE could not be retrieved from the DACL. Causes include an index beyond aceCount, a corrupt ACL, or reflection-derived aceCount mismatching the real ACL state (e.g. the ACL changed concurrently).

Source

Thrown at pkg/csplugin/utils_windows.go:129

	/*
			For reference, the structure of the ACL type is:
			type ACL struct {
			aclRevision byte
			sbz1        byte
			aclSize     uint16
			aceCount    uint16
			sbz2        uint16
		}
		As the field are not exported, we have to use reflection to access them, this should not be an issue as the structure won't (probably) change any time soon.
	*/
	aceCount := rs.Field(3).Uint()

	for i := range aceCount {
		ace := &AccessAllowedAce{}
		ret, _, _ := procGetAce.Call(uintptr(unsafe.Pointer(dacl)), uintptr(i), uintptr(unsafe.Pointer(&ace)))
		if ret == 0 {
			return fmt.Errorf("while getting ACE: %w", windows.GetLastError())
		}
		log.Debugf("ACE %d: %+v\n", i, ace)

		if ace.AceType == ACCESS_DENIED_ACE_TYPE {
			continue
		}
		aceSid := (*windows.SID)(unsafe.Pointer(&ace.SidStart))

		if aceSid.Equals(systemSid) || aceSid.Equals(adminSid) {
			log.Debugf("Not checking permission for well-known SID %s", aceSid.String())
			continue
		}

		if aceSid.Equals(currentUserSid) {
			log.Debugf("Not checking permission for current user %s", currentUser.Username)
			continue
		}

View on GitHub (pinned to 909b515798)

Solutions

  1. Re-run crowdsec once ACL churn settles — a transient race often resolves on retry
  2. Reset and re-apply a stable ACL: `icacls <plugin> /reset` then `icacls <plugin> /grant ...`
  3. Exclude the crowdsec plugin directory from antivirus ACL-rewriting policies
  4. Read the wrapped GetLastError() code to confirm the specific Win32 failure

Example fix

// before
ret, _, _ := procGetAce.Call(uintptr(unsafe.Pointer(dacl)), uintptr(i), uintptr(unsafe.Pointer(&ace)))
if ret == 0 {
	return fmt.Errorf("while getting ACE: %w", windows.GetLastError())
}
// after
ret, _, _ := procGetAce.Call(uintptr(unsafe.Pointer(dacl)), uintptr(i), uintptr(unsafe.Pointer(&ace)))
if ret == 0 {
	return fmt.Errorf("while getting ACE %d of %d for %s: %w", i, aceCount, path, windows.GetLastError())
}
Defensive patterns

Strategy: retry

Validate before calling

// ensure ACL size fields are consistent before enumeration
sd, err := windows.GetNamedSecurityInfo(path, windows.SE_FILE_OBJECT, windows.DACL_SECURITY_INFORMATION)
if err != nil || !sd.IsValid() {
	return errors.New("DACL not reliably readable")
}

Try / catch

var lastErr error
for attempt := 0; attempt < 3; attempt++ {
	err := CheckPerms(pluginPath)
	if err == nil {
		break
	}
	if strings.Contains(err.Error(), "while getting ACE") {
		lastErr = err
		time.Sleep(500 * time.Millisecond) // ACL may be mid-modification
		continue
	}
	return err
}
return lastErr

Prevention

When it happens

Trigger: Calling CheckPerms when procGetAce.Call fails for some index i: ACL was modified between reading aceCount and enumerating; ACL revision/size fields inconsistent with actual memory; malformed ACL on the file.

Common situations: Antivirus/EDR or group policy rewriting plugin-directory ACLs while crowdsec is enumerating them; corrupted ACLs from backup/restore tools; races when multiple processes adjust ACLs simultaneously.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/dd49780b87ac2af5. Report an issue: GitHub.