Description
Hi all, I think I have found a bug in the region_allocator used by Vulkan.
In short, the bug is created by a call to the can_split(const BlockRegion *block_region, size_t size) method which, instead of checking the size returned by the conform_size(size_t offset, size_t size, size_t alignment, size_t nearest_multiple) method, uses the size set in the MemoryRequest. This can cause a problem if the free region returned by the method find_block_region has the same size as the value returned by the conform_size method. Since the request->size value is used this case is not recognised and leads to the creation of a memory region with a theoretical size of zero. When a new memory region is created, the conform_size method is used to determine its size. The method will not return 0 but the nearest multiple of the properties.nearest_multiple value starting from the actual_alignment value passed to the method. This happens do to this check.
This creates the problem that if all regions in a block are free and the coalesce_block_regions method is called, the size of the resulting region exceeds the size of the block.
This also leads to the following error:
The fix I made is as follows. I have not tested it yet.
Original:
BlockRegion *block_region = find_block_region(user_context, request);
if (block_region == nullptr) {
#ifdef DEBUG_RUNTIME_INTERNAL
debug(user_context) << "RegionAllocator: Failed to locate region for requested size ("
<< (int32_t)(request.size) << " bytes)!\n";
#endif
return nullptr;
}
if (can_split(block_region, request->size)) {
#ifdef DEBUG_RUNTIME_INTERNAL
debug(user_context) << "RegionAllocator: Splitting region of size ( " << (int32_t)(block_region->memory.size) << ") "
<< "to accomodate requested size (" << (int32_t)(request.size) << " bytes)!\n";
#endif
split_block_region(user_context, block_region, request.size, request.alignment);
}
Fixed:
BlockRegion *block_region = find_block_region(user_context, request);
if (block_region == nullptr) {
#ifdef DEBUG_RUNTIME_INTERNAL
debug(user_context) << "RegionAllocator: Failed to locate region for requested size ("
<< (int32_t)(request.size) << " bytes)!\n";
#endif
return nullptr;
}
// Need to check if empty region is not 0
actual_size = conform_size(block_region->memory.offset, request.size, actual_alignment, block->memory.properties.nearest_multiple);
if (can_split(block_region, actual_size)) {
#ifdef DEBUG_RUNTIME_INTERNAL
debug(user_context) << "RegionAllocator: Splitting region of size ( " << (int32_t)(block_region->memory.size) << ") "
<< "to accomodate requested size (" << (int32_t)(request.size) << " bytes)!\n";
#endif
split_block_region(user_context, block_region, request.size, request.alignment);
}