From 066fd65d0b8c8aa0a45862dd7dabefede0763546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 24 Aug 2026 00:49:59 +0200 Subject: [PATCH] logical_disk: fix BitLocker shell property handling (#2482) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/headers/shell32/shell32.go | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/internal/headers/shell32/shell32.go b/internal/headers/shell32/shell32.go index ce60b5c3..17c14769 100644 --- a/internal/headers/shell32/shell32.go +++ b/internal/headers/shell32/shell32.go @@ -52,8 +52,13 @@ func SHCreateItemFromParsingName(path string) (*IShellItem2, error) { uintptr(unsafe.Pointer(iidIShellItem2)), uintptr(unsafe.Pointer(&result)), ) - if hr != 0 { - return nil, fmt.Errorf("syscall failed: %w", err) + + // procSHCreateItemFromParsingName.Call returns an HRESULT as its first value. + // Only the sign bit indicates failure (the FAILED macro); success codes such + // as S_OK (0) and S_FALSE (1) must not be treated as errors. The HRESULT, not + // GetLastError, carries the COM error information. + if int32(hr) < 0 { + return nil, fmt.Errorf("SHCreateItemFromParsingName failed: %w", ole.NewError(hr)) } if result == nil { @@ -64,15 +69,27 @@ func SHCreateItemFromParsingName(path string) (*IShellItem2, error) { } func (item *IShellItem2) GetProperty(key *propsys.PROPERTYKEY, v *ole.VARIANT) error { - hr, _, err := syscall.SyscallN( + hr, _, _ := syscall.SyscallN( item.lpVtbl.GetProperty, uintptr(unsafe.Pointer(item)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(v)), ) - if hr != 0 { - return fmt.Errorf("GetProperty failed: %w", err) + // GetProperty is a COM method whose return value is an HRESULT. Only the sign + // bit indicates failure (the FAILED macro); success codes such as S_OK (0) + // and S_FALSE (1) must not be treated as errors. The third return value of + // SyscallN is GetLastError, which COM methods do not set, so it would report + // stale, unrelated errors (e.g. "Too many posts were made to a semaphore"). + if int32(hr) < 0 { + return fmt.Errorf("GetProperty failed: %w", ole.NewError(hr)) + } + + // A successful property lookup may still return VT_EMPTY when the Shell + // property is unavailable. Keep that distinct from a real numeric zero, + // which the BitLocker collector maps to the "disabled" state. + if v.VT == ole.VT_EMPTY { + v.Val = -1 } return nil