diff options
| author | BoiledElectricity <james.dellamorte@gmail.com> | 2026-06-15 03:02:53 +0200 |
|---|---|---|
| committer | crueter <crueter@eden-emu.dev> | 2026-06-15 03:02:53 +0200 |
| commit | ef4113aeaade8a7055fbedb386837554aead254f (patch) | |
| tree | bd8dd1e12d645decb3c3f3633e4c117a5d1c6265 /src/video_core/vulkan_common/vulkan_instance.cpp | |
| parent | 78a1cd0533fd4b6d5be77f9f6f2a87ca6b509d03 (diff) | |
[video_core] fix TOCTOU in Vulkan instance extension enumeration (#4072)
So CreateInstance was enumerating instance extensions twice, and that could race if the driver returned a different list the second time. On some AMD iGPU drivers, we could enable an extension from the first list, then fail the second check with VK_ERROR_EXTENSION_NOT_PRESENT.
Fix this by enumerating once and passing that same snapshot into RequiredExtensions.
Fixes: https://github.com/eden-emulator/Issue-Reports/issues/414
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4072
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Diffstat (limited to 'src/video_core/vulkan_common/vulkan_instance.cpp')
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_instance.cpp | 30 |
1 files changed, 20 insertions, 10 deletions
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp index 398342296e..7f3d8ed41f 100644 --- a/src/video_core/vulkan_common/vulkan_instance.cpp +++ b/src/video_core/vulkan_common/vulkan_instance.cpp @@ -36,8 +36,8 @@ namespace { } [[nodiscard]] std::vector<const char*> RequiredExtensions( - const vk::InstanceDispatch& dld, Core::Frontend::WindowSystemType window_type, - bool enable_validation) { + const vk::InstanceDispatch& dld, std::vector<VkExtensionProperties> const& properties, + Core::Frontend::WindowSystemType window_type, bool enable_validation) { std::vector<const char*> extensions; extensions.reserve(6); switch (window_type) { @@ -74,14 +74,14 @@ namespace { if (window_type != Core::Frontend::WindowSystemType::Headless) { extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); } - if (auto const properties = vk::EnumerateInstanceExtensionProperties(dld); properties) { + // Probe optional extensions against the same snapshot the caller verifies against, so the + // check here and the verification in CreateInstance can never disagree (see TOCTOU note below). #ifdef __APPLE__ - if (AreExtensionsSupported(dld, *properties, std::array{VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME})) - extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + if (AreExtensionsSupported(dld, properties, std::array{VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME})) + extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); #endif - if (enable_validation && AreExtensionsSupported(dld, *properties, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME})) - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } + if (enable_validation && AreExtensionsSupported(dld, properties, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME})) + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); return extensions; } @@ -128,9 +128,19 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers"); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); } - std::vector<const char*> const extensions = RequiredExtensions(dld, window_type, enable_validation); + // Enumerate instance extensions exactly once. RequiredExtensions() used to enumerate a second + // time internally; if the driver returned a different set between the two calls (a real TOCTOU + // seen on some AMD iGPU drivers), an extension added to the list could be missing from the + // verification snapshot, throwing EXTENSION_NOT_PRESENT on an otherwise valid launch. Sharing + // one snapshot for both the optional-extension probe and the final check removes that window. auto const properties = vk::EnumerateInstanceExtensionProperties(dld); - if (!properties || !AreExtensionsSupported(dld, *properties, extensions)) + if (!properties) { + LOG_ERROR(Render_Vulkan, "Failed to query instance extension properties"); + throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); + } + std::vector<const char*> const extensions = + RequiredExtensions(dld, *properties, window_type, enable_validation); + if (!AreExtensionsSupported(dld, *properties, extensions)) throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); std::vector<const char*> layers = Layers(enable_validation); RemoveUnavailableLayers(dld, layers); |
