From a5efdff93c36f75345a2a18f18bffee9b602bc7b Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 22 Oct 2021 16:00:02 +0200 Subject: [PATCH] BUG/MEDIUM: lua: fix memory leaks with realloc() on non-glibc systems In issue #1406, Lev Petrushchak reported a nasty memory leak on Alpine since haproxy 2.4 when using Lua, that memory profiling didn't detect. After inspecting the code and Lua's code, it appeared that Lua's default allocator does an explicit free() on size zero, while since 2.4 commit d36c7fa5e ("MINOR: lua: simplify hlua_alloc() to only rely on realloc()"), haproxy only calls realloc(ptr,0) that performs a free() on glibc but not on other systems as it's not required by POSIX... This patch reinstalls the explicit test for nsize==0 to call free(). Thanks to Lev for the very documented report, and to Tim for the links to a musl thread on the same subject that confirms the diagnostic. This must be backported to 2.4. --- src/hlua.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/hlua.c b/src/hlua.c index 758341a4f..ac61a3171 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -11451,6 +11451,12 @@ int hlua_post_init() * indicated by being NULL. A free is indicated by being * zero. This one verifies that the limits are respected but is optimized * for the fast case where limits are not used, hence stats are not updated. + * + * Warning: while this API ressembles glibc's realloc() a lot, glibc surpasses + * POSIX by making realloc(ptr,0) an effective free(), but others do not do + * that and will simply allocate zero as if it were the result of malloc(0), + * so mapping this onto realloc() will lead to memory leaks on non-glibc + * systems. */ static void *hlua_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { @@ -11463,8 +11469,13 @@ static void *hlua_alloc(void *ud, void *ptr, size_t osize, size_t nsize) /* a limit of ~0 means unlimited and boot complete, so there's no need * for accounting anymore. */ - if (likely(~zone->limit == 0)) - return realloc(ptr, nsize); + if (likely(~zone->limit == 0)) { + if (!nsize) + ha_free(&ptr); + else + ptr = realloc(ptr, nsize); + return ptr; + } if (!ptr) osize = 0; @@ -11478,7 +11489,10 @@ static void *hlua_alloc(void *ud, void *ptr, size_t osize, size_t nsize) return NULL; } while (!_HA_ATOMIC_CAS(&zone->allocated, &old, new)); - ptr = realloc(ptr, nsize); + if (!nsize) + ha_free(&ptr); + else + ptr = realloc(ptr, nsize); if (unlikely(!ptr && nsize)) // failed _HA_ATOMIC_SUB(&zone->allocated, nsize - osize);