Commit 3216646bdb for strongswan.org
commit 3216646bdbdecaa74f092564ba9c898311893042
Author: Tobias Brunner <tobias@strongswan.org>
Date: Fri Jun 5 12:24:43 2026 +0200
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).
diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c
index 201b0f619b..a3215014a2 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
{