Fix shutdown use-after-free of UiState via the ImGui settings handler

register_ui_settings installs an ImGui settings handler whose UserData points at
the run()-local UiState. ~ImGuiLayer calls DestroyContext, which flushes the .ini
through that handler (ui_settings_write_all dereferences UserData). But UiState
was declared after ImGuiLayer in run(), so it (and the panels between them) were
destroyed first -- the shutdown save read freed/clobbered stack every clean exit
(UB; could corrupt coop_layout.ini / the persisted debug-details flag).

Declare UiState before ImGuiLayer so it outlives the context and is destroyed
last. Not unit-testable (shutdown lifetime ordering); fixed by inspection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-24 01:05:47 +02:00
parent 3f1ce0c78a
commit 43d093405e
2 changed files with 5 additions and 4 deletions

View File

@@ -114,9 +114,6 @@ From an in-depth review pass. Each item is fixed test-first (a failing test, the
as its own commit; "verify" items are confirmed real before any change, and dropped if not.
Confirmed bugs:
- **Shutdown use-after-free of `ui`** — the ImGui settings handler holds `&ui`, but `~ImGuiLayer`'s
`DestroyContext` saves settings *after* `ui` (declared later in `run()`) is destroyed. Reorder /
remove the handler before teardown.
- **Host device loss unhandled** — `Present`/`ResizeBuffers`/`CreateRenderTargetView` HRESULTs are
ignored and a removed/reset device spins silently. Detect `DEVICE_REMOVED/RESET`, log
`GetDeviceRemovedReason`, surface it, and halt the render loop cleanly.