CNA-VGAP-059: The VibrateController shutdown-ordering tests never open a haptic device or destroy a Game-owned SDL3 platform after SDL_Quit, and CNA's own plan keeps SDLCORE-011 open for that reason

CNA snapshot 009d40f5  ·  Known Issues › Verification gaps  ·  source links pinned to 009d40f5

✓

Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.

The ordering test spawns a harness that probes the controller and calls SDL_Quit, but no haptic device is opened and the platform is the immortal lazy default, so the SDL_CloseHaptic-after-SDL_Quit path stays reasoned from source only.

Identifier
CNA-VGAP-059
Category
Verification gap
Subsystem
Testing & evidence
Status
Open
Verified against
CNA 009d40f5 (009d40f5dd085c4e674d3479675fac84b12b3e0a)
Evidence basis
Source-verified: read at TARGET, not executed
Tests touching this area
Yes: see Current tests
Affected contract
Microsoft::Devices::VibrateController shutdown ordering (DevicesShutdownCoordinator, Sdl3Haptics::CloseAll) relative to SDL_Quit

Expected behaviour

The hazard DevicesShutdownCoordinator exists for, closing a cached SDL haptic handle after the host's SDL_Quit (a use-after-free by CNA's own reading of SDL), is exercised by a test on a sanitizer build, with a device actually opened and with the platform owned the way a game owns it (by Game, destroyed at scope exit).

Actual behaviour at TARGET

shutdown_ordering_harness.cpp calls VibrateController::getIsSupportedProperty on the lazily created default platform, optionally Shutdown(), then SDL_Quit(), and returns; DevicesShutdownOrderingTests.cpp asserts only exit code 0, so a sanitizer finding needs an ASan build. Where no haptic device exists (the author's container, per the test's own comment, and any host without haptics) no handle is opened, so Sdl3Haptics::CloseAll never reaches SDL_CloseHaptic in the harness, and the lazy default platform is an immortal holder, so ~Sdl3Platform is never run. The test's own comment says the native haptic-close guard 'remains reasoned-from-source only, not reproduced under a sanitizer build', and plan_devices.md still records SDLCORE-011 as OPEN for that reason (it names the removed SdlHapticVibrateBackend; the same SDL_CloseHaptic calls now live in Sdl3Haptics). The spawning test is also excluded on Windows, Emscripten, Android, iOS and SDL2-only builds. A Game-owned SDL3 platform destroyed after a host SDL_Quit runs ~Sdl3Platform, then haptics_.Deactivate(), then SDL_CloseHaptic for each cached handle with no SDL_WasInit check.

Source locations

Evidence

Read at 009d40f5; nothing executed. Read the harness, both ordering tests with their comments, the coordinator and adapter, the SDL3 haptics service and platform destructor, CurrentPlatform's lazy holder and the SDLCORE-011 plan section. That a Game-owned platform would run the unguarded close after a host SDL_Quit is a reading of the code; the use-after-free itself is CNA's own source-reading conclusion about SDL and is not reproduced here.

Focused reproduction

No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.

Current tests

DevicesShutdownOrderingTests spawns shutdown_ordering_harness and asserts exit code 0; the harness opens no haptic device and uses the immortal lazy platform, and the test is excluded on Windows, Emscripten, Android, iOS and SDL2-only builds.

Regression test

On a host with a haptic device (or a fake SDL haptic layer), open a device through a Game-owned SDL3 platform, call SDL_Quit first and destroy the platform afterwards under AddressSanitizer; close SDLCORE-011 when that passes.

Blast radius

Hosts with SDL haptic devices whose application calls SDL_Quit before its Game-owned platform is destroyed; hosts without haptics never reach the close call.

Workaround

Call VibrateController::Shutdown() before SDL_Quit (a documented obligation with a fallback).

The same subject is explained at several altitudes. These are the neighbouring pages at each one.