WRAPPER(int , munmap, void *start, size_t length) { DECLARE(int, munmap, void *, size_t); int result; BEGIN_PROTECT (munmap, start, length); result = CALL_REAL (munmap, start, length); /* VERBOSE_TRACE ("munmap (%08lx, %08lx, ...) => %08lx\n", (uintptr_t) start, (uintptr_t) length, (uintptr_t) result); */ if (result == 0) { /* Unregister each page as a heap object. */ size_t ps = getpagesize (); uintptr_t base = (uintptr_t) start & (~ (ps - 1)); /* page align */ uintptr_t offset; for (offset=0; offset<length; offset+=ps) __mf_unregister ((void *) CLAMPADD (base, offset), ps, __MF_TYPE_HEAP_I); } return result; }
WRAPPER(void *, calloc, size_t c, size_t n) { size_t size_with_crumple_zones; DECLARE(void *, calloc, size_t, size_t); DECLARE(void *, malloc, size_t); DECLARE(void *, memset, void *, int, size_t); char *result; BEGIN_PROTECT (calloc, c, n); size_with_crumple_zones = CLAMPADD((c * n), /* XXX: CLAMPMUL */ CLAMPADD(__mf_opts.crumple_zone, __mf_opts.crumple_zone)); BEGIN_MALLOC_PROTECT (); result = (char *) CALL_REAL (malloc, size_with_crumple_zones); END_MALLOC_PROTECT (); if (LIKELY(result)) memset (result, 0, size_with_crumple_zones); if (LIKELY(result)) { result += __mf_opts.crumple_zone; __mf_register (result, c*n /* XXX: clamp */, __MF_TYPE_HEAP_I, "calloc region"); /* XXX: register __MF_TYPE_NOACCESS for crumple zones. */ } return result; }
WRAPPER(void, pthread_exit, void *rc) { DECLARE(void, pthread_exit, void *rc); TRACE ("pthread_exit\n"); /* __mf_state = reentrant; */ CALL_REAL (pthread_exit, rc); /* NOTREACHED */ exit (0); /* Satisfy noreturn attribute of pthread_exit. */ }
WRAPPER(int, pthread_join, pthread_t thr, void **rc) { DECLARE(int, pthread_join, pthread_t thr, void **rc); int result; TRACE ("pthread_join\n"); __mf_state = reentrant; result = CALL_REAL (pthread_join, thr, rc); __mf_state = active; return result; }
WRAPPER(void *, realloc, void *buf, size_t c) { DECLARE(void * , realloc, void *, size_t); size_t size_with_crumple_zones; char *base = buf; unsigned saved_wipe_heap; char *result; BEGIN_PROTECT (realloc, buf, c); if (LIKELY(buf)) base -= __mf_opts.crumple_zone; size_with_crumple_zones = CLAMPADD(c, CLAMPADD(__mf_opts.crumple_zone, __mf_opts.crumple_zone)); BEGIN_MALLOC_PROTECT (); result = (char *) CALL_REAL (realloc, base, size_with_crumple_zones); END_MALLOC_PROTECT (); /* Ensure heap wiping doesn't occur during this peculiar unregister/reregister pair. */ LOCKTH (); __mf_set_state (reentrant); saved_wipe_heap = __mf_opts.wipe_heap; __mf_opts.wipe_heap = 0; if (LIKELY(buf)) __mfu_unregister (buf, 0, __MF_TYPE_HEAP_I); /* NB: underlying region may have been __MF_TYPE_HEAP. */ if (LIKELY(result)) { result += __mf_opts.crumple_zone; __mfu_register (result, c, __MF_TYPE_HEAP_I, "realloc region"); /* XXX: register __MF_TYPE_NOACCESS for crumple zones. */ } /* Restore previous setting. */ __mf_opts.wipe_heap = saved_wipe_heap; __mf_set_state (active); UNLOCKTH (); return result; }
WRAPPER(void *, mmap64, void *start, size_t length, int prot, int flags, int fd, off64_t offset) { DECLARE(void *, mmap64, void *, size_t, int, int, int, off64_t); void *result; BEGIN_PROTECT (mmap64, start, length, prot, flags, fd, offset); result = CALL_REAL (mmap64, start, length, prot, flags, fd, offset); /* VERBOSE_TRACE ("mmap64 (%08lx, %08lx, ...) => %08lx\n", (uintptr_t) start, (uintptr_t) length, (uintptr_t) result); */ if (result != (void *)-1) { /* Register each page as a heap object. Why not register it all as a single segment? That's so that a later munmap() call can unmap individual pages. XXX: would __MF_TYPE_GUESS make this more automatic? */ size_t ps = getpagesize (); uintptr_t base = (uintptr_t) result; uintptr_t offset; for (offset=0; offset<length; offset+=ps) { /* XXX: We could map PROT_NONE to __MF_TYPE_NOACCESS. */ /* XXX: Unaccessed HEAP pages are reported as leaks. Is this appropriate for unaccessed mmap pages? */ __mf_register ((void *) CLAMPADD (base, offset), ps, __MF_TYPE_HEAP_I, "mmap64 page"); } } return result; }
WRAPPER(void *, malloc, size_t c) { size_t size_with_crumple_zones; DECLARE(void *, malloc, size_t c); void *result; BEGIN_PROTECT (malloc, c); size_with_crumple_zones = CLAMPADD(c,CLAMPADD(__mf_opts.crumple_zone, __mf_opts.crumple_zone)); BEGIN_MALLOC_PROTECT (); result = (char *) CALL_REAL (malloc, size_with_crumple_zones); END_MALLOC_PROTECT (); if (LIKELY(result)) { result += __mf_opts.crumple_zone; __mf_register (result, c, __MF_TYPE_HEAP, "malloc region"); /* XXX: register __MF_TYPE_NOACCESS for crumple zones. */ } return result; }
/* This wrapper is a little different, as it's called indirectly from __mf_fini also to clean up pending allocations. */ void * __mf_wrap_alloca_indirect (size_t c) { DECLARE (void *, malloc, size_t); DECLARE (void, free, void *); /* This struct, a linked list, tracks alloca'd objects. The newest object is at the head of the list. If we detect that we've popped a few levels of stack, then the listed objects are freed as needed. NB: The tracking struct is allocated with real_malloc; the user data with wrap_malloc. */ struct alloca_tracking { void *ptr; void *stack; struct alloca_tracking* next; }; static struct alloca_tracking *alloca_history = NULL; void *stack = __builtin_frame_address (0); void *result; struct alloca_tracking *track; TRACE ("%s\n", __PRETTY_FUNCTION__); VERBOSE_TRACE ("alloca stack level %p\n", (void *) stack); /* XXX: thread locking! */ /* Free any previously alloca'd blocks that belong to deeper-nested functions, which must therefore have exited by now. */ #define DEEPER_THAN < /* XXX: for x86; steal find_stack_direction() from libiberty/alloca.c */ while (alloca_history && ((uintptr_t) alloca_history->stack DEEPER_THAN (uintptr_t) stack)) { struct alloca_tracking *next = alloca_history->next; __mf_unregister (alloca_history->ptr, 0, __MF_TYPE_HEAP); BEGIN_MALLOC_PROTECT (); CALL_REAL (free, alloca_history->ptr); CALL_REAL (free, alloca_history); END_MALLOC_PROTECT (); alloca_history = next; } /* Allocate new block. */ result = NULL; if (LIKELY (c > 0)) /* alloca(0) causes no allocation. */ { BEGIN_MALLOC_PROTECT (); track = (struct alloca_tracking *) CALL_REAL (malloc, sizeof (struct alloca_tracking)); END_MALLOC_PROTECT (); if (LIKELY (track != NULL)) { BEGIN_MALLOC_PROTECT (); result = CALL_REAL (malloc, c); END_MALLOC_PROTECT (); if (UNLIKELY (result == NULL)) { BEGIN_MALLOC_PROTECT (); CALL_REAL (free, track); END_MALLOC_PROTECT (); /* Too bad. XXX: What about errno? */ } else { __mf_register (result, c, __MF_TYPE_HEAP, "alloca region"); track->ptr = result; track->stack = stack; track->next = alloca_history; alloca_history = track; } } } return result; }
WRAPPER(void, free, void *buf) { /* Use a circular queue to delay some number (__mf_opts.free_queue_length) of free()s. */ static void *free_queue [__MF_FREEQ_MAX]; static unsigned free_ptr = 0; static int freeq_initialized = 0; DECLARE(void, free, void *); if (UNLIKELY(buf == NULL)) return; BEGIN_PROTECT (free, buf); #if PIC /* Check whether the given buffer might have come from a __mf_0fn_malloc/calloc call that for whatever reason was not redirected back to __mf_0fn_free. If so, we just ignore the call. */ if (UNLIKELY((uintptr_t) buf >= (uintptr_t) __mf_0fn_bufs && (uintptr_t) buf < ((uintptr_t) __mf_0fn_bufs + sizeof(__mf_0fn_bufs)))) { VERBOSE_TRACE ("skipping free of boot (0fn) alloc buffer %p\n", buf); return; } #endif LOCKTH (); if (UNLIKELY(!freeq_initialized)) { memset (free_queue, 0, __MF_FREEQ_MAX * sizeof (void *)); freeq_initialized = 1; } UNLOCKTH (); __mf_unregister (buf, 0, __MF_TYPE_HEAP_I); /* NB: underlying region may have been __MF_TYPE_HEAP. */ if (UNLIKELY(__mf_opts.free_queue_length > 0)) { char *freeme = NULL; LOCKTH (); if (free_queue [free_ptr] != NULL) { freeme = free_queue [free_ptr]; freeme -= __mf_opts.crumple_zone; } free_queue [free_ptr] = buf; free_ptr = (free_ptr == (__mf_opts.free_queue_length-1) ? 0 : free_ptr + 1); UNLOCKTH (); if (freeme) { if (__mf_opts.trace_mf_calls) { VERBOSE_TRACE ("freeing deferred pointer %p (crumple %u)\n", (void *) freeme, __mf_opts.crumple_zone); } BEGIN_MALLOC_PROTECT (); CALL_REAL (free, freeme); END_MALLOC_PROTECT (); } } else { /* back pointer up a bit to the beginning of crumple zone */ char *base = (char *)buf; base -= __mf_opts.crumple_zone; if (__mf_opts.trace_mf_calls) { VERBOSE_TRACE ("freeing pointer %p = %p - %u\n", (void *) base, (void *) buf, __mf_opts.crumple_zone); } BEGIN_MALLOC_PROTECT (); CALL_REAL (free, base); END_MALLOC_PROTECT (); } }
WRAPPER(void, free, void *buf) { /* Use a circular queue to delay some number (__mf_opts.free_queue_length) of free()s. */ static void *free_queue [__MF_FREEQ_MAX]; static unsigned free_ptr = 0; static int freeq_initialized = 0; DECLARE(void, free, void *); BEGIN_PROTECT (free, buf); if (UNLIKELY(buf == NULL)) return; LOCKTH (); if (UNLIKELY(!freeq_initialized)) { memset (free_queue, 0, __MF_FREEQ_MAX * sizeof (void *)); freeq_initialized = 1; } UNLOCKTH (); __mf_unregister (buf, 0, __MF_TYPE_HEAP_I); /* NB: underlying region may have been __MF_TYPE_HEAP. */ if (UNLIKELY(__mf_opts.free_queue_length > 0)) { char *freeme = NULL; LOCKTH (); if (free_queue [free_ptr] != NULL) { freeme = free_queue [free_ptr]; freeme -= __mf_opts.crumple_zone; } free_queue [free_ptr] = buf; free_ptr = (free_ptr == (__mf_opts.free_queue_length-1) ? 0 : free_ptr + 1); UNLOCKTH (); if (freeme) { if (__mf_opts.trace_mf_calls) { VERBOSE_TRACE ("freeing deferred pointer %p (crumple %u)\n", (void *) freeme, __mf_opts.crumple_zone); } BEGIN_MALLOC_PROTECT (); CALL_REAL (free, freeme); END_MALLOC_PROTECT (); } } else { /* back pointer up a bit to the beginning of crumple zone */ char *base = (char *)buf; base -= __mf_opts.crumple_zone; if (__mf_opts.trace_mf_calls) { VERBOSE_TRACE ("freeing pointer %p = %p - %u\n", (void *) base, (void *) buf, __mf_opts.crumple_zone); } BEGIN_MALLOC_PROTECT (); CALL_REAL (free, base); END_MALLOC_PROTECT (); } }