Skip to content

fix: transient WMI fault no longer poisons the static-hardware cache for the session#1416

Merged
laurentiu021 merged 2 commits into
mainfrom
fix/systeminfo-cache-poison
Jul 10, 2026
Merged

fix: transient WMI fault no longer poisons the static-hardware cache for the session#1416
laurentiu021 merged 2 commits into
mainfrom
fix/systeminfo-cache-poison

Conversation

@laurentiu021

Copy link
Copy Markdown
Owner

Summary

Fixes ultra-audit finding P2 #40.

SystemInfoService caches static hardware info with ??=, but every Query* helper returned a non-null fallback on a WMI fault (CpuInfo("Unknown", …), OsInfo("Windows", …), an empty disk/module list). Because the fallback was non-null, ??= stored it and never re-queried. So if the first CaptureAsync raced a transient WMI fault — plausible under the app's documented ~36-service startup thundering herd → RPC server too busy (0x800706BB) — the fallback defaults were cached process-wide (the service is a singleton) even after WMI recovered milliseconds later. The Dashboard, System Info tab, System Report and tray tooltip then permanently showed "Unknown CPU" / "Windows" / no disks / no RAM modules until the app was restarted.

Fix

Cache only a successful query:

  • QueryOsStatic / QueryCpuStatic return null on a WMI fault (a successful no-rows result still returns the "unknown" answer and is cached).
  • QueryDisks / QueryMemoryModules return null only when a fault yielded nothing; a genuinely empty-but-successful enumeration (or a partial list gathered before a fault) is a usable answer and is cached.
  • Capture uses a transient, non-cached default for the current snapshot when the cache is still null, so ??= retries on the next poll.

Regression test (integration)

CaptureAsync_ReQueriesStaticInfo_AfterCacheCleared: after a successful capture populates the cache, nulling _cachedCpuStatic (the post-fault state the fix now produces) and re-capturing must repopulate a real CPU — proving a cleared/null cache is retried, not left permanently degraded. Red before the fix (the fault path cached a non-null fallback, so the cache was never null to retry).

Checks

  • All 3 projects build 0 warnings / 0 errors
  • Leak scan on diff: clean
  • Behavior unchanged on the happy path (WMI healthy): static info still queried once and cached

…for the session

SystemInfoService caches static hardware with ??=, but every Query* helper
returned a NON-NULL fallback on a WMI fault (CpuInfo("Unknown",…),
OsInfo("Windows",…), empty disk/module lists). ??= stored the non-null fallback
and never re-queried, so a transient fault on the first CaptureAsync (plausible
under the ~36-service startup thundering herd -> RPC-server-too-busy) cached the
fallback defaults process-wide (singleton) even after WMI recovered ms later —
Dashboard / System Info / System Report / tray tooltip stuck on 'Unknown CPU' /
'Windows' / no disks / no RAM until restart.

Each Query* now returns null on a WMI FAULT (distinguished from a genuinely
empty-but-successful result, which is still cached), so ??= caches only a
successful query and retries next poll; Capture uses a transient, non-cached
default for the current snapshot only.

Fixes ultra-audit finding P2 #40.

Regression test (integration): CaptureAsync_ReQueriesStaticInfo_AfterCacheCleared
— after a successful capture, nulling _cachedCpuStatic (the post-fault state the
fix now produces) and re-capturing must repopulate a real CPU, proving a
cleared/null cache is retried rather than left permanently degraded.
@laurentiu021 laurentiu021 merged commit 36e6591 into main Jul 10, 2026
4 checks passed
@laurentiu021 laurentiu021 deleted the fix/systeminfo-cache-poison branch July 10, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant