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
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
tools/devices/shutdown_ordering_harness.cpp— main: probes the controller, optional Shutdown, SDL_Quit, returnmodules/devices/tests/Microsoft/Devices/Detail/DevicesShutdownOrderingTests.cpp— exit-code-only assertions and the comment admitting the haptic-close guard is unreproducedplans/plan_devices.md— SDLCORE-011 heading and Remaining limitationsmodules/platform/src/Sdl3/Sdl3DeviceServices.cpp— Sdl3Haptics destructor, Deactivate and CloseAllmodules/platform/src/Sdl3/Sdl3Platform.cpp— ~Sdl3Platform calls haptics_.Deactivate()modules/platform/src/CurrentPlatform.cpp— LazyDefault is an immortal holdercmake/UnitTests.cmake— DevicesShutdownOrderingTests exclusion listmodules/devices/src/Detail/PlatformVibrateBackend.cpp— ReleaseService identity guard
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).
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Internals
- Devices module internals
- Deep dives
- Sensors and vibration
- Known issues
- Verification gap index