From 3216646bdbdecaa74f092564ba9c898311893042 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 5 Jun 2026 12:24:43 +0200 Subject: [PATCH] leak-detective: Add workaround for unknown memory reports with glibc With glibc, there is an issue if TZ is not set, which causes a change of the internally cached TZ value. Because the original value was cached before LD was active via `init_static_allocations()`, the memory is freed as unknown memory later. This change allows whitelisting a function that might free such memory (tzset() only for now). --- src/libstrongswan/utils/leak_detective.c | 44 +++++++++++++++++++----- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 201b0f619..a3215014a 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -672,6 +672,28 @@ static char *whitelist[] = { "__cxa_thread_atexit", }; +/** + * Functions that may free memory allocated before hooks were enabled + * (e.g. during init_static_allocations()). If such a function appears + * in the backtrace of a free/realloc of unknown memory, we silently pass + * it through instead of printing a warning. + */ +static char *unknown_memory_whitelist[] = { + "tzset", +}; + +/** + * Check if the current call stack contains a function that is known to free + * pre-existing (i.e. unknown) memory. The backtrace is returned and used to + * report the function if that's not the case. + */ +static bool allow_unknown_memory(backtrace_t **bt) +{ + *bt = backtrace_create(3); + return (*bt)->contains_function(*bt, unknown_memory_whitelist, + countof(unknown_memory_whitelist)); +} + /** * Some functions are hard to whitelist, as they don't use a symbol directly. * Use some static initialization to suppress them on leak reports @@ -942,7 +964,6 @@ HOOK(void, free, void *ptr) { memory_header_t *hdr; memory_tail_t *tail; - backtrace_t *backtrace; bool before; if (!enabled || thread_disabled->get(thread_disabled)) @@ -974,6 +995,7 @@ HOOK(void, free, void *ptr) if (hdr->magic != MEMORY_HEADER_MAGIC || tail->magic != MEMORY_TAIL_MAGIC) { + backtrace_t *backtrace = NULL; bool bt = TRUE; /* check if memory appears to be allocated by our hooks */ @@ -1000,7 +1022,7 @@ HOOK(void, free, void *ptr) /* just free this block of unknown memory */ hdr = ptr; - if (ignore_unknown) + if (ignore_unknown || allow_unknown_memory(&backtrace)) { bt = FALSE; } @@ -1011,10 +1033,13 @@ HOOK(void, free, void *ptr) } if (bt) { - backtrace = backtrace_create(2); + if (!backtrace) + { + backtrace = backtrace_create(2); + } backtrace->log(backtrace, stderr, TRUE); - backtrace->destroy(backtrace); } + DESTROY_IF(backtrace); } else { @@ -1037,7 +1062,6 @@ HOOK(void*, realloc, void *old, size_t bytes) { memory_header_t *hdr; memory_tail_t *tail; - backtrace_t *backtrace; bool before, have_backtrace = TRUE; if (!enabled || thread_disabled->get(thread_disabled)) @@ -1063,6 +1087,7 @@ HOOK(void*, realloc, void *old, size_t bytes) if (hdr->magic != MEMORY_HEADER_MAGIC || tail->magic != MEMORY_TAIL_MAGIC) { + backtrace_t *backtrace = NULL; bool bt = TRUE; /* check if memory appears to be allocated by our hooks */ @@ -1093,7 +1118,7 @@ HOOK(void*, realloc, void *old, size_t bytes) hdr = old; have_backtrace = FALSE; - if (ignore_unknown) + if (ignore_unknown || allow_unknown_memory(&backtrace)) { bt = FALSE; } @@ -1105,10 +1130,13 @@ HOOK(void*, realloc, void *old, size_t bytes) } if (bt) { - backtrace = backtrace_create(2); + if (!backtrace) + { + backtrace = backtrace_create(2); + } backtrace->log(backtrace, stderr, TRUE); - backtrace->destroy(backtrace); } + DESTROY_IF(backtrace); } else {