From 44e6844788428318b3b2481d5a0605933b967297 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Thu, 27 Feb 2025 11:27:34 +0000 Subject: [PATCH] Update realloc(p,0) semantics This commit changes the behaviour of realloc(p,0) to be free(p) if p!=nullptr, and malloc(0) if p== nullptr. --- src/snmalloc/global/libc.h | 26 +++++++++++++++++++++----- src/test/func/malloc/malloc.cc | 5 +++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/snmalloc/global/libc.h b/src/snmalloc/global/libc.h index 73e178335..d6ffa58fd 100644 --- a/src/snmalloc/global/libc.h +++ b/src/snmalloc/global/libc.h @@ -52,6 +52,25 @@ namespace snmalloc::libc SNMALLOC_FAST_PATH_INLINE void* realloc(void* ptr, size_t size) { + // Glibc treats + // realloc(p, 0) as free(p) + // realloc(nullptr, s) as malloc(s) + // and for the overlap + // realloc(nullptr, 0) as malloc(1) + // Without the first two we don't pass the glibc tests. + // The last one is required by various gnu utilities such as grep, sed. + if (SNMALLOC_UNLIKELY(!is_small_sizeclass(size))) + { + if (SNMALLOC_UNLIKELY(size == 0)) + { + if (SNMALLOC_UNLIKELY(ptr == nullptr)) + return malloc(1); + + dealloc(ptr); + return nullptr; + } + } + size_t sz = alloc_size(ptr); // Keep the current allocation if the given size is in the same sizeclass. if (sz == round_size(size)) @@ -72,13 +91,10 @@ namespace snmalloc::libc } dealloc(ptr); } - else if (SNMALLOC_LIKELY(size == 0)) - { - dealloc(ptr); - } else { - return set_error(); + // Error should be set by alloc on this path already. + SNMALLOC_ASSERT(errno == ENOMEM); } return p; } diff --git a/src/test/func/malloc/malloc.cc b/src/test/func/malloc/malloc.cc index 630d7239a..b3ab963cd 100644 --- a/src/test/func/malloc/malloc.cc +++ b/src/test/func/malloc/malloc.cc @@ -265,6 +265,9 @@ int main(int argc, char** argv) test_calloc(0, size, SUCCESS, false); } + // Check realloc(nullptr,0) behaves like malloc(1) + test_realloc(nullptr, 0, SUCCESS, false); + for (smallsizeclass_t sc = 0; sc < NUM_SMALL_SIZECLASSES; sc++) { const size_t size = sizeclass_to_size(sc); @@ -277,6 +280,8 @@ int main(int argc, char** argv) test_realloc(our_malloc(size), size2, SUCCESS, false); test_realloc(our_malloc(size + 1), size2, SUCCESS, false); } + // Check realloc(p,0), behaves like free(p), if p != nullptr + test_realloc(our_malloc(size), 0, SUCCESS, true); } for (smallsizeclass_t sc = 0; sc < (MAX_SMALL_SIZECLASS_BITS + 4); sc++)