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",
|
"__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.
|
* Some functions are hard to whitelist, as they don't use a symbol directly.
|
||||||
* Use some static initialization to suppress them on leak reports
|
* Use some static initialization to suppress them on leak reports
|
||||||
@@ -942,7 +964,6 @@ HOOK(void, free, void *ptr)
|
|||||||
{
|
{
|
||||||
memory_header_t *hdr;
|
memory_header_t *hdr;
|
||||||
memory_tail_t *tail;
|
memory_tail_t *tail;
|
||||||
backtrace_t *backtrace;
|
|
||||||
bool before;
|
bool before;
|
||||||
|
|
||||||
if (!enabled || thread_disabled->get(thread_disabled))
|
if (!enabled || thread_disabled->get(thread_disabled))
|
||||||
@@ -974,6 +995,7 @@ HOOK(void, free, void *ptr)
|
|||||||
if (hdr->magic != MEMORY_HEADER_MAGIC ||
|
if (hdr->magic != MEMORY_HEADER_MAGIC ||
|
||||||
tail->magic != MEMORY_TAIL_MAGIC)
|
tail->magic != MEMORY_TAIL_MAGIC)
|
||||||
{
|
{
|
||||||
|
backtrace_t *backtrace = NULL;
|
||||||
bool bt = TRUE;
|
bool bt = TRUE;
|
||||||
|
|
||||||
/* check if memory appears to be allocated by our hooks */
|
/* 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 */
|
/* just free this block of unknown memory */
|
||||||
hdr = ptr;
|
hdr = ptr;
|
||||||
|
|
||||||
if (ignore_unknown)
|
if (ignore_unknown || allow_unknown_memory(&backtrace))
|
||||||
{
|
{
|
||||||
bt = FALSE;
|
bt = FALSE;
|
||||||
}
|
}
|
||||||
@@ -1011,10 +1033,13 @@ HOOK(void, free, void *ptr)
|
|||||||
}
|
}
|
||||||
if (bt)
|
if (bt)
|
||||||
{
|
{
|
||||||
backtrace = backtrace_create(2);
|
if (!backtrace)
|
||||||
|
{
|
||||||
|
backtrace = backtrace_create(2);
|
||||||
|
}
|
||||||
backtrace->log(backtrace, stderr, TRUE);
|
backtrace->log(backtrace, stderr, TRUE);
|
||||||
backtrace->destroy(backtrace);
|
|
||||||
}
|
}
|
||||||
|
DESTROY_IF(backtrace);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1037,7 +1062,6 @@ HOOK(void*, realloc, void *old, size_t bytes)
|
|||||||
{
|
{
|
||||||
memory_header_t *hdr;
|
memory_header_t *hdr;
|
||||||
memory_tail_t *tail;
|
memory_tail_t *tail;
|
||||||
backtrace_t *backtrace;
|
|
||||||
bool before, have_backtrace = TRUE;
|
bool before, have_backtrace = TRUE;
|
||||||
|
|
||||||
if (!enabled || thread_disabled->get(thread_disabled))
|
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 ||
|
if (hdr->magic != MEMORY_HEADER_MAGIC ||
|
||||||
tail->magic != MEMORY_TAIL_MAGIC)
|
tail->magic != MEMORY_TAIL_MAGIC)
|
||||||
{
|
{
|
||||||
|
backtrace_t *backtrace = NULL;
|
||||||
bool bt = TRUE;
|
bool bt = TRUE;
|
||||||
|
|
||||||
/* check if memory appears to be allocated by our hooks */
|
/* check if memory appears to be allocated by our hooks */
|
||||||
@@ -1093,7 +1118,7 @@ HOOK(void*, realloc, void *old, size_t bytes)
|
|||||||
hdr = old;
|
hdr = old;
|
||||||
have_backtrace = FALSE;
|
have_backtrace = FALSE;
|
||||||
|
|
||||||
if (ignore_unknown)
|
if (ignore_unknown || allow_unknown_memory(&backtrace))
|
||||||
{
|
{
|
||||||
bt = FALSE;
|
bt = FALSE;
|
||||||
}
|
}
|
||||||
@@ -1105,10 +1130,13 @@ HOOK(void*, realloc, void *old, size_t bytes)
|
|||||||
}
|
}
|
||||||
if (bt)
|
if (bt)
|
||||||
{
|
{
|
||||||
backtrace = backtrace_create(2);
|
if (!backtrace)
|
||||||
|
{
|
||||||
|
backtrace = backtrace_create(2);
|
||||||
|
}
|
||||||
backtrace->log(backtrace, stderr, TRUE);
|
backtrace->log(backtrace, stderr, TRUE);
|
||||||
backtrace->destroy(backtrace);
|
|
||||||
}
|
}
|
||||||
|
DESTROY_IF(backtrace);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user