From baf35cc80efe5826a5f6794ca4c95c6483a63086 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Fri, 11 Feb 2022 15:11:15 +0000 Subject: [PATCH] Return a small allocation from realloc(ptr, 0). (#460) An annoying amount of real-world code (e.g. mandoc, BSD sort) treats a NULL return from `realloc` as a failure, even when requesting a size of 0. This code is wrong (the standard explicitly permits a return of NULL from realloc when given a size 0) but working around it in snmalloc is easier than fixing it everywhere. --- src/mem/sizeclasstable.h | 7 ++++++- src/test/func/malloc/malloc.cc | 9 ++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/mem/sizeclasstable.h b/src/mem/sizeclasstable.h index 7355987..1569bc8 100644 --- a/src/mem/sizeclasstable.h +++ b/src/mem/sizeclasstable.h @@ -480,9 +480,14 @@ namespace snmalloc { return bits::next_pow2(size); } + // If realloc(ptr, 0) returns nullptr, some consumers treat this as a + // reallocation failure and abort. To avoid this, we round up the size of + // requested allocations to the smallest size class. This can be changed + // on any platform that's happy to return nullptr from realloc(ptr,0) and + // should eventually become a configuration option. if (size == 0) { - return 0; + return sizeclass_to_size(size_to_sizeclass(1)); } return sizeclass_to_size(size_to_sizeclass(size)); } diff --git a/src/test/func/malloc/malloc.cc b/src/test/func/malloc/malloc.cc index 5ff3f83..43fda4a 100644 --- a/src/test/func/malloc/malloc.cc +++ b/src/test/func/malloc/malloc.cc @@ -36,12 +36,19 @@ void check_result(size_t size, size_t align, void* p, int err, bool null) failed = true; } const auto alloc_size = our_malloc_usable_size(p); - const auto expected_size = round_size(size); + auto expected_size = round_size(size); #ifdef SNMALLOC_PASS_THROUGH // Calling system allocator may allocate a larger block than // snmalloc. Note, we have called the system allocator with // the size snmalloc would allocate, so it won't be smaller. const auto exact_size = false; + // We allocate MIN_ALLOC_SIZE byte for 0-sized allocations (and so round_size + // will tell us that the minimum size is MIN_ALLOC_SIZE), but the system + // allocator may return a 0-sized allocation. + if (size == 0) + { + expected_size = 0; + } #else const auto exact_size = align == 1; #endif