From 47052da3d397f9dafcb0e6e47d8da95fdf1c2018 Mon Sep 17 00:00:00 2001 From: Charles Giessen Date: Tue, 27 Oct 2026 16:59:23 -0500 Subject: [PATCH] Guard against zero size allocations With the recent replacement of alloca with calloc & realloc, several places in the codebase needed extra checks to prevent calling calloc & realloc with a size of zero. Alloca() is defined to return a non-null pointer that is invalid to dereference. Calloc & realloc make no such guarantees. Some platforms return NULL, which the loader would wrongly interpret as out-of-host-memory. Fixes are to check the count before calling the allocation function and add assertions inside the allocation functions to catch the situation in debug builds. --- loader/allocation.c | 2 ++ loader/extension_manual.c | 2 +- loader/loader.c | 34 ++++++++++++++++++++++------------ loader/loader_windows.c | 22 ++++++++++++++++++++-- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/loader/allocation.c b/loader/allocation.c index 4a82c4dde..01c61b5e0 100644 --- a/loader/allocation.c +++ b/loader/allocation.c @@ -32,6 +32,7 @@ #define DEBUG_DISABLE_APP_ALLOCATORS 0 void *loader_calloc(const VkAllocationCallbacks *pAllocator, size_t size, VkSystemAllocationScope allocation_scope) { + assert(size > 0 && "All allocations must have a size greater than zero!"); void *pMemory = NULL; #if (DEBUG_DISABLE_APP_ALLOCATORS == 1) { @@ -67,6 +68,7 @@ void loader_free(const VkAllocationCallbacks *pAllocator, void *pMemory) { void *loader_realloc(const VkAllocationCallbacks *pAllocator, void *pMemory, size_t orig_size, size_t size, VkSystemAllocationScope allocation_scope) { + assert(size > 0 && "All allocations must have a size greater than zero!"); void *pNewMem = NULL; if (pMemory == NULL || orig_size == 0) { pNewMem = loader_calloc(pAllocator, size, allocation_scope); diff --git a/loader/extension_manual.c b/loader/extension_manual.c index 772565daa..11454106f 100644 --- a/loader/extension_manual.c +++ b/loader/extension_manual.c @@ -356,7 +356,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceToolPropertiesEXT(VkP VkResult enumerate_res = VK_SUCCESS; enumerate_res = icd_term->dispatch.EnumerateDeviceExtensionProperties(phys_dev_term->phys_dev, NULL, &ext_count, NULL); - if (enumerate_res != VK_SUCCESS) { + if (enumerate_res != VK_SUCCESS || ext_count == 0) { goto out; } diff --git a/loader/loader.c b/loader/loader.c index ab67253cc..02e575130 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -3699,11 +3699,13 @@ VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_t goto out; } - icd_details = loader_instance_heap_calloc(inst, sizeof(struct ICDManifestInfo) * manifest_files.count, - VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); - if (NULL == icd_details) { - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; + if (manifest_files.count > 0) { + icd_details = loader_instance_heap_calloc(inst, sizeof(struct ICDManifestInfo) * manifest_files.count, + VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (NULL == icd_details) { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } } for (uint32_t i = 0; i < manifest_files.count; i++) { @@ -6037,13 +6039,16 @@ VkResult setup_loader_tramp_phys_devs(struct loader_instance *inst, uint32_t phy new_count = phys_dev_count; } - // We want an old to new index array and a new to old index array - old_to_new_index = - (int32_t *)loader_instance_heap_calloc(inst, sizeof(int32_t) * old_count, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); - if (NULL == old_to_new_index) { - res = VK_ERROR_OUT_OF_HOST_MEMORY; - goto out; + if (old_count > 0) { + // We want an old to new index array and a new to old index array + old_to_new_index = + (int32_t *)loader_instance_heap_calloc(inst, sizeof(int32_t) * old_count, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); + if (NULL == old_to_new_index) { + res = VK_ERROR_OUT_OF_HOST_MEMORY; + goto out; + } } + new_to_old_index = (int32_t *)loader_instance_heap_calloc(inst, sizeof(int32_t) * new_count, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); if (NULL == new_to_old_index) { @@ -6648,6 +6653,11 @@ VkResult setup_loader_tramp_phys_dev_groups(struct loader_instance *inst, uint32 for (cur_idx = 0; cur_idx < group_count; ++cur_idx) { phys_dev_count += groups[cur_idx].physicalDeviceCount; } + + if (phys_dev_count == 0) { + return VK_SUCCESS; + } + VkPhysicalDevice *devices = (VkPhysicalDevice *)loader_instance_heap_calloc(inst, sizeof(VkPhysicalDevice) * phys_dev_count, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); if (NULL == devices) { @@ -7392,7 +7402,7 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumeratePhysicalDeviceGroups( } } - if (NULL != pPhysicalDeviceGroupProperties) { + if (NULL != pPhysicalDeviceGroupProperties && total_count > 0) { // Create an array for the new physical device groups, which will be stored // in the instance for the Terminator code. new_phys_dev_groups = (VkPhysicalDeviceGroupProperties **)loader_instance_heap_calloc( diff --git a/loader/loader_windows.c b/loader/loader_windows.c index 2929d9c6e..8ce598c33 100644 --- a/loader/loader_windows.c +++ b/loader/loader_windows.c @@ -237,9 +237,18 @@ VkResult windows_get_device_registry_files(const struct loader_instance *inst, u // if after obtaining the DeviceNameSize, new device is added start over do { - CM_Get_Device_ID_List_SizeW(&deviceNamesSize, displayGUID, flags); - loader_instance_heap_free(inst, pDeviceNames); + pDeviceNames = NULL; + + CONFIGRET cm_ret = CM_Get_Device_ID_List_SizeW(&deviceNamesSize, displayGUID, flags); + if (CR_SUCCESS != cm_ret) { + loader_log(inst, VULKAN_LOADER_ERROR_BIT | log_target_flag, 0, + "windows_get_device_registry_files: Failed call to CM_Get_Device_ID_List_SizeW with error code %lu", cm_ret); + return VK_ERROR_INCOMPATIBLE_DRIVER; + } + if (deviceNamesSize == 0) { + break; + } pDeviceNames = loader_instance_heap_calloc(inst, deviceNamesSize * sizeof(wchar_t), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); if (pDeviceNames == NULL) { @@ -593,6 +602,10 @@ VkResult windows_read_manifest_from_d3d_adapters(const struct loader_instance *i goto out; } + // Zero size adapter, skip it. + if (full_info->output_value_size == 0) { + continue; + } // Convert the wide string to a narrow string void *buffer = loader_instance_heap_realloc(inst, json_path, json_path_size, full_info->output_value_size, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND); @@ -740,6 +753,11 @@ VkResult enumerate_adapter_physical_devices(struct loader_instance *inst, struct // Get the actual physical devices do { + // As the driver should return the count in the first call, making sure we don't call realloc with a size of zero. + if (count == 0) { + return VK_SUCCESS; + } + void *new_ptr = loader_instance_heap_realloc(inst, next_icd_phys_devs->physical_devices, next_icd_phys_devs->device_count * sizeof(VkPhysicalDevice), count * sizeof(VkPhysicalDevice), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);