aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/vulkan_common/vulkan_instance.cpp
diff options
context:
space:
mode:
authorBoiledElectricity <james.dellamorte@gmail.com>2026-06-15 03:02:53 +0200
committercrueter <crueter@eden-emu.dev>2026-06-15 03:02:53 +0200
commitef4113aeaade8a7055fbedb386837554aead254f (patch)
treebd8dd1e12d645decb3c3f3633e4c117a5d1c6265 /src/video_core/vulkan_common/vulkan_instance.cpp
parent78a1cd0533fd4b6d5be77f9f6f2a87ca6b509d03 (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.cpp30
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);