From 128b9e82f2cecd06913411ed152a78d29f2f9fbd Mon Sep 17 00:00:00 2001 From: Stephen Jia Date: Mon, 22 Dec 2025 12:23:45 -0800 Subject: [PATCH] [ET-VK][ez] Fix use-after-free bug in Vulkan queue creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: A use-after-free bug in find_compute_queues() was discovered using Valgrind. The function created a local queue_priorities vector inside a loop and stored its data pointer in VkDeviceQueueCreateInfo. When the vector went out of scope, its memory was freed, but vkCreateDevice() later accessed this freed memory. Fixed by adding a queue_priorities parameter to persist the data until after vkCreateDevice() completes. ## Problem A use-after-free bug was discovered in find_compute_queues() using Valgrind. The function created a local std::vector queue_priorities inside a loop and stored its data pointer in VkDeviceQueueCreateInfo. When the vector went out of scope at the end of each iteration, its memory was freed. Later, when vkCreateDevice() accessed these queue priorities, it read from freed memory. ## Investigation Valgrind reported: - Invalid read of size 4 at 0xb3bdd60 (freed memory) - Block was freed by operator delete in find_compute_queues() - Block was allocated by operator new in find_compute_queues() - Error occurred during vkCreateDevice() call ## Fix Modified find_compute_queues() to accept an additional parameter std::vector>& queue_priorities that persists the queue priority data until after vkCreateDevice() completes. This ensures the memory remains valid when Vulkan needs to access it. Updated all call sites: - create_logical_device() - Adapter constructor (external device variant) ## Verification Valgrind results before fix: 296 errors from 13 contexts, 1 Invalid read Valgrind results after fix: 295 errors from 12 contexts, 0 Invalid reads ✓ Remaining errors are in NVIDIA drivers and third-party libraries. --- backends/vulkan/runtime/vk_api/Adapter.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/backends/vulkan/runtime/vk_api/Adapter.cpp b/backends/vulkan/runtime/vk_api/Adapter.cpp index e0b2f1c978b..57fb4252c74 100644 --- a/backends/vulkan/runtime/vk_api/Adapter.cpp +++ b/backends/vulkan/runtime/vk_api/Adapter.cpp @@ -22,7 +22,8 @@ void find_compute_queues( const PhysicalDevice& physical_device, const uint32_t num_queues_to_create, std::vector& queue_create_infos, - std::vector>& queues_to_get) { + std::vector>& queues_to_get, + std::vector>& queue_priorities) { queue_create_infos.reserve(num_queues_to_create); queues_to_get.reserve(num_queues_to_create); @@ -36,14 +37,14 @@ void find_compute_queues( const uint32_t queues_to_init = std::min(remaining_queues, queue_properties.queueCount); - const std::vector queue_priorities(queues_to_init, 1.0f); + queue_priorities.emplace_back(queues_to_init, 1.0f); queue_create_infos.push_back({ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType nullptr, // pNext 0u, // flags family_i, // queueFamilyIndex queues_to_init, // queueCount - queue_priorities.data(), // pQueuePriorities + queue_priorities.back().data(), // pQueuePriorities }); for (size_t queue_i = 0; queue_i < queues_to_init; ++queue_i) { @@ -90,8 +91,13 @@ VkDevice create_logical_device( std::vector queue_create_infos; std::vector> queues_to_get; + std::vector> queue_priorities; find_compute_queues( - physical_device, num_queues_to_create, queue_create_infos, queues_to_get); + physical_device, + num_queues_to_create, + queue_create_infos, + queues_to_get, + queue_priorities); // Create the VkDevice std::vector requested_device_extensions{ @@ -277,8 +283,13 @@ Adapter::Adapter( owns_device_{false} { std::vector queue_create_infos; std::vector> queues_to_get; + std::vector> queue_priorities; find_compute_queues( - physical_device_, num_queues, queue_create_infos, queues_to_get); + physical_device_, + num_queues, + queue_create_infos, + queues_to_get, + queue_priorities); populate_queue_info( physical_device_, device_.handle, queues_to_get, queues_, queue_usage_); }