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
 	{