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).
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user