2006-04-10 08:07:38 +00:00
|
|
|
/*
|
2018-08-08 15:06:15 +00:00
|
|
|
* Copyright (C) 2013-2018 Tobias Brunner
|
2013-04-02 11:37:06 +00:00
|
|
|
* Copyright (C) 2006-2013 Martin Willi
|
2018-05-23 14:04:50 +00:00
|
|
|
* HSR Hochschule fuer Technik Rapperswil
|
2006-04-10 08:07:38 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2008-12-12 09:10:52 +00:00
|
|
|
#define _GNU_SOURCE
|
2006-04-10 08:07:38 +00:00
|
|
|
#include <stddef.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
2009-09-04 11:46:09 +00:00
|
|
|
#include <signal.h>
|
2006-04-18 07:22:20 +00:00
|
|
|
#include <unistd.h>
|
2008-03-13 14:14:44 +00:00
|
|
|
#include <locale.h>
|
2013-10-11 12:45:56 +00:00
|
|
|
#ifdef HAVE_DLADDR
|
2013-04-02 11:37:06 +00:00
|
|
|
#include <dlfcn.h>
|
2013-10-11 12:45:56 +00:00
|
|
|
#endif
|
2013-04-02 13:47:55 +00:00
|
|
|
#include <time.h>
|
2013-04-18 11:07:27 +00:00
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <malloc/malloc.h>
|
|
|
|
/* overload some of our types clashing with mach */
|
|
|
|
#define host_t strongswan_host_t
|
|
|
|
#define processor_t strongswan_processor_t
|
|
|
|
#define thread_t strongswan_thread_t
|
|
|
|
#endif /* __APPLE__ */
|
2006-04-10 08:07:38 +00:00
|
|
|
|
|
|
|
#include "leak_detective.h"
|
|
|
|
|
2006-10-18 11:46:13 +00:00
|
|
|
#include <library.h>
|
2013-10-11 12:45:56 +00:00
|
|
|
#include <utils/utils.h>
|
2012-10-16 14:03:21 +00:00
|
|
|
#include <utils/debug.h>
|
2008-11-05 13:58:19 +00:00
|
|
|
#include <utils/backtrace.h>
|
2012-10-16 12:54:16 +00:00
|
|
|
#include <collections/hashtable.h>
|
2013-04-02 11:37:06 +00:00
|
|
|
#include <threading/thread_value.h>
|
|
|
|
#include <threading/spinlock.h>
|
2006-04-10 08:07:38 +00:00
|
|
|
|
2008-03-13 14:14:44 +00:00
|
|
|
typedef struct private_leak_detective_t private_leak_detective_t;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* private data of leak_detective
|
|
|
|
*/
|
|
|
|
struct private_leak_detective_t {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* public functions
|
|
|
|
*/
|
|
|
|
leak_detective_t public;
|
2013-10-16 08:37:38 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Registered report() function
|
|
|
|
*/
|
|
|
|
leak_detective_report_cb_t report_cb;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Registered report() summary function
|
|
|
|
*/
|
|
|
|
leak_detective_summary_cb_t report_scb;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Registered user data for callbacks
|
|
|
|
*/
|
|
|
|
void *report_data;
|
2008-03-13 14:14:44 +00:00
|
|
|
};
|
2006-04-10 08:07:38 +00:00
|
|
|
|
|
|
|
/**
|
2006-07-12 11:15:31 +00:00
|
|
|
* Magic value which helps to detect memory corruption. Yummy!
|
2006-04-10 08:07:38 +00:00
|
|
|
*/
|
2006-07-12 11:15:31 +00:00
|
|
|
#define MEMORY_HEADER_MAGIC 0x7ac0be11
|
|
|
|
|
2008-03-28 14:51:26 +00:00
|
|
|
/**
|
|
|
|
* Magic written to tail of allocation
|
|
|
|
*/
|
|
|
|
#define MEMORY_TAIL_MAGIC 0xcafebabe
|
|
|
|
|
2006-07-12 11:15:31 +00:00
|
|
|
/**
|
|
|
|
* Pattern which is filled in memory before freeing it
|
|
|
|
*/
|
|
|
|
#define MEMORY_FREE_PATTERN 0xFF
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pattern which is filled in newly allocated memory
|
|
|
|
*/
|
|
|
|
#define MEMORY_ALLOC_PATTERN 0xEE
|
|
|
|
|
2006-04-10 08:07:38 +00:00
|
|
|
typedef struct memory_header_t memory_header_t;
|
2008-03-28 14:51:26 +00:00
|
|
|
typedef struct memory_tail_t memory_tail_t;
|
2006-04-10 08:07:38 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Header which is prepended to each allocated memory block
|
|
|
|
*/
|
|
|
|
struct memory_header_t {
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2006-04-10 08:07:38 +00:00
|
|
|
/**
|
|
|
|
* Pointer to previous entry in linked list
|
|
|
|
*/
|
|
|
|
memory_header_t *previous;
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2006-04-10 08:07:38 +00:00
|
|
|
/**
|
|
|
|
* Pointer to next entry in linked list
|
|
|
|
*/
|
|
|
|
memory_header_t *next;
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2008-11-05 13:58:19 +00:00
|
|
|
/**
|
|
|
|
* backtrace taken during (re-)allocation
|
|
|
|
*/
|
|
|
|
backtrace_t *backtrace;
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2013-04-02 15:41:04 +00:00
|
|
|
/**
|
|
|
|
* Padding to make sizeof(memory_header_t) == 32
|
|
|
|
*/
|
2016-03-22 12:22:01 +00:00
|
|
|
uint32_t padding[sizeof(void*) == sizeof(uint32_t) ? 3 : 0];
|
2013-04-02 15:41:04 +00:00
|
|
|
|
2011-05-25 16:32:00 +00:00
|
|
|
/**
|
|
|
|
* Number of bytes following after the header
|
|
|
|
*/
|
2016-03-22 12:22:01 +00:00
|
|
|
uint32_t bytes;
|
2011-05-25 16:32:00 +00:00
|
|
|
|
2008-03-28 14:51:26 +00:00
|
|
|
/**
|
|
|
|
* magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
|
|
|
|
*/
|
2016-03-22 12:22:01 +00:00
|
|
|
uint32_t magic;
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2008-03-28 14:51:26 +00:00
|
|
|
}__attribute__((__packed__));
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tail appended to each allocated memory block
|
|
|
|
*/
|
|
|
|
struct memory_tail_t {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
|
|
|
|
*/
|
2016-03-22 12:22:01 +00:00
|
|
|
uint32_t magic;
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2008-03-28 14:51:26 +00:00
|
|
|
}__attribute__((__packed__));
|
2006-04-10 08:07:38 +00:00
|
|
|
|
|
|
|
/**
|
2009-09-04 11:46:09 +00:00
|
|
|
* first mem header is just a dummy to chain
|
2006-04-10 08:07:38 +00:00
|
|
|
* the others on it...
|
|
|
|
*/
|
2006-04-20 11:48:57 +00:00
|
|
|
static memory_header_t first_header = {
|
2013-04-02 11:37:06 +00:00
|
|
|
.magic = MEMORY_HEADER_MAGIC,
|
2006-04-10 08:07:38 +00:00
|
|
|
};
|
|
|
|
|
2006-04-18 07:22:20 +00:00
|
|
|
/**
|
2013-04-02 11:37:06 +00:00
|
|
|
* Spinlock to access header linked list
|
2006-04-18 07:22:20 +00:00
|
|
|
*/
|
2013-04-02 11:37:06 +00:00
|
|
|
static spinlock_t *lock;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is leak detection currently enabled?
|
|
|
|
*/
|
2018-08-08 15:06:15 +00:00
|
|
|
static bool enabled;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether to report calls to free() with memory not allocated by us
|
|
|
|
*/
|
|
|
|
static bool ignore_unknown;
|
2013-04-02 11:37:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Is leak detection disabled for the current thread?
|
|
|
|
*/
|
|
|
|
static thread_value_t *thread_disabled;
|
2006-04-10 08:07:38 +00:00
|
|
|
|
2011-05-25 16:18:21 +00:00
|
|
|
/**
|
|
|
|
* Installs the malloc hooks, enables leak detection
|
|
|
|
*/
|
2013-04-02 11:37:06 +00:00
|
|
|
static void enable_leak_detective()
|
2011-05-25 16:18:21 +00:00
|
|
|
{
|
2013-04-02 11:37:06 +00:00
|
|
|
enabled = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Uninstalls the malloc hooks, disables leak detection
|
|
|
|
*/
|
|
|
|
static void disable_leak_detective()
|
|
|
|
{
|
|
|
|
enabled = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable/Disable leak detective for the current thread
|
|
|
|
*
|
|
|
|
* @return Previous value
|
|
|
|
*/
|
|
|
|
static bool enable_thread(bool enable)
|
|
|
|
{
|
|
|
|
bool before;
|
|
|
|
|
|
|
|
before = thread_disabled->get(thread_disabled) == NULL;
|
|
|
|
thread_disabled->set(thread_disabled, enable ? NULL : (void*)TRUE);
|
|
|
|
return before;
|
|
|
|
}
|
|
|
|
|
2013-07-10 14:29:18 +00:00
|
|
|
/**
|
|
|
|
* Add a header to the beginning of the list
|
|
|
|
*/
|
|
|
|
static void add_hdr(memory_header_t *hdr)
|
|
|
|
{
|
|
|
|
lock->lock(lock);
|
|
|
|
hdr->next = first_header.next;
|
|
|
|
if (hdr->next)
|
|
|
|
{
|
|
|
|
hdr->next->previous = hdr;
|
|
|
|
}
|
|
|
|
hdr->previous = &first_header;
|
|
|
|
first_header.next = hdr;
|
|
|
|
lock->unlock(lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove a header from the list
|
|
|
|
*/
|
|
|
|
static void remove_hdr(memory_header_t *hdr)
|
|
|
|
{
|
|
|
|
lock->lock(lock);
|
|
|
|
if (hdr->next)
|
|
|
|
{
|
|
|
|
hdr->next->previous = hdr->previous;
|
|
|
|
}
|
|
|
|
hdr->previous->next = hdr->next;
|
|
|
|
lock->unlock(lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a header is in the list
|
|
|
|
*/
|
|
|
|
static bool has_hdr(memory_header_t *hdr)
|
|
|
|
{
|
|
|
|
memory_header_t *current;
|
|
|
|
bool found = FALSE;
|
|
|
|
|
|
|
|
lock->lock(lock);
|
|
|
|
for (current = &first_header; current != NULL; current = current->next)
|
|
|
|
{
|
|
|
|
if (current == hdr)
|
|
|
|
{
|
|
|
|
found = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lock->unlock(lock);
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2013-04-18 11:07:27 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copy of original default zone, with functions we call in hooks
|
|
|
|
*/
|
|
|
|
static malloc_zone_t original;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call original malloc()
|
|
|
|
*/
|
|
|
|
static void* real_malloc(size_t size)
|
|
|
|
{
|
|
|
|
return original.malloc(malloc_default_zone(), size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call original free()
|
|
|
|
*/
|
|
|
|
static void real_free(void *ptr)
|
|
|
|
{
|
|
|
|
original.free(malloc_default_zone(), ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call original realloc()
|
|
|
|
*/
|
|
|
|
static void* real_realloc(void *ptr, size_t size)
|
|
|
|
{
|
|
|
|
return original.realloc(malloc_default_zone(), ptr, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hook definition: static function with _hook suffix, takes additional zone
|
|
|
|
*/
|
|
|
|
#define HOOK(ret, name, ...) \
|
|
|
|
static ret name ## _hook(malloc_zone_t *_z, __VA_ARGS__)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* forward declaration of hooks
|
|
|
|
*/
|
|
|
|
HOOK(void*, malloc, size_t bytes);
|
|
|
|
HOOK(void*, calloc, size_t nmemb, size_t size);
|
|
|
|
HOOK(void*, valloc, size_t size);
|
|
|
|
HOOK(void, free, void *ptr);
|
|
|
|
HOOK(void*, realloc, void *old, size_t bytes);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* malloc zone size(), must consider the memory header prepended
|
|
|
|
*/
|
|
|
|
HOOK(size_t, size, const void *ptr)
|
|
|
|
{
|
|
|
|
bool before;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
if (enabled)
|
|
|
|
{
|
|
|
|
before = enable_thread(FALSE);
|
|
|
|
if (before)
|
|
|
|
{
|
|
|
|
ptr -= sizeof(memory_header_t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
size = original.size(malloc_default_zone(), ptr);
|
|
|
|
if (enabled)
|
|
|
|
{
|
|
|
|
enable_thread(before);
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Version of malloc zones we currently support
|
|
|
|
*/
|
|
|
|
#define MALLOC_ZONE_VERSION 8 /* Snow Leopard */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hook-in our malloc functions into the default zone
|
|
|
|
*/
|
|
|
|
static bool register_hooks()
|
|
|
|
{
|
2013-11-06 09:09:04 +00:00
|
|
|
static bool once = FALSE;
|
2013-04-18 11:07:27 +00:00
|
|
|
malloc_zone_t *zone;
|
|
|
|
void *page;
|
|
|
|
|
2013-11-06 09:09:04 +00:00
|
|
|
if (once)
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
once = TRUE;
|
|
|
|
|
2013-04-18 11:07:27 +00:00
|
|
|
zone = malloc_default_zone();
|
|
|
|
if (zone->version != MALLOC_ZONE_VERSION)
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "malloc zone version %d unsupported (requiring %d)",
|
|
|
|
zone->version, MALLOC_ZONE_VERSION);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
original = *zone;
|
|
|
|
|
|
|
|
page = (void*)((uintptr_t)zone / getpagesize() * getpagesize());
|
|
|
|
if (mprotect(page, getpagesize(), PROT_WRITE | PROT_READ) != 0)
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "malloc zone unprotection failed: %s", strerror(errno));
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
zone->size = size_hook;
|
|
|
|
zone->malloc = malloc_hook;
|
|
|
|
zone->calloc = calloc_hook;
|
|
|
|
zone->valloc = valloc_hook;
|
|
|
|
zone->free = free_hook;
|
|
|
|
zone->realloc = realloc_hook;
|
|
|
|
|
|
|
|
/* those other functions can be NULLed out to not use them */
|
|
|
|
zone->batch_malloc = NULL;
|
|
|
|
zone->batch_free = NULL;
|
|
|
|
zone->memalign = NULL;
|
|
|
|
zone->free_definite_size = NULL;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* !__APPLE__ */
|
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
/**
|
|
|
|
* dlsym() might do a malloc(), but we can't do one before we get the malloc()
|
|
|
|
* function pointer. Use this minimalistic malloc implementation instead.
|
|
|
|
*/
|
|
|
|
static void* malloc_for_dlsym(size_t size)
|
|
|
|
{
|
|
|
|
static char buf[1024] = {};
|
|
|
|
static size_t used = 0;
|
|
|
|
char *ptr;
|
|
|
|
|
|
|
|
/* roundup to a multiple of 32 */
|
|
|
|
size = (size - 1) / 32 * 32 + 32;
|
|
|
|
|
|
|
|
if (used + size > sizeof(buf))
|
2011-05-25 16:18:21 +00:00
|
|
|
{
|
2013-04-02 11:37:06 +00:00
|
|
|
return NULL;
|
2011-05-25 16:18:21 +00:00
|
|
|
}
|
2013-04-02 11:37:06 +00:00
|
|
|
ptr = buf + used;
|
|
|
|
used += size;
|
|
|
|
return ptr;
|
2011-05-25 16:18:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-04-02 11:37:06 +00:00
|
|
|
* Lookup a malloc function, while disabling wrappers
|
|
|
|
*/
|
|
|
|
static void* get_malloc_fn(char *name)
|
|
|
|
{
|
|
|
|
bool before = FALSE;
|
|
|
|
void *fn;
|
|
|
|
|
|
|
|
if (enabled)
|
|
|
|
{
|
|
|
|
before = enable_thread(FALSE);
|
|
|
|
}
|
|
|
|
fn = dlsym(RTLD_NEXT, name);
|
|
|
|
if (enabled)
|
|
|
|
{
|
|
|
|
enable_thread(before);
|
|
|
|
}
|
|
|
|
return fn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call original malloc()
|
|
|
|
*/
|
|
|
|
static void* real_malloc(size_t size)
|
|
|
|
{
|
|
|
|
static void* (*fn)(size_t size);
|
|
|
|
static int recursive = 0;
|
|
|
|
|
|
|
|
if (!fn)
|
|
|
|
{
|
|
|
|
/* checking recursiveness should actually be thread-specific. But as
|
|
|
|
* it is very likely that the first allocation is done before we go
|
|
|
|
* multi-threaded, we keep it simple. */
|
|
|
|
if (recursive)
|
|
|
|
{
|
|
|
|
return malloc_for_dlsym(size);
|
|
|
|
}
|
|
|
|
recursive++;
|
|
|
|
fn = get_malloc_fn("malloc");
|
|
|
|
recursive--;
|
|
|
|
}
|
|
|
|
return fn(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call original free()
|
2011-05-25 16:18:21 +00:00
|
|
|
*/
|
2013-04-02 11:37:06 +00:00
|
|
|
static void real_free(void *ptr)
|
2011-05-25 16:18:21 +00:00
|
|
|
{
|
2013-04-02 11:37:06 +00:00
|
|
|
static void (*fn)(void *ptr);
|
|
|
|
|
|
|
|
if (!fn)
|
2011-05-25 16:18:21 +00:00
|
|
|
{
|
2013-04-02 11:37:06 +00:00
|
|
|
fn = get_malloc_fn("free");
|
2011-05-25 16:18:21 +00:00
|
|
|
}
|
2013-04-02 11:37:06 +00:00
|
|
|
return fn(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Call original realloc()
|
|
|
|
*/
|
|
|
|
static void* real_realloc(void *ptr, size_t size)
|
|
|
|
{
|
|
|
|
static void* (*fn)(void *ptr, size_t size);
|
|
|
|
|
|
|
|
if (!fn)
|
|
|
|
{
|
|
|
|
fn = get_malloc_fn("realloc");
|
|
|
|
}
|
|
|
|
return fn(ptr, size);
|
2011-05-25 16:18:21 +00:00
|
|
|
}
|
|
|
|
|
2013-04-18 11:07:27 +00:00
|
|
|
/**
|
|
|
|
* Hook definition: plain function overloading existing malloc calls
|
|
|
|
*/
|
|
|
|
#define HOOK(ret, name, ...) ret name(__VA_ARGS__)
|
|
|
|
|
|
|
|
/**
|
2013-06-11 13:38:56 +00:00
|
|
|
* Hook initialization when not using hooks, resolve functions.
|
2013-04-18 11:07:27 +00:00
|
|
|
*/
|
|
|
|
static bool register_hooks()
|
|
|
|
{
|
2013-06-11 13:38:56 +00:00
|
|
|
void *buf = real_malloc(8);
|
2014-02-10 16:14:24 +00:00
|
|
|
buf = real_realloc(buf, 16);
|
2013-06-11 13:38:56 +00:00
|
|
|
real_free(buf);
|
2013-04-18 11:07:27 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* !__APPLE__ */
|
|
|
|
|
2006-06-20 10:05:56 +00:00
|
|
|
/**
|
2008-03-13 14:14:44 +00:00
|
|
|
* Leak report white list
|
2006-09-27 14:15:49 +00:00
|
|
|
*
|
2008-03-13 14:14:44 +00:00
|
|
|
* List of functions using static allocation buffers or should be suppressed
|
2009-09-04 11:46:09 +00:00
|
|
|
* otherwise on leak report.
|
2006-06-20 10:05:56 +00:00
|
|
|
*/
|
2016-09-13 15:28:21 +00:00
|
|
|
static char *whitelist[] = {
|
2008-11-05 13:58:19 +00:00
|
|
|
/* backtraces, including own */
|
|
|
|
"backtrace_create",
|
2014-02-21 16:31:14 +00:00
|
|
|
"strerror_safe",
|
2008-03-28 13:16:36 +00:00
|
|
|
/* pthread stuff */
|
2008-03-13 14:14:44 +00:00
|
|
|
"pthread_create",
|
|
|
|
"pthread_setspecific",
|
2008-12-08 15:27:24 +00:00
|
|
|
"__pthread_setspecific",
|
2008-03-28 13:16:36 +00:00
|
|
|
/* glibc functions */
|
2008-03-13 14:14:44 +00:00
|
|
|
"inet_ntoa",
|
|
|
|
"strerror",
|
2010-05-02 19:13:10 +00:00
|
|
|
"getprotobyname",
|
2008-03-13 14:14:44 +00:00
|
|
|
"getprotobynumber",
|
|
|
|
"getservbyport",
|
|
|
|
"getservbyname",
|
2011-09-10 08:54:33 +00:00
|
|
|
"gethostbyname",
|
2009-08-13 14:47:57 +00:00
|
|
|
"gethostbyname2",
|
2008-06-06 15:05:54 +00:00
|
|
|
"gethostbyname_r",
|
|
|
|
"gethostbyname2_r",
|
2009-08-13 14:47:57 +00:00
|
|
|
"getnetbyname",
|
2008-05-08 10:58:04 +00:00
|
|
|
"getpwnam_r",
|
|
|
|
"getgrnam_r",
|
2008-03-13 14:14:44 +00:00
|
|
|
"register_printf_function",
|
2009-11-17 15:51:57 +00:00
|
|
|
"register_printf_specifier",
|
2008-03-13 14:14:44 +00:00
|
|
|
"syslog",
|
|
|
|
"vsyslog",
|
2010-07-14 15:40:06 +00:00
|
|
|
"__syslog_chk",
|
|
|
|
"__vsyslog_chk",
|
2016-06-15 09:21:16 +00:00
|
|
|
"__fprintf_chk",
|
2008-03-13 14:14:44 +00:00
|
|
|
"getaddrinfo",
|
|
|
|
"setlocale",
|
2010-07-19 07:43:11 +00:00
|
|
|
"getpass",
|
2011-09-10 08:54:33 +00:00
|
|
|
"getpwent_r",
|
|
|
|
"setpwent",
|
|
|
|
"endpwent",
|
2012-08-09 12:38:29 +00:00
|
|
|
"getspnam_r",
|
2013-03-03 08:04:49 +00:00
|
|
|
"getpwuid_r",
|
|
|
|
"initgroups",
|
2013-08-28 20:51:17 +00:00
|
|
|
"tzset",
|
2016-04-24 21:34:44 +00:00
|
|
|
"_IO_file_doallocate",
|
2008-03-28 13:16:36 +00:00
|
|
|
/* ignore dlopen, as we do not dlclose to get proper leak reports */
|
|
|
|
"dlopen",
|
2008-05-15 14:01:26 +00:00
|
|
|
"dlerror",
|
2008-08-21 12:10:07 +00:00
|
|
|
"dlclose",
|
2011-02-18 15:07:16 +00:00
|
|
|
"dlsym",
|
2008-03-28 13:16:36 +00:00
|
|
|
/* mysql functions */
|
2008-03-13 14:14:44 +00:00
|
|
|
"mysql_init_character_set",
|
|
|
|
"init_client_errs",
|
|
|
|
"my_thread_init",
|
2008-03-28 13:16:36 +00:00
|
|
|
/* fastcgi library */
|
2008-03-27 11:42:35 +00:00
|
|
|
"FCGX_Init",
|
2008-03-28 13:16:36 +00:00
|
|
|
/* libxml */
|
|
|
|
"xmlInitCharEncodingHandlers",
|
|
|
|
"xmlInitParser",
|
|
|
|
"xmlInitParserCtxt",
|
2009-09-27 21:07:21 +00:00
|
|
|
/* libcurl */
|
|
|
|
"Curl_client_write",
|
2016-09-13 13:38:43 +00:00
|
|
|
/* libsoup */
|
2019-12-12 16:29:08 +00:00
|
|
|
"soup_add_timeout",
|
|
|
|
"soup_headers_parse_response",
|
2016-09-13 13:38:43 +00:00
|
|
|
"soup_message_headers_append",
|
|
|
|
"soup_message_headers_clear",
|
|
|
|
"soup_message_headers_get_list",
|
|
|
|
"soup_message_headers_get_one",
|
|
|
|
"soup_session_abort",
|
|
|
|
"soup_session_get_type",
|
2019-12-12 16:29:08 +00:00
|
|
|
"soup_session_remove_feature",
|
2016-09-13 10:27:33 +00:00
|
|
|
/* libldap */
|
|
|
|
"ldap_int_initialize",
|
2008-03-28 13:16:36 +00:00
|
|
|
/* ClearSilver */
|
|
|
|
"nerr_init",
|
2009-06-04 13:49:19 +00:00
|
|
|
/* libgcrypt */
|
2015-04-13 10:02:07 +00:00
|
|
|
"gcrypt_plugin_create",
|
2009-06-04 13:49:19 +00:00
|
|
|
"gcry_control",
|
|
|
|
"gcry_check_version",
|
2009-06-04 19:27:31 +00:00
|
|
|
"gcry_randomize",
|
|
|
|
"gcry_create_nonce",
|
2013-06-20 08:06:07 +00:00
|
|
|
/* OpenSSL: These are needed for unit-tests only, the openssl plugin
|
|
|
|
* does properly clean up any memory during destroy(). */
|
|
|
|
"ECDSA_do_sign_ex",
|
|
|
|
"ECDSA_verify",
|
|
|
|
"RSA_new_method",
|
2016-06-27 15:44:57 +00:00
|
|
|
/* OpenSSL 1.1.0 does not cleanup anymore until the library is unloaded */
|
|
|
|
"OPENSSL_init_crypto",
|
|
|
|
"CRYPTO_THREAD_lock_new",
|
|
|
|
"ERR_add_error_data",
|
2018-09-14 15:13:09 +00:00
|
|
|
"ERR_set_mark",
|
|
|
|
"ENGINE_load_builtin_engines",
|
|
|
|
"OPENSSL_load_builtin_modules",
|
|
|
|
"CONF_modules_load_file",
|
|
|
|
"CONF_module_add",
|
2018-11-16 09:57:50 +00:00
|
|
|
"RAND_DRBG_bytes",
|
|
|
|
"RAND_DRBG_generate",
|
|
|
|
"RAND_DRBG_get0_master",
|
|
|
|
"RAND_DRBG_get0_private",
|
|
|
|
"RAND_DRBG_get0_public",
|
2020-08-24 14:20:43 +00:00
|
|
|
/* We get this via libcurl and OpenSSL 1.1.1 */
|
|
|
|
"CRYPTO_get_ex_new_index",
|
2014-08-28 09:10:21 +00:00
|
|
|
/* OpenSSL libssl */
|
|
|
|
"SSL_COMP_get_compression_methods",
|
2010-07-14 15:42:18 +00:00
|
|
|
/* NSPR */
|
|
|
|
"PR_CallOnce",
|
2010-11-04 13:56:34 +00:00
|
|
|
/* libapr */
|
|
|
|
"apr_pool_create_ex",
|
2011-01-17 12:23:00 +00:00
|
|
|
/* glib */
|
2016-09-13 13:38:43 +00:00
|
|
|
"g_output_stream_write",
|
|
|
|
"g_resolver_lookup_by_name",
|
|
|
|
"g_signal_connect_data",
|
|
|
|
"g_socket_connection_factory_lookup_type",
|
2011-01-17 12:23:00 +00:00
|
|
|
"g_type_init_with_debug_flags",
|
|
|
|
"g_type_register_static",
|
|
|
|
"g_type_class_ref",
|
|
|
|
"g_type_create_instance",
|
|
|
|
"g_type_add_interface_static",
|
|
|
|
"g_type_interface_add_prerequisite",
|
2016-09-13 13:38:43 +00:00
|
|
|
"g_private_set",
|
|
|
|
"g_queue_pop_tail",
|
2011-01-17 12:23:00 +00:00
|
|
|
/* libgpg */
|
|
|
|
"gpg_err_init",
|
2011-01-17 12:32:45 +00:00
|
|
|
/* gnutls */
|
|
|
|
"gnutls_global_init",
|
2016-09-13 14:28:01 +00:00
|
|
|
/* Ada runtime */
|
|
|
|
"system__tasking__initialize",
|
|
|
|
"system__tasking__initialization__abort_defer",
|
|
|
|
"system__tasking__stages__create_task",
|
2020-08-24 14:20:43 +00:00
|
|
|
"system__task_primitives__operations__register_foreign_thread__2",
|
2016-09-13 15:28:21 +00:00
|
|
|
/* in case external threads call into our code */
|
|
|
|
"thread_current_id",
|
2017-05-28 18:07:20 +00:00
|
|
|
/* FHH IMCs and IMVs */
|
|
|
|
"TNC_IMC_NotifyConnectionChange",
|
|
|
|
"TNC_IMV_NotifyConnectionChange",
|
2018-08-08 09:41:36 +00:00
|
|
|
/* Botan */
|
|
|
|
"botan_public_key_load",
|
2019-07-02 08:47:54 +00:00
|
|
|
"botan_privkey_create",
|
2018-08-08 09:41:36 +00:00
|
|
|
"botan_privkey_load_ecdh",
|
2018-10-22 15:55:13 +00:00
|
|
|
"botan_privkey_load",
|
2006-06-20 10:05:56 +00:00
|
|
|
};
|
|
|
|
|
2013-04-02 13:47:55 +00:00
|
|
|
/**
|
|
|
|
* Some functions are hard to whitelist, as they don't use a symbol directly.
|
|
|
|
* Use some static initialization to suppress them on leak reports
|
|
|
|
*/
|
|
|
|
static void init_static_allocations()
|
|
|
|
{
|
2013-11-06 09:10:49 +00:00
|
|
|
struct tm tm;
|
|
|
|
time_t t = 0;
|
|
|
|
|
2013-04-02 13:47:55 +00:00
|
|
|
tzset();
|
2013-11-06 09:10:49 +00:00
|
|
|
gmtime_r(&t, &tm);
|
|
|
|
localtime_r(&t, &tm);
|
2013-04-02 13:47:55 +00:00
|
|
|
}
|
2006-04-10 08:07:38 +00:00
|
|
|
|
2011-04-29 12:53:24 +00:00
|
|
|
/**
|
|
|
|
* Hashtable hash function
|
|
|
|
*/
|
|
|
|
static u_int hash(backtrace_t *key)
|
|
|
|
{
|
|
|
|
enumerator_t *enumerator;
|
|
|
|
void *addr;
|
|
|
|
u_int hash = 0;
|
|
|
|
|
|
|
|
enumerator = key->create_frame_enumerator(key);
|
|
|
|
while (enumerator->enumerate(enumerator, &addr))
|
|
|
|
{
|
|
|
|
hash = chunk_hash_inc(chunk_from_thing(addr), hash);
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hashtable equals function
|
|
|
|
*/
|
|
|
|
static bool equals(backtrace_t *a, backtrace_t *b)
|
|
|
|
{
|
|
|
|
return a->equals(a, b);
|
|
|
|
}
|
|
|
|
|
2011-05-25 16:18:21 +00:00
|
|
|
/**
|
|
|
|
* Summarize and print backtraces
|
|
|
|
*/
|
|
|
|
static int print_traces(private_leak_detective_t *this,
|
2013-10-16 08:37:38 +00:00
|
|
|
leak_detective_report_cb_t cb, void *user,
|
|
|
|
int thresh, int thresh_count,
|
2013-07-12 17:57:17 +00:00
|
|
|
bool detailed, int *whitelisted, size_t *sum)
|
2011-04-29 12:53:24 +00:00
|
|
|
{
|
2011-05-25 16:18:21 +00:00
|
|
|
int leaks = 0;
|
2011-04-29 12:53:24 +00:00
|
|
|
memory_header_t *hdr;
|
|
|
|
enumerator_t *enumerator;
|
2018-10-05 09:23:36 +00:00
|
|
|
hashtable_t *entries, *ignored = NULL;
|
|
|
|
backtrace_t *bt;
|
2011-04-29 12:53:24 +00:00
|
|
|
struct {
|
|
|
|
/** associated backtrace */
|
|
|
|
backtrace_t *backtrace;
|
|
|
|
/** total size of all allocations */
|
|
|
|
size_t bytes;
|
|
|
|
/** number of allocations */
|
|
|
|
u_int count;
|
|
|
|
} *entry;
|
2013-04-02 11:37:06 +00:00
|
|
|
bool before;
|
2011-04-29 12:53:24 +00:00
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
before = enable_thread(FALSE);
|
2011-04-29 12:53:24 +00:00
|
|
|
|
|
|
|
entries = hashtable_create((hashtable_hash_t)hash,
|
|
|
|
(hashtable_equals_t)equals, 1024);
|
2018-10-05 09:23:36 +00:00
|
|
|
if (whitelisted)
|
|
|
|
{
|
|
|
|
ignored = hashtable_create((hashtable_hash_t)hash,
|
|
|
|
(hashtable_equals_t)equals, 1024);
|
|
|
|
}
|
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
lock->lock(lock);
|
2011-04-29 12:53:24 +00:00
|
|
|
for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
|
|
|
|
{
|
2018-10-05 09:23:36 +00:00
|
|
|
if (whitelisted)
|
2011-05-25 16:18:21 +00:00
|
|
|
{
|
2018-10-05 09:23:36 +00:00
|
|
|
bt = ignored->get(ignored, hdr->backtrace);
|
|
|
|
if (!bt)
|
|
|
|
{
|
|
|
|
if (hdr->backtrace->contains_function(hdr->backtrace, whitelist,
|
|
|
|
countof(whitelist)))
|
|
|
|
{
|
|
|
|
bt = hdr->backtrace;
|
|
|
|
ignored->put(ignored, bt, bt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bt)
|
|
|
|
{
|
|
|
|
(*whitelisted)++;
|
|
|
|
continue;
|
|
|
|
}
|
2011-05-25 16:18:21 +00:00
|
|
|
}
|
2011-04-29 12:53:24 +00:00
|
|
|
entry = entries->get(entries, hdr->backtrace);
|
|
|
|
if (entry)
|
|
|
|
{
|
|
|
|
entry->bytes += hdr->bytes;
|
|
|
|
entry->count++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
INIT(entry,
|
2013-07-10 15:15:00 +00:00
|
|
|
.backtrace = hdr->backtrace->clone(hdr->backtrace),
|
2011-04-29 12:53:24 +00:00
|
|
|
.bytes = hdr->bytes,
|
|
|
|
.count = 1,
|
|
|
|
);
|
2013-07-10 15:15:00 +00:00
|
|
|
entries->put(entries, entry->backtrace, entry);
|
2011-04-29 12:53:24 +00:00
|
|
|
}
|
2013-07-12 17:57:17 +00:00
|
|
|
if (sum)
|
|
|
|
{
|
|
|
|
*sum += hdr->bytes;
|
|
|
|
}
|
2011-05-25 16:18:21 +00:00
|
|
|
leaks++;
|
2011-04-29 12:53:24 +00:00
|
|
|
}
|
2013-04-02 11:37:06 +00:00
|
|
|
lock->unlock(lock);
|
2018-10-05 09:23:36 +00:00
|
|
|
DESTROY_IF(ignored);
|
2013-10-16 08:37:38 +00:00
|
|
|
|
2011-04-29 12:53:24 +00:00
|
|
|
enumerator = entries->create_enumerator(entries);
|
|
|
|
while (enumerator->enumerate(enumerator, NULL, &entry))
|
|
|
|
{
|
2013-10-16 08:37:38 +00:00
|
|
|
if (cb)
|
2011-04-29 12:53:24 +00:00
|
|
|
{
|
2013-10-16 08:37:38 +00:00
|
|
|
if (!thresh || entry->bytes >= thresh)
|
|
|
|
{
|
|
|
|
if (!thresh_count || entry->count >= thresh_count)
|
|
|
|
{
|
2015-04-20 09:19:52 +00:00
|
|
|
cb(user, entry->count, entry->bytes, entry->backtrace,
|
|
|
|
detailed);
|
2013-10-16 08:37:38 +00:00
|
|
|
}
|
|
|
|
}
|
2011-04-29 12:53:24 +00:00
|
|
|
}
|
2013-07-10 15:15:00 +00:00
|
|
|
entry->backtrace->destroy(entry->backtrace);
|
2011-04-29 12:53:24 +00:00
|
|
|
free(entry);
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
entries->destroy(entries);
|
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
enable_thread(before);
|
2011-05-25 16:18:21 +00:00
|
|
|
return leaks;
|
|
|
|
}
|
|
|
|
|
|
|
|
METHOD(leak_detective_t, report, void,
|
|
|
|
private_leak_detective_t *this, bool detailed)
|
|
|
|
{
|
|
|
|
if (lib->leak_detective)
|
|
|
|
{
|
2013-06-04 14:21:48 +00:00
|
|
|
int leaks, whitelisted = 0;
|
2013-07-12 17:57:17 +00:00
|
|
|
size_t sum = 0;
|
2011-05-25 16:18:21 +00:00
|
|
|
|
2013-10-16 08:37:38 +00:00
|
|
|
leaks = print_traces(this, this->report_cb, this->report_data,
|
|
|
|
0, 0, detailed, &whitelisted, &sum);
|
|
|
|
if (this->report_scb)
|
2011-05-25 16:18:21 +00:00
|
|
|
{
|
2013-10-16 08:37:38 +00:00
|
|
|
this->report_scb(this->report_data, leaks, sum, whitelisted);
|
2011-05-25 16:18:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-16 08:37:38 +00:00
|
|
|
METHOD(leak_detective_t, set_report_cb, void,
|
|
|
|
private_leak_detective_t *this, leak_detective_report_cb_t cb,
|
|
|
|
leak_detective_summary_cb_t scb, void *user)
|
|
|
|
{
|
|
|
|
this->report_cb = cb;
|
|
|
|
this->report_scb = scb;
|
|
|
|
this->report_data = user;
|
|
|
|
}
|
|
|
|
|
2013-06-04 14:21:48 +00:00
|
|
|
METHOD(leak_detective_t, leaks, int,
|
|
|
|
private_leak_detective_t *this)
|
|
|
|
{
|
2013-10-16 08:37:38 +00:00
|
|
|
int whitelisted = 0;
|
2013-06-04 14:21:48 +00:00
|
|
|
|
2013-10-16 08:37:38 +00:00
|
|
|
return print_traces(this, NULL, NULL, 0, 0, FALSE, &whitelisted, NULL);
|
2013-06-04 14:21:48 +00:00
|
|
|
}
|
|
|
|
|
2012-07-11 15:08:30 +00:00
|
|
|
METHOD(leak_detective_t, set_state, bool,
|
|
|
|
private_leak_detective_t *this, bool enable)
|
|
|
|
{
|
2013-07-10 15:16:49 +00:00
|
|
|
return enable_thread(enable);
|
2012-07-11 15:08:30 +00:00
|
|
|
}
|
|
|
|
|
2011-05-25 16:18:21 +00:00
|
|
|
METHOD(leak_detective_t, usage, void,
|
2013-10-16 08:37:38 +00:00
|
|
|
private_leak_detective_t *this, leak_detective_report_cb_t cb,
|
|
|
|
leak_detective_summary_cb_t scb, void *user)
|
2011-05-25 16:18:21 +00:00
|
|
|
{
|
|
|
|
bool detailed;
|
2013-10-16 08:37:38 +00:00
|
|
|
int thresh, thresh_count, leaks, whitelisted = 0;
|
2013-07-12 17:57:17 +00:00
|
|
|
size_t sum = 0;
|
2011-05-25 16:18:21 +00:00
|
|
|
|
|
|
|
thresh = lib->settings->get_int(lib->settings,
|
2014-01-28 15:38:06 +00:00
|
|
|
"%s.leak_detective.usage_threshold", 10240, lib->ns);
|
2013-07-10 15:27:31 +00:00
|
|
|
thresh_count = lib->settings->get_int(lib->settings,
|
2014-01-28 15:38:06 +00:00
|
|
|
"%s.leak_detective.usage_threshold_count", 0, lib->ns);
|
2011-05-25 16:18:21 +00:00
|
|
|
detailed = lib->settings->get_bool(lib->settings,
|
2014-01-28 15:38:06 +00:00
|
|
|
"%s.leak_detective.detailed", TRUE, lib->ns);
|
2011-05-25 16:18:21 +00:00
|
|
|
|
2013-10-16 08:37:38 +00:00
|
|
|
leaks = print_traces(this, cb, user, thresh, thresh_count,
|
|
|
|
detailed, &whitelisted, &sum);
|
|
|
|
if (scb)
|
|
|
|
{
|
|
|
|
scb(user, leaks, sum, whitelisted);
|
|
|
|
}
|
2011-04-29 12:53:24 +00:00
|
|
|
}
|
|
|
|
|
2006-04-10 08:07:38 +00:00
|
|
|
/**
|
2013-04-02 11:37:06 +00:00
|
|
|
* Wrapped malloc() function
|
2006-04-10 08:07:38 +00:00
|
|
|
*/
|
2013-04-18 11:07:27 +00:00
|
|
|
HOOK(void*, malloc, size_t bytes)
|
2006-04-10 08:07:38 +00:00
|
|
|
{
|
|
|
|
memory_header_t *hdr;
|
2008-03-28 14:51:26 +00:00
|
|
|
memory_tail_t *tail;
|
2013-04-02 11:37:06 +00:00
|
|
|
bool before;
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
if (!enabled || thread_disabled->get(thread_disabled))
|
|
|
|
{
|
|
|
|
return real_malloc(bytes);
|
|
|
|
}
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
|
2008-03-28 14:51:26 +00:00
|
|
|
tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
|
2006-06-20 10:05:56 +00:00
|
|
|
/* set to something which causes crashes */
|
2008-03-28 14:51:26 +00:00
|
|
|
memset(hdr, MEMORY_ALLOC_PATTERN,
|
|
|
|
sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
before = enable_thread(FALSE);
|
|
|
|
hdr->backtrace = backtrace_create(2);
|
|
|
|
enable_thread(before);
|
|
|
|
|
2006-04-10 08:07:38 +00:00
|
|
|
hdr->magic = MEMORY_HEADER_MAGIC;
|
|
|
|
hdr->bytes = bytes;
|
2008-03-28 14:51:26 +00:00
|
|
|
tail->magic = MEMORY_TAIL_MAGIC;
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2013-07-10 14:29:18 +00:00
|
|
|
add_hdr(hdr);
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2006-04-10 08:07:38 +00:00
|
|
|
return hdr + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-04-02 11:37:06 +00:00
|
|
|
* Wrapped calloc() function
|
2006-04-10 08:07:38 +00:00
|
|
|
*/
|
2013-04-18 11:07:27 +00:00
|
|
|
HOOK(void*, calloc, size_t nmemb, size_t size)
|
2013-04-02 11:37:06 +00:00
|
|
|
{
|
|
|
|
void *ptr;
|
2016-06-15 09:22:04 +00:00
|
|
|
volatile size_t total;
|
2013-04-02 11:37:06 +00:00
|
|
|
|
2016-06-15 09:22:04 +00:00
|
|
|
total = nmemb * size;
|
|
|
|
ptr = malloc(total);
|
|
|
|
memset(ptr, 0, total);
|
2013-04-02 11:37:06 +00:00
|
|
|
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
2013-04-18 11:07:27 +00:00
|
|
|
/**
|
|
|
|
* Wrapped valloc(), TODO: currently not supported
|
|
|
|
*/
|
|
|
|
HOOK(void*, valloc, size_t size)
|
|
|
|
{
|
|
|
|
DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
/**
|
|
|
|
* Wrapped free() function
|
|
|
|
*/
|
2013-04-18 11:07:27 +00:00
|
|
|
HOOK(void, free, void *ptr)
|
2006-04-10 08:07:38 +00:00
|
|
|
{
|
2013-07-10 14:29:18 +00:00
|
|
|
memory_header_t *hdr;
|
2008-03-28 14:51:26 +00:00
|
|
|
memory_tail_t *tail;
|
2009-09-04 12:50:23 +00:00
|
|
|
backtrace_t *backtrace;
|
2013-07-10 14:29:18 +00:00
|
|
|
bool before;
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
if (!enabled || thread_disabled->get(thread_disabled))
|
|
|
|
{
|
2016-06-27 16:04:39 +00:00
|
|
|
/* after deinitialization we might have to free stuff we allocated
|
|
|
|
* while we were enabled */
|
|
|
|
if (!first_header.magic && ptr)
|
|
|
|
{
|
|
|
|
hdr = ptr - sizeof(memory_header_t);
|
|
|
|
tail = ptr + hdr->bytes;
|
|
|
|
if (hdr->magic == MEMORY_HEADER_MAGIC &&
|
|
|
|
tail->magic == MEMORY_TAIL_MAGIC)
|
|
|
|
{
|
|
|
|
ptr = hdr;
|
|
|
|
}
|
|
|
|
}
|
2013-04-02 11:37:06 +00:00
|
|
|
real_free(ptr);
|
|
|
|
return;
|
|
|
|
}
|
2006-04-10 08:07:38 +00:00
|
|
|
/* allow freeing of NULL */
|
2018-08-08 15:06:15 +00:00
|
|
|
if (!ptr)
|
2006-04-10 08:07:38 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2008-03-28 14:51:26 +00:00
|
|
|
hdr = ptr - sizeof(memory_header_t);
|
|
|
|
tail = ptr + hdr->bytes;
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
before = enable_thread(FALSE);
|
2008-04-03 10:22:17 +00:00
|
|
|
if (hdr->magic != MEMORY_HEADER_MAGIC ||
|
|
|
|
tail->magic != MEMORY_TAIL_MAGIC)
|
2006-04-10 08:07:38 +00:00
|
|
|
{
|
2018-08-08 15:06:15 +00:00
|
|
|
bool bt = TRUE;
|
|
|
|
|
|
|
|
/* check if memory appears to be allocated by our hooks */
|
2013-07-10 14:29:18 +00:00
|
|
|
if (has_hdr(hdr))
|
2009-10-05 08:49:10 +00:00
|
|
|
{
|
|
|
|
fprintf(stderr, "freeing corrupted memory (%p): "
|
2018-08-08 15:06:15 +00:00
|
|
|
"%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");
|
|
|
|
}
|
2009-10-05 08:49:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-08-08 15:06:15 +00:00
|
|
|
/* 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);
|
2009-10-05 08:49:10 +00:00
|
|
|
}
|
2006-04-10 08:07:38 +00:00
|
|
|
}
|
2008-04-03 10:22:17 +00:00
|
|
|
else
|
2008-03-28 14:51:26 +00:00
|
|
|
{
|
2013-07-10 14:29:18 +00:00
|
|
|
remove_hdr(hdr);
|
2013-04-02 11:37:06 +00:00
|
|
|
|
2008-11-05 13:58:19 +00:00
|
|
|
hdr->backtrace->destroy(hdr->backtrace);
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2018-08-08 15:06:15 +00:00
|
|
|
/* set mem to something remarkable */
|
2009-10-05 08:49:10 +00:00
|
|
|
memset(hdr, MEMORY_FREE_PATTERN,
|
|
|
|
sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
|
2008-04-03 10:22:17 +00:00
|
|
|
}
|
2018-08-08 15:06:15 +00:00
|
|
|
real_free(hdr);
|
2013-04-02 11:37:06 +00:00
|
|
|
enable_thread(before);
|
2006-04-10 08:07:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-04-02 11:37:06 +00:00
|
|
|
* Wrapped realloc() function
|
2006-04-10 08:07:38 +00:00
|
|
|
*/
|
2013-04-18 11:07:27 +00:00
|
|
|
HOOK(void*, realloc, void *old, size_t bytes)
|
2006-04-10 08:07:38 +00:00
|
|
|
{
|
2006-06-07 13:22:38 +00:00
|
|
|
memory_header_t *hdr;
|
2008-03-28 14:51:26 +00:00
|
|
|
memory_tail_t *tail;
|
2008-11-05 13:58:19 +00:00
|
|
|
backtrace_t *backtrace;
|
2018-08-08 15:06:15 +00:00
|
|
|
bool before, have_backtrace = TRUE;
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
if (!enabled || thread_disabled->get(thread_disabled))
|
|
|
|
{
|
|
|
|
return real_realloc(old, bytes);
|
|
|
|
}
|
2006-04-10 08:07:38 +00:00
|
|
|
/* allow reallocation of NULL */
|
2018-08-08 15:06:15 +00:00
|
|
|
if (!old)
|
2006-04-10 08:07:38 +00:00
|
|
|
{
|
2013-04-02 11:37:06 +00:00
|
|
|
return malloc(bytes);
|
2006-04-10 08:07:38 +00:00
|
|
|
}
|
2013-07-12 17:58:02 +00:00
|
|
|
/* handle zero size as a free() */
|
2018-08-08 15:06:15 +00:00
|
|
|
if (!bytes)
|
2013-07-12 17:58:02 +00:00
|
|
|
{
|
|
|
|
free(old);
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2006-06-07 13:22:38 +00:00
|
|
|
hdr = old - sizeof(memory_header_t);
|
2008-03-28 14:51:26 +00:00
|
|
|
tail = old + hdr->bytes;
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2018-08-08 15:06:15 +00:00
|
|
|
before = enable_thread(FALSE);
|
2008-04-03 10:22:17 +00:00
|
|
|
if (hdr->magic != MEMORY_HEADER_MAGIC ||
|
|
|
|
tail->magic != MEMORY_TAIL_MAGIC)
|
2008-03-28 14:51:26 +00:00
|
|
|
{
|
2018-08-08 15:06:15 +00:00
|
|
|
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);
|
|
|
|
}
|
2008-03-28 14:51:26 +00:00
|
|
|
}
|
2012-07-12 07:26:33 +00:00
|
|
|
else
|
|
|
|
{
|
2018-08-08 15:06:15 +00:00
|
|
|
remove_hdr(hdr);
|
2012-07-12 07:26:33 +00:00
|
|
|
/* clear tail magic, allocate, set tail magic */
|
|
|
|
memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
|
|
|
|
}
|
2018-08-08 15:06:15 +00:00
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
hdr = real_realloc(hdr,
|
|
|
|
sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
|
2008-03-28 14:51:26 +00:00
|
|
|
tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
|
|
|
|
tail->magic = MEMORY_TAIL_MAGIC;
|
2008-04-03 10:22:17 +00:00
|
|
|
|
2006-06-07 13:22:38 +00:00
|
|
|
/* update statistics */
|
|
|
|
hdr->bytes = bytes;
|
2013-04-02 11:37:06 +00:00
|
|
|
|
2018-08-08 15:06:15 +00:00
|
|
|
if (have_backtrace)
|
|
|
|
{
|
|
|
|
hdr->backtrace->destroy(hdr->backtrace);
|
|
|
|
}
|
2012-07-12 07:27:20 +00:00
|
|
|
hdr->backtrace = backtrace_create(2);
|
2013-04-02 11:37:06 +00:00
|
|
|
enable_thread(before);
|
2008-04-03 10:22:17 +00:00
|
|
|
|
2013-07-10 14:29:18 +00:00
|
|
|
add_hdr(hdr);
|
2013-04-02 11:37:06 +00:00
|
|
|
|
2006-06-07 13:22:38 +00:00
|
|
|
return hdr + 1;
|
2006-04-10 08:07:38 +00:00
|
|
|
}
|
|
|
|
|
2011-04-29 12:00:45 +00:00
|
|
|
METHOD(leak_detective_t, destroy, void,
|
|
|
|
private_leak_detective_t *this)
|
2006-04-10 08:07:38 +00:00
|
|
|
{
|
2013-04-02 11:37:06 +00:00
|
|
|
disable_leak_detective();
|
|
|
|
lock->destroy(lock);
|
|
|
|
thread_disabled->destroy(thread_disabled);
|
2008-03-13 14:14:44 +00:00
|
|
|
free(this);
|
2016-06-27 16:04:39 +00:00
|
|
|
first_header.magic = 0;
|
2013-10-16 09:16:41 +00:00
|
|
|
first_header.next = NULL;
|
2006-04-20 11:48:57 +00:00
|
|
|
}
|
|
|
|
|
2008-03-13 14:14:44 +00:00
|
|
|
/*
|
|
|
|
* see header file
|
2006-09-21 07:03:21 +00:00
|
|
|
*/
|
2008-03-13 14:14:44 +00:00
|
|
|
leak_detective_t *leak_detective_create()
|
2006-09-21 07:03:21 +00:00
|
|
|
{
|
2011-04-29 12:00:45 +00:00
|
|
|
private_leak_detective_t *this;
|
|
|
|
|
|
|
|
INIT(this,
|
|
|
|
.public = {
|
|
|
|
.report = _report,
|
2013-10-16 08:37:38 +00:00
|
|
|
.set_report_cb = _set_report_cb,
|
2011-04-29 12:53:24 +00:00
|
|
|
.usage = _usage,
|
2013-10-16 08:37:38 +00:00
|
|
|
.leaks = _leaks,
|
2012-07-11 15:08:30 +00:00
|
|
|
.set_state = _set_state,
|
2011-04-29 12:00:45 +00:00
|
|
|
.destroy = _destroy,
|
|
|
|
},
|
|
|
|
);
|
2009-09-04 11:46:09 +00:00
|
|
|
|
2014-04-01 08:30:13 +00:00
|
|
|
if (getenv("LEAK_DETECTIVE_DISABLE") != NULL)
|
|
|
|
{
|
|
|
|
free(this);
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-08-08 15:06:15 +00:00
|
|
|
ignore_unknown = getenv("LEAK_DETECTIVE_IGNORE_UNKNOWN") != NULL;
|
2014-04-01 08:30:13 +00:00
|
|
|
|
2013-04-02 11:37:06 +00:00
|
|
|
lock = spinlock_create();
|
|
|
|
thread_disabled = thread_value_create(NULL);
|
|
|
|
|
2013-04-02 13:47:55 +00:00
|
|
|
init_static_allocations();
|
|
|
|
|
2014-04-01 08:30:13 +00:00
|
|
|
if (register_hooks())
|
2006-09-21 07:03:21 +00:00
|
|
|
{
|
2014-04-01 08:30:13 +00:00
|
|
|
enable_leak_detective();
|
2006-09-21 07:03:21 +00:00
|
|
|
}
|
2008-03-13 14:14:44 +00:00
|
|
|
return &this->public;
|
2006-09-21 07:03:21 +00:00
|
|
|
}
|