Browse Source

[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>
pull/4095/head
BoiledElectricity 2 days ago
committed by crueter
parent
commit
ef4113aeaa
No known key found for this signature in database GPG Key ID: 425ACD2D4830EBC6
  1. 30
      src/video_core/vulkan_common/vulkan_instance.cpp

30
src/video_core/vulkan_common/vulkan_instance.cpp

@ -36,8 +36,8 @@ namespace {
} }
[[nodiscard]] std::vector<const char*> RequiredExtensions( [[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; std::vector<const char*> extensions;
extensions.reserve(6); extensions.reserve(6);
switch (window_type) { switch (window_type) {
@ -74,14 +74,14 @@ namespace {
if (window_type != Core::Frontend::WindowSystemType::Headless) { if (window_type != Core::Frontend::WindowSystemType::Headless) {
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); 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__ #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 #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; 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"); LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers");
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 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); 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); throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
std::vector<const char*> layers = Layers(enable_validation); std::vector<const char*> layers = Layers(enable_validation);
RemoveUnavailableLayers(dld, layers); RemoveUnavailableLayers(dld, layers);

Loading…
Cancel
Save