leak-detective: Add an option to ignore frees of unknown memory blocks

This also changes how unknown/corrupted memory is handled in the free()
and realloc() hooks in general.

Incorporates changes provided by Thomas Egerer who ran into a similar
issue.
This commit is contained in:
Tobias Brunner
2018-09-12 16:25:00 +02:00
parent 9ee23d5efa
commit 472efd3809
+101 -26
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2014 Tobias Brunner
* Copyright (C) 2013-2018 Tobias Brunner
* Copyright (C) 2006-2013 Martin Willi
* HSR Hochschule fuer Technik Rapperswil
*
@@ -162,7 +162,12 @@ static spinlock_t *lock;
/**
* Is leak detection currently enabled?
*/
static bool enabled = FALSE;
static bool enabled;
/**
* Whether to report calls to free() with memory not allocated by us
*/
static bool ignore_unknown;
/**
* Is leak detection disabled for the current thread?
@@ -888,7 +893,7 @@ HOOK(void, free, void *ptr)
return;
}
/* allow freeing of NULL */
if (ptr == NULL)
if (!ptr)
{
return;
}
@@ -899,21 +904,47 @@ HOOK(void, free, void *ptr)
if (hdr->magic != MEMORY_HEADER_MAGIC ||
tail->magic != MEMORY_TAIL_MAGIC)
{
bool bt = TRUE;
/* check if memory appears to be allocated by our hooks */
if (has_hdr(hdr))
{
/* memory was allocated by our hooks but is corrupted */
fprintf(stderr, "freeing corrupted memory (%p): "
"header magic 0x%x, tail magic 0x%x:\n",
ptr, hdr->magic, tail->magic);
"%u bytes, header magic 0x%x, tail magic 0x%x:\n",
ptr, hdr->bytes, hdr->magic, tail->magic);
remove_hdr(hdr);
if (hdr->magic == MEMORY_HEADER_MAGIC)
{ /* only access the old backtrace if header magic is valid */
hdr->backtrace->log(hdr->backtrace, stderr, TRUE);
hdr->backtrace->destroy(hdr->backtrace);
}
else
{
fprintf(stderr, " header magic invalid, ignore backtrace of "
"allocation\n");
}
}
else
{
/* memory was not allocated by our hooks */
fprintf(stderr, "freeing invalid memory (%p)\n", ptr);
/* just free this block of unknown memory */
hdr = ptr;
if (ignore_unknown)
{
bt = FALSE;
}
else
{
fprintf(stderr, "freeing unknown memory (%p):\n", ptr);
}
}
if (bt)
{
backtrace = backtrace_create(2);
backtrace->log(backtrace, stderr, TRUE);
backtrace->destroy(backtrace);
}
backtrace = backtrace_create(2);
backtrace->log(backtrace, stderr, TRUE);
backtrace->destroy(backtrace);
}
else
{
@@ -921,12 +952,11 @@ HOOK(void, free, void *ptr)
hdr->backtrace->destroy(hdr->backtrace);
/* clear MAGIC, set mem to something remarkable */
/* set mem to something remarkable */
memset(hdr, MEMORY_FREE_PATTERN,
sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
real_free(hdr);
}
real_free(hdr);
enable_thread(before);
}
@@ -938,19 +968,19 @@ HOOK(void*, realloc, void *old, size_t bytes)
memory_header_t *hdr;
memory_tail_t *tail;
backtrace_t *backtrace;
bool before;
bool before, have_backtrace = TRUE;
if (!enabled || thread_disabled->get(thread_disabled))
{
return real_realloc(old, bytes);
}
/* allow reallocation of NULL */
if (old == NULL)
if (!old)
{
return malloc(bytes);
}
/* handle zero size as a free() */
if (bytes == 0)
if (!bytes)
{
free(old);
return NULL;
@@ -959,22 +989,64 @@ HOOK(void*, realloc, void *old, size_t bytes)
hdr = old - sizeof(memory_header_t);
tail = old + hdr->bytes;
remove_hdr(hdr);
before = enable_thread(FALSE);
if (hdr->magic != MEMORY_HEADER_MAGIC ||
tail->magic != MEMORY_TAIL_MAGIC)
{
fprintf(stderr, "reallocating invalid memory (%p):\n"
"header magic 0x%x:\n", old, hdr->magic);
backtrace = backtrace_create(2);
backtrace->log(backtrace, stderr, TRUE);
backtrace->destroy(backtrace);
bool bt = TRUE;
/* check if memory appears to be allocated by our hooks */
if (has_hdr(hdr))
{
fprintf(stderr, "reallocating corrupted memory (%p, %u bytes): "
"%zu bytes, header magic 0x%x, tail magic 0x%x:\n",
old, hdr->bytes, bytes, hdr->magic, tail->magic);
remove_hdr(hdr);
if (hdr->magic == MEMORY_HEADER_MAGIC)
{ /* only access header fields (backtrace, bytes) if header magic
* is still valid */
hdr->backtrace->log(hdr->backtrace, stderr, TRUE);
memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
}
else
{
fprintf(stderr, " header magic invalid, ignore backtrace of "
"allocation\n");
have_backtrace = FALSE;
hdr->magic = MEMORY_HEADER_MAGIC;
}
}
else
{
/* adopt this block of unknown memory */
hdr = old;
have_backtrace = FALSE;
if (ignore_unknown)
{
bt = FALSE;
}
else
{
fprintf(stderr, "reallocating unknown memory (%p): %zu bytes:\n",
old, bytes);
}
}
if (bt)
{
backtrace = backtrace_create(2);
backtrace->log(backtrace, stderr, TRUE);
backtrace->destroy(backtrace);
}
}
else
{
remove_hdr(hdr);
/* clear tail magic, allocate, set tail magic */
memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
}
hdr = real_realloc(hdr,
sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
@@ -983,8 +1055,10 @@ HOOK(void*, realloc, void *old, size_t bytes)
/* update statistics */
hdr->bytes = bytes;
before = enable_thread(FALSE);
hdr->backtrace->destroy(hdr->backtrace);
if (have_backtrace)
{
hdr->backtrace->destroy(hdr->backtrace);
}
hdr->backtrace = backtrace_create(2);
enable_thread(before);
@@ -1027,6 +1101,7 @@ leak_detective_t *leak_detective_create()
free(this);
return NULL;
}
ignore_unknown = getenv("LEAK_DETECTIVE_IGNORE_UNKNOWN") != NULL;
lock = spinlock_create();
thread_disabled = thread_value_create(NULL);