diff options
Diffstat (limited to 'malloc/malloc-debug.c')
-rw-r--r-- | malloc/malloc-debug.c | 173 |
1 files changed, 153 insertions, 20 deletions
diff --git a/malloc/malloc-debug.c b/malloc/malloc-debug.c index 41dfcd3369..7c3a1e26b5 100644 --- a/malloc/malloc-debug.c +++ b/malloc/malloc-debug.c @@ -43,14 +43,96 @@ DEBUG_FN(valloc); DEBUG_FN(pvalloc); DEBUG_FN(calloc); -extern void (*__free_hook) (void *, const void *); -compat_symbol_reference (libc, __free_hook, __free_hook, GLIBC_2_0); -extern void * (*__malloc_hook) (size_t, const void *); -compat_symbol_reference (libc, __malloc_hook, __malloc_hook, GLIBC_2_0); -extern void * (*__realloc_hook) (void *, size_t, const void *); -compat_symbol_reference (libc, __realloc_hook, __realloc_hook, GLIBC_2_0); -extern void * (*__memalign_hook) (size_t, size_t, const void *); -compat_symbol_reference (libc, __memalign_hook, __memalign_hook, GLIBC_2_0); +static int debug_initialized = -1; + +enum malloc_debug_hooks +{ + MALLOC_NONE_HOOK = 0, + MALLOC_MCHECK_HOOK = 1 << 0, /* mcheck() */ +}; +static unsigned __malloc_debugging_hooks; + +static __always_inline bool +__is_malloc_debug_enabled (enum malloc_debug_hooks flag) +{ + return __malloc_debugging_hooks & flag; +} + +static __always_inline void +__malloc_debug_enable (enum malloc_debug_hooks flag) +{ + __malloc_debugging_hooks |= flag; +} + +static __always_inline void +__malloc_debug_disable (enum malloc_debug_hooks flag) +{ + __malloc_debugging_hooks &= ~flag; +} + +#include "mcheck.c" + +extern void (*__malloc_initialize_hook) (void); +compat_symbol_reference (libc, __malloc_initialize_hook, + __malloc_initialize_hook, GLIBC_2_0); + +static void *malloc_hook_ini (size_t, const void *) __THROW; +static void *realloc_hook_ini (void *, size_t, const void *) __THROW; +static void *memalign_hook_ini (size_t, size_t, const void *) __THROW; + +void (*__free_hook) (void *, const void *) = NULL; +void *(*__malloc_hook) (size_t, const void *) = malloc_hook_ini; +void *(*__realloc_hook) (void *, size_t, const void *) = realloc_hook_ini; +void *(*__memalign_hook) (size_t, size_t, const void *) = memalign_hook_ini; + +/* Hooks for debugging versions. The initial hooks just call the + initialization routine, then do the normal work. */ + +/* These hooks will get executed only through the interposed allocator + functions in libc_malloc_debug.so. This means that the calls to malloc, + realloc, etc. will lead back into the interposed functions, which is what we + want. + + These initial hooks are assumed to be called in a single-threaded context, + so it is safe to reset all hooks at once upon initialization. */ + +static void +generic_hook_ini (void) +{ + debug_initialized = 0; + __malloc_hook = NULL; + __realloc_hook = NULL; + __memalign_hook = NULL; + /* The compiler does not know that these functions are allocators, so it will + not try to optimize it away. */ + __libc_free (__libc_malloc (0)); + + void (*hook) (void) = __malloc_initialize_hook; + if (hook != NULL) + (*hook)(); + debug_initialized = 1; +} + +static void * +malloc_hook_ini (size_t sz, const void *caller) +{ + generic_hook_ini (); + return __debug_malloc (sz); +} + +static void * +realloc_hook_ini (void *ptr, size_t sz, const void *caller) +{ + generic_hook_ini (); + return __debug_realloc (ptr, sz); +} + +static void * +memalign_hook_ini (size_t alignment, size_t sz, const void *caller) +{ + generic_hook_ini (); + return __debug_memalign (alignment, sz); +} static size_t pagesize; @@ -63,7 +145,17 @@ __debug_malloc (size_t bytes) if (__builtin_expect (hook != NULL, 0)) return (*hook)(bytes, RETURN_ADDRESS (0)); - return __libc_malloc (bytes); + void *victim = NULL; + size_t orig_bytes = bytes; + if (!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) + || !malloc_mcheck_before (&bytes, &victim)) + { + victim = __libc_malloc (bytes); + } + if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) && victim != NULL) + victim = malloc_mcheck_after (victim, orig_bytes); + + return victim; } strong_alias (__debug_malloc, malloc) @@ -76,6 +168,10 @@ __debug_free (void *mem) (*hook)(mem, RETURN_ADDRESS (0)); return; } + + if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK)) + mem = free_mcheck (mem); + __libc_free (mem); } strong_alias (__debug_free, free) @@ -88,7 +184,19 @@ __debug_realloc (void *oldmem, size_t bytes) if (__builtin_expect (hook != NULL, 0)) return (*hook)(oldmem, bytes, RETURN_ADDRESS (0)); - return __libc_realloc (oldmem, bytes); + size_t orig_bytes = bytes, oldsize = 0; + void *victim = NULL; + + if (!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) + || !realloc_mcheck_before (&oldmem, &bytes, &oldsize, &victim)) + { + victim = __libc_realloc (oldmem, bytes); + } + if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) && victim != NULL) + victim = realloc_mcheck_after (victim, oldmem, orig_bytes, + oldsize); + + return victim; } strong_alias (__debug_realloc, realloc) @@ -100,7 +208,18 @@ _debug_mid_memalign (size_t alignment, size_t bytes, const void *address) if (__builtin_expect (hook != NULL, 0)) return (*hook)(alignment, bytes, address); - return __libc_memalign (alignment, bytes); + void *victim = NULL; + size_t orig_bytes = bytes; + + if (!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) + || !memalign_mcheck_before (alignment, &bytes, &victim)) + { + victim = __libc_memalign (alignment, bytes); + } + if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) && victim != NULL) + victim = memalign_mcheck_after (victim, alignment, orig_bytes); + + return victim; } static void * @@ -165,17 +284,17 @@ strong_alias (__debug_posix_memalign, posix_memalign) static void * __debug_calloc (size_t nmemb, size_t size) { + size_t bytes; + + if (__glibc_unlikely (__builtin_mul_overflow (nmemb, size, &bytes))) + { + errno = ENOMEM; + return NULL; + } + void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook); if (__builtin_expect (hook != NULL, 0)) { - size_t bytes; - - if (__glibc_unlikely (__builtin_mul_overflow (nmemb, size, &bytes))) - { - errno = ENOMEM; - return NULL; - } - void *mem = (*hook)(bytes, RETURN_ADDRESS (0)); if (mem != NULL) @@ -184,6 +303,20 @@ __debug_calloc (size_t nmemb, size_t size) return mem; } - return __libc_calloc (nmemb, size); + size_t orig_bytes = bytes; + void *victim = NULL; + + if (!__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK) + || !malloc_mcheck_before (&bytes, &victim)) + { + victim = __libc_malloc (bytes); + } + if (victim != NULL) + { + if (__is_malloc_debug_enabled (MALLOC_MCHECK_HOOK)) + victim = malloc_mcheck_after (victim, orig_bytes); + memset (victim, 0, orig_bytes); + } + return victim; } strong_alias (__debug_calloc, calloc) |