diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index 43fb4d036..5ea096c62 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -846,6 +846,10 @@ void Device::initialize(Instance& instance, const Version version, const Contain const auto found = std::lower_bound(knownExtensions.begin(), knownExtensions.end(), extension, [](const Extension& a, Containers::StringView b) { return a.string() < b; }); + /* Thanks, C++, for forcing me to have a larger bug surface instead + of providing a library helper to find the damn thing. See + DeviceVkTest::wrapExtensionNotFound() for verification of both + cases. */ if(found == knownExtensions.end() || found->string() != extension) continue; _enabledExtensions.set(found->index(), true); } diff --git a/src/Magnum/Vk/Instance.cpp b/src/Magnum/Vk/Instance.cpp index 047efd41b..8c7ce7a9d 100644 --- a/src/Magnum/Vk/Instance.cpp +++ b/src/Magnum/Vk/Instance.cpp @@ -346,6 +346,10 @@ void Instance::initialize(const Version version, const Containers::StringIterabl const auto found = std::lower_bound(knownExtensions.begin(), knownExtensions.end(), extension, [](const InstanceExtension& a, Containers::StringView b) { return a.string() < b; }); + /* Thanks, C++, for forcing me to have a larger bug surface instead + of providing a library helper to find the damn thing. See + InstanceVkTest::wrapExtensionNotFound() for verification of both + cases. */ if(found == knownExtensions.end() || found->string() != extension) continue; _extensionStatus.set(found->index(), true); } diff --git a/src/Magnum/Vk/Test/DeviceVkTest.cpp b/src/Magnum/Vk/Test/DeviceVkTest.cpp index a356524ba..357651ce5 100644 --- a/src/Magnum/Vk/Test/DeviceVkTest.cpp +++ b/src/Magnum/Vk/Test/DeviceVkTest.cpp @@ -95,6 +95,7 @@ struct DeviceVkTest: VulkanTester { void tryCreateUnknownExtension(); void wrap(); + void wrapExtensionNotFound(); void wrapAlreadyCreated(); void populateGlobalFunctionPointers(); @@ -233,6 +234,7 @@ DeviceVkTest::DeviceVkTest(): VulkanTester{NoCreate} { &DeviceVkTest::tryCreateUnknownExtension, &DeviceVkTest::wrap, + &DeviceVkTest::wrapExtensionNotFound, &DeviceVkTest::wrapAlreadyCreated, &DeviceVkTest::populateGlobalFunctionPointers}); @@ -1189,6 +1191,45 @@ void DeviceVkTest::wrap() { wrapped->DestroyDevice(device, nullptr); } +void DeviceVkTest::wrapExtensionNotFound() { + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + + /* Creating a dedicated instance so we can enable device extensions + independently */ + Instance instance2; + + DeviceProperties deviceProperties = pickDevice(instance2); + ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({}); + if(!extensions.isSupported()) + CORRADE_SKIP(Extensions::KHR::bind_memory2::string() << "not supported, can't test"); + + Queue queue{NoCreate}; + Device device{instance2, DeviceCreateInfo{deviceProperties} + .addQueues(0, {0.0f}, {queue}) + .addEnabledExtensions() + }; + + /* Verify that we don't dereference garbage when std::lower_bound() ... */ + Device wrapped{NoCreate}; + wrapped.wrap(instance2, deviceProperties, device, device.version(), { + /* ... returns `last` for an unknown extension, */ + "VK_ZZZ_this_does_not_exist", + Extensions::KHR::bind_memory2::string(), + /* ... or returns an extension that isn't actually the one we're + looking for, in this case being right before VK_KHR_maintenance1 + because of this here + -----------------v */ + "VK_KHR_maintenancd1", + }, {}, {}); + + /* This one shouldn't be listed */ + CORRADE_VERIFY(!wrapped.isExtensionEnabled()); + + /* The valid extension should be */ + CORRADE_VERIFY(wrapped.isExtensionEnabled()); +} + void DeviceVkTest::wrapAlreadyCreated() { CORRADE_SKIP_IF_NO_ASSERT(); diff --git a/src/Magnum/Vk/Test/InstanceVkTest.cpp b/src/Magnum/Vk/Test/InstanceVkTest.cpp index 05f59877f..10f988746 100644 --- a/src/Magnum/Vk/Test/InstanceVkTest.cpp +++ b/src/Magnum/Vk/Test/InstanceVkTest.cpp @@ -64,6 +64,7 @@ struct InstanceVkTest: TestSuite::Tester { void tryCreateUnknownExtension(); void wrap(); + void wrapExtensionNotFound(); void wrapAlreadyCreated(); void populateGlobalFunctionPointers(); @@ -172,6 +173,7 @@ InstanceVkTest::InstanceVkTest() { &InstanceVkTest::tryCreateUnknownExtension, &InstanceVkTest::wrap, + &InstanceVkTest::wrapExtensionNotFound, &InstanceVkTest::wrapAlreadyCreated, &InstanceVkTest::populateGlobalFunctionPointers}); @@ -526,6 +528,38 @@ void InstanceVkTest::wrap() { wrapped->DestroyInstance(instance, nullptr); } +void InstanceVkTest::wrapExtensionNotFound() { + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + + InstanceExtensionProperties properties = enumerateInstanceExtensionProperties(); + if(!properties.isSupported()) + CORRADE_SKIP(Extensions::EXT::debug_report::string() << "not supported, can't test"); + + Instance instance{InstanceCreateInfo{} + .addEnabledExtensions() + }; + + /* Verify that we don't dereference garbage when std::lower_bound() ... */ + Instance wrapped{NoCreate}; + wrapped.wrap(instance, Version::Vk11, { + /* ... returns `last` for an unknown extension, */ + "VK_ZZZ_this_does_not_exist", + Extensions::EXT::debug_report::string(), + /* ... or returns an extension that isn't actually the one we're + looking for, in this case being right before + VK_KHR_get_physical_device_properties2 because of this here + ------------------------------------v */ + "VK_KHR_get_physical_device_propertier2", + }, {}); + + /* This one shouldn't be listed */ + CORRADE_VERIFY(!wrapped.isExtensionEnabled()); + + /* The valid extension should be */ + CORRADE_VERIFY(wrapped.isExtensionEnabled()); +} + void InstanceVkTest::wrapAlreadyCreated() { CORRADE_SKIP_IF_NO_ASSERT();