CNA-BUG-096: VulkanRenderer's constructor leaks the Vulkan handles it created when a later construction step throws

CNA snapshot 009d40f5  ·  Known Issues › Current bugs  ·  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.

VulkanRenderer::VulkanRenderer creates the instance, debug messenger, device, swapchain and other raw handles in sequence with no cleanup path, so a throw from a later step such as PickPhysicalDevice leaves them undestroyed.

Identifier
CNA-BUG-096
Category
Bug
Subsystem
Graphics & renderers
Status
Open
Verified against
CNA 009d40f5 (009d40f5dd085c4e674d3479675fac84b12b3e0a)
Severity
Low (a triage suggestion, not a project priority)
Evidence basis
Source-verified: read at TARGET, not executed
Tests touching this area
Yes: see Current tests
Affected contract
GraphicsDevice construction on VULKAN, including runtime fallback from VULKAN to another renderer

Expected behaviour

A failed renderer construction releases what it created, so that a fallback chain can move to the next renderer without leaking, and so that Vulkan object tracking and LeakSanitizer stay clean. VulkanRenderer::~VulkanRenderer is written for partial states (it destroys only the instance when device_ is null), but a destructor never runs for an object whose constructor threw.

Actual behaviour at TARGET

The constructor body calls CreateInstance, SetupDebugMessenger (validation builds), CreateSurface, PickPhysicalDevice, CreateLogicalDevice, CreateSwapchain and then image views, depth resources, render passes, framebuffers, command pool and buffers, sync objects, sampler, descriptor layout and pool and sprite buffers, with no try/catch and no scope guard. The handles are plain members (VkInstance instance_, VkDebugUtilsMessengerEXT debugMessenger_, VkDevice device_, VkSwapchainKHR swapchain_ and more), so only RAII members such as platformSurface_ are cleaned up while the exception unwinds. PickPhysicalDevice throws "Vulkan: no GPU found" or "Vulkan: no suitable GPU" after the instance and surface exist, so every failed attempt on a host without a usable Vulkan device leaks the instance (and the debug messenger in validation builds); a throw after CreateLogicalDevice also leaks the device and everything created on it.

Source locations

Evidence

Checked by reading at 009d40f5; not executed. In C++ only fully constructed members are destroyed when a constructor throws; the class destructor does not run. VULKAN-013's terminate handler exists because PickPhysicalDevice's two refusals are routine on machines without a usable device. SDL_GPU has a comparable guard and test (SdlGpu_ConstructorExceptionSafety); VULKAN has neither.

Focused reproduction

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

Current tests

vulkan_unavailable_device_skip.cpp turns the two expected failures into a CTest skip; the VULKAN-408 validation-output gate reports leaks only for a device that is destroyed. No test constructs the renderer with a failure injected after CreateInstance.

Regression test

Give the constructor a cleanup path (a function-try-block or a scope guard calling a teardown that tolerates partial state) and add a failure-injection seam after each creation step, asserting under the validation layer's object tracking that no handle survives a failed construction.

Blast radius

VULKAN only, on hosts where construction fails part-way (no suitable GPU, surface or swapchain failure, out of memory). A fallback chain that continues to another renderer keeps the leaked handles for the life of the process, and sanitizer or validation-layer runs on such hosts report them. Successful constructions are unaffected.

Workaround

No workaround is known.

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

Maintainer workflow
Debug shutdown and lifetime
Known issues
Bug index