diff options
Diffstat (limited to 'libmudflap/mf-hooks1.c')
-rw-r--r-- | libmudflap/mf-hooks1.c | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/libmudflap/mf-hooks1.c b/libmudflap/mf-hooks1.c new file mode 100644 index 000000000..1e46a6509 --- /dev/null +++ b/libmudflap/mf-hooks1.c @@ -0,0 +1,500 @@ +/* Mudflap: narrow-pointer bounds-checking by tree rewriting. + Copyright (C) 2002, 2003, 2004, 2009 Free Software Foundation, Inc. + Contributed by Frank Ch. Eigler <fche@redhat.com> + and Graydon Hoare <graydon@redhat.com> + +This file is part of GCC. + +GCC 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 3, or (at your option) any later +version. + +GCC 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. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" + +#ifndef HAVE_SOCKLEN_T +#define socklen_t int +#endif + + +/* These attempt to coax various unix flavours to declare all our + needed tidbits in the system headers. */ +#if !defined(__FreeBSD__) && !defined(__APPLE__) +#define _POSIX_SOURCE +#endif /* Some BSDs break <sys/socket.h> if this is defined. */ +#define _GNU_SOURCE +#define _XOPEN_SOURCE +#define _BSD_TYPES +#define __EXTENSIONS__ +#define _ALL_SOURCE +#define _LARGE_FILE_API +#define _XOPEN_SOURCE_EXTENDED 1 + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <time.h> + +#include "mf-runtime.h" +#include "mf-impl.h" + +#ifdef _MUDFLAP +#error "Do not compile this file with -fmudflap!" +#endif + + +/* Memory allocation related hook functions. Some of these are + intercepted via linker wrapping or symbol interposition. Others + use plain macros in mf-runtime.h. */ + + +#if PIC + +enum { BS = 4096, NB=10 }; +static char __mf_0fn_bufs[NB][BS]; +static unsigned __mf_0fn_bufs_used[NB]; + + +/* A special bootstrap variant. */ +void * +__mf_0fn_malloc (size_t c) +{ + unsigned i; + + for (i=0; i<NB; i++) + { + if (! __mf_0fn_bufs_used[i] && c < BS) + { + __mf_0fn_bufs_used[i] = 1; + return & __mf_0fn_bufs[i][0]; + } + } + return NULL; +} +#endif + + +#undef malloc +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; +} + + +#ifdef PIC +/* A special bootstrap variant. */ +void * +__mf_0fn_calloc (size_t c, size_t n) +{ + return __mf_0fn_malloc (c * n); +} +#endif + + +#undef calloc +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; +} + + +#if PIC +/* A special bootstrap variant. */ +void * +__mf_0fn_realloc (void *buf, size_t c) +{ + return NULL; +} +#endif + + +#undef realloc +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; +} + + +#if PIC +/* A special bootstrap variant. */ +void +__mf_0fn_free (void *buf) +{ + return; +} +#endif + +#undef free +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; + +#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 (); + } +} + + +/* We can only wrap mmap if the target supports it. Likewise for munmap. + We assume we have both if we have mmap. */ +#ifdef HAVE_MMAP + +#if PIC +/* A special bootstrap variant. */ +void * +__mf_0fn_mmap (void *start, size_t l, int prot, int f, int fd, off_t off) +{ +#if defined(__FreeBSD__) + if (f == 0x1000 && fd == -1 && prot == 0 && off == 0) + return 0; +#endif /* Ignore red zone allocation request for initial thread's stack. */ + + return (void *) -1; +} +#endif + + +#undef mmap +WRAPPER(void *, mmap, + void *start, size_t length, int prot, + int flags, int fd, off_t offset) +{ + DECLARE(void *, mmap, void *, size_t, int, + int, int, off_t); + void *result; + BEGIN_PROTECT (mmap, start, length, prot, flags, fd, offset); + + result = CALL_REAL (mmap, start, length, prot, + flags, fd, offset); + + /* + VERBOSE_TRACE ("mmap (%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, "mmap page"); + } + } + + return result; +} + + +#if PIC +/* A special bootstrap variant. */ +int +__mf_0fn_munmap (void *start, size_t length) +{ + return -1; +} +#endif + + +#undef munmap +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; +} +#endif /* HAVE_MMAP */ + + +/* 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; +} + + +#undef alloca +WRAPPER(void *, alloca, size_t c) +{ + return __mf_wrap_alloca_indirect (c); +} + |