From 7b8299c8dde4f4e302c0f27b5ead3468b37e9753 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 10 Feb 2026 11:23:51 +0100 Subject: [PATCH] - Reallocating overaligned memory with the LibcAllocator was unsafe. --- lib/std/core/allocators/libc_allocator.c3 | 43 +++++++++++++++-------- releasenotes.md | 1 + 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/lib/std/core/allocators/libc_allocator.c3 b/lib/std/core/allocators/libc_allocator.c3 index 9c8c5cc6e..4b59e5c5e 100644 --- a/lib/std/core/allocators/libc_allocator.c3 +++ b/lib/std/core/allocators/libc_allocator.c3 @@ -15,7 +15,6 @@ module std::core::mem::allocator @if(env::POSIX); import std::os; import libc; - fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic { if (init_type == ZERO) @@ -50,24 +49,38 @@ fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a fn void*? LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic { if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~; - void* new_ptr; - if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return mem::OUT_OF_MEMORY~; - $switch: - $case env::DARWIN: - usz old_usable_size = darwin::malloc_size(old_ptr); - $case env::LINUX: - usz old_usable_size = linux::malloc_usable_size(old_ptr); - $default: - usz old_usable_size = new_bytes; - $endswitch + // Easy case, it's 0 + if (!new_bytes) + { + libc::free(old_ptr); + return null; + } + + // Try realloc, even though it might be unaligned. + void* new_ptr = libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~!; + + // If it's aligned then we're done! + uptr ptr_val = (uptr)new_ptr; + if (ptr_val & (alignment - 1) == 0) return new_ptr; + + // We failed, so we need to use memalign + // We will free new_ptr before we exit. + defer libc::free(new_ptr); + + // Create a pointer which is sure to be aligned. + void* aligned_ptr; + if (posix::posix_memalign(&aligned_ptr, alignment, new_bytes)) + { + return mem::OUT_OF_MEMORY~; + } + // Now it is safe to copy the full range of data. + mem::copy(aligned_ptr, old_ptr, new_bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT); + return aligned_ptr; - usz copy_size = new_bytes < old_usable_size ? new_bytes : old_usable_size; - mem::copy(new_ptr, old_ptr, copy_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT); - libc::free(old_ptr); - return new_ptr; } + fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic { libc::free(old_ptr); diff --git a/releasenotes.md b/releasenotes.md index fe7a74a8c..1399ee88a 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -27,6 +27,7 @@ - Const inline enums would not always implicitly get converted to the underlying type. - Update to dstring.append_string to take any type converting to String. - Flag `--cpu-flags` doesn't work if the first item is an exclusion. #2905 +- Reallocating overaligned memory with the LibcAllocator was unsafe. ## 0.7.9 Change list