diff --git a/malloc/malloc.c b/malloc/malloc.c index 2a61c8b5ee..ef8c794fb7 100644 --- a/malloc/malloc.c +++ b/malloc/malloc.c @@ -1100,6 +1100,8 @@ static void munmap_chunk(mchunkptr p); static mchunkptr mremap_chunk(mchunkptr p, size_t new_size); #endif +static size_t musable (void *mem); + /* ------------------ MMAP support ------------------ */ @@ -3396,6 +3398,14 @@ __libc_realloc (void *oldmem, size_t bytes) if (__glibc_unlikely (mtag_enabled)) *(volatile char*) oldmem; + /* Return the chunk as is whenever possible, i.e. there's enough usable space + but not so much that we end up fragmenting the block. We use the trim + threshold as the heuristic to decide the latter. */ + size_t usable = musable (oldmem); + if (bytes <= usable + && (unsigned long) (usable - bytes) <= mp_.trim_threshold) + return oldmem; + /* chunk corresponding to oldmem */ const mchunkptr oldp = mem2chunk (oldmem); /* its size */ diff --git a/malloc/tst-realloc.c b/malloc/tst-realloc.c index 5eb62a770f..3b78a2420a 100644 --- a/malloc/tst-realloc.c +++ b/malloc/tst-realloc.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -142,6 +143,28 @@ do_test (void) free (p); + /* Smoke test to make sure that allocations do not move if they have enough + space to expand in the chunk. */ + for (size_t sz = 3; sz < 256 * 1024; sz += 2048) + { + p = realloc (NULL, sz); + if (p == NULL) + FAIL_EXIT1 ("realloc (NULL, %zu) returned NULL.", sz); + size_t newsz = malloc_usable_size (p); + printf ("size: %zu, usable size: %zu, extra: %zu\n", + sz, newsz, newsz - sz); + uintptr_t oldp = (uintptr_t) p; + void *new_p = realloc (p, newsz); + if ((uintptr_t) new_p != oldp) + FAIL_EXIT1 ("Expanding (%zu bytes) to usable size (%zu) moved block", + sz, newsz); + free (new_p); + + /* We encountered a large enough extra size at least once. */ + if (newsz - sz > 1024) + break; + } + return 0; }