/****************************************************************************** * Memory Checker * * Copyright (C) 2002 Hal Duston * * * * 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. * * * * 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. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * ******************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include #ifdef HAVE_ERRNO_H # include #endif #ifndef HAVE_DECL_ERRNO extern int errno; #endif #ifdef HAVE_LIMITS_H # include #endif #ifdef HAVE_STDARG_H # include #else # ifdef HAVE_VARARGS_H # include # endif #endif #include #ifdef HAVE_SYS_TYPES_H # include #endif #if STDC_HEADERS # include # include #else # if HAVE_STDLIB_H # include # endif #endif #if HAVE_STRING_H # if !STDC_HEADERS && HAVE_MEMORY_H # include # endif # include #else # if HAVE_STRINGS_H # include # endif #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SIGNAL_H # include #endif #ifndef HAVE_MEMCPY # ifdef HAVE_BCOPY # define memcpy(d,s,n) bcopy((s),(d),(n)) # endif #endif #ifndef HAVE_MEMMOVE # ifdef HAVE_BCOPY # define memmove(d,s,n) bcopy((s),(d),(n)) # endif #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_MALLOC_H # include #endif /* This mess was copied from the autoconf acspecific.m4, */ /* which in turn was copied from the GNU getpagesize.h. */ #ifndef HAVE_GETPAGESIZE # ifdef _SC_PAGESIZE # define getpagesize() sysconf(_SC_PAGESIZE) # else /* no _SC_PAGESIZE */ # ifdef HAVE_SYS_PARAM_H # include # ifdef EXEC_PAGESIZE # define getpagesize() EXEC_PAGESIZE # else /* no EXEC_PAGESIZE */ # ifdef NBPG # define getpagesize() NBPG * CLSIZE # ifndef CLSIZE # define CLSIZE 1 # endif /* no CLSIZE */ # else /* no NBPG */ # ifdef NBPC # define getpagesize() NBPC # else /* no NBPC */ # ifdef PAGESIZE # define getpagesize() PAGESIZE # endif /* no PAGESIZE */ # endif /* no NBPC */ # endif /* no NBPG */ # endif /* no EXEC_PAGESIZE */ # else /* no HAVE_SYS_PARAM_H */ # define getpagesize() 8192 /* punt totally */ # endif /* no HAVE_SYS_PARAM_H */ # endif /* no _SC_PAGESIZE */ #else extern int getpagesize(void); #endif /* no HAVE_GETPAGESIZE */ #ifdef HAVE_SYS_MMAN_H # include #endif #ifdef HAVE_SYS_REG_H # include #endif #ifdef HAVE_SYS_PARAM_H # include #endif #ifdef HAVE_PTHREAD_H # include #endif #ifdef HAVE_SYS_USER_H # include #endif #if defined(__x86_64__) && defined(__linux__) # include "x86_64-ucontext.h" #else # if (defined(__arm) || defined(__arm__)) && defined(__linux__) # include "arm-ucontext.h" # else # ifdef HAVE_UCONTEXT_H # include # endif # endif #endif #ifdef HAVE_EXECINFO_H # include #endif #include "memcheck.h" #ifndef MAP_FAILED # define MAP_FAILED (void *)(-1L) #endif #ifndef MAP_ANONYMOUS # ifdef MAP_ANON # define MAP_ANONYMOUS MAP_ANON # endif #endif #ifndef SA_SIGINFO # define SA_SIGINFO 0 #endif #ifdef __GNUC__ # define memcheck_return_address(arg1,arg2) \ ((void)(arg1), (void)(arg2), __builtin_return_address(0)) #else # if STACK_ORDER == -1 # define memcheck_return_address(arg1,arg2) \ ((void)(arg2), memcheck_get_return_address(&(arg1))) # elif STACK_ORDER == 1 # define memcheck_return_address(arg1,arg2) \ ((void)(arg1), memcheck_get_return_address(&(arg2))) # endif # if defined(_AIX) # define STACK_OFFSET 4 # elif defined(__hppa) || defined(__hppa__) # define STACK_OFFSET 3 # elif defined(__alpha) # define STACK_OFFSET 2 # else # define STACK_OFFSET 1 # endif #endif /* This is the backtrace function depth, and can be increased if necessary. */ #define BACKTRACE_SIZE 128 /* Number of pages required for the memcheck_info structure. (Normally 1.) */ #define memcheck_info_pages \ ((sizeof(struct memcheck_info) \ - 1) \ / memcheck_pagesize \ + 1) /* Number of realloc caller objects that will fit after the * memcheck_info structure on the initial page(s). */ #define MAX_REALLOC_CALL \ ((memcheck_info_pages \ * memcheck_pagesize \ - (sizeof(struct memcheck_info) \ - sizeof(struct caller))) \ / sizeof(struct caller)) /* Number of overflow pages required for a given count of realloc caller objects. */ #define memcheck_info_overflow_pages(count) \ ((((count \ - MAX_REALLOC_CALL) \ * sizeof(struct caller) \ - 1) \ / memcheck_pagesize) \ + 1) /* Given the pointer find the associated memcheck_info structure object. */ #define memcheck_info_from_ptr(ptr) \ ((struct memcheck_info *)((((long)(ptr)) \ & -memcheck_pagesize) \ - (memcheck_info_pages \ * memcheck_pagesize))) #ifdef HAVE_CADDR_T # define mprotect(ptr,size,prot) (mprotect)((caddr_t)ptr,size,prot) # define munmap(ptr,size) (munmap)((caddr_t)ptr,size) #endif #ifdef HAVE_UCONTEXT_T # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_CR2 # define ucontext_data(uc) ((void *)((uc)->uc_mcontext.cr2)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_SC_TRAPARG_A0 # define ucontext_data(uc) ((void *)((uc)->uc_mcontext.sc_traparg_a0)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_MC_ERR # define ucontext_data(uc) ((void *)((uc)->uc_mcontext.mc_err)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_FAULT_ADDRESS # define ucontext_data(uc) ((void *)((uc)->uc_mcontext.fault_address)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT__ES_DAR # define ucontext_data(uc) ((void *)((uc)->uc_mcontext->es.dar)) # endif # endif # endif # endif # endif # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_SC_IP # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.sc_ip)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_SC_PC # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.sc_pc)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_RIP # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.rip)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_EIP # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.eip)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_MC_RIP # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.mc_rip)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_MC_EIP # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.mc_eip)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_ARM_PC # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.arm_pc)) # else # ifdef GetSSReg # define ucontext_text(uc) ((void *)GetSSReg(&(uc)->uc_mcontext, ss_pcoq_head)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_GR8 # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.gr8)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_PSW # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.psw.addr)) # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT__SS_SRR0 # define ucontext_text(uc) ((void *)((uc)->uc_mcontext->ss.srr0)) # else # ifdef _UC_MACHINE_PC # define ucontext_text(uc) ((void *)_UC_MACHINE_PC(uc)) # else # ifndef R_PC # ifdef REG_EIP # define R_PC REG_EIP # else # ifdef EIP # define R_PC EIP # else # ifdef REG_RIP # define R_PC REG_RIP # else # ifdef REG_PC # define R_PC REG_PC # else # ifdef _REG_EIP # define R_PC _REG_EIP # endif # endif # endif # endif # endif # endif # endif # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS # ifdef R_TRAPARG_A0 # define ucontext_data(uc) ((void *)((uc)->uc_mcontext.gregs[R_TRAPARG_A0])) # endif # ifdef EF_A0 # define ucontext_data(uc) ((void *)((uc)->uc_mcontext.gregs[EF_A0])) # endif # ifdef R_PC # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.gregs[R_PC])) # endif # ifdef EF_CAUSE # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.gregs[EF_CAUSE])) # endif # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_MC_REGS # ifdef R_TRAPARG_A0 # define ucontext_data(uc) ((void *)((uc)->uc_mcontext.mc_regs[R_TRAPARG_A0])) # endif # ifdef R_PC # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.mc_regs[R_PC])) # endif # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_SC_REGS # ifdef R_TRAPARG_A0 # define ucontext_data(uc) ((void *)((uc)->uc_mcontext.sc_regs[R_TRAPARG_A0])) # endif # ifdef R_PC # define ucontext_text(uc) ((void *)((uc)->uc_mcontext.sc_regs[R_PC])) # endif # else # ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_SC # define ucontext_text(uc) (sigcontext_text((uc)->uc_mcontext.sc)) # define ucontext_data(uc) (sigcontext_data((uc)->uc_mcontext.sc)) # endif # endif # endif # endif # endif # endif # endif # endif # endif # endif # endif # endif # endif # endif # endif #endif #if defined(HAVE_SIGCONTEXT_T) || defined(HAVE_STRUCT_SIGCONTEXT) # if defined(HAVE_SIGCONTEXT_T_CR2) || defined(HAVE_STRUCT_SIGCONTEXT_CR2) # define sigcontext_data(sc) ((void *)((sc)->cr2)) # else # if defined(HAVE_SIGCONTEXT_T_FAULT_ADDRESS) || defined(HAVE_STRUCT_SIGCONTEXT_FAULT_ADDRESS) # define sigcontext_data(sc) (((void *)((sc)->fault_address))) # endif # endif # if defined(HAVE_SIGCONTEXT_T_SC_PC) || defined(HAVE_STRUCT_SIGCONTEXT_SC_PC) # define sigcontext_text(sc) ((void *)((sc)->sc_pc)) # else # if defined(HAVE_SIGCONTEXT_T_SC_IR) || defined(HAVE_STRUCT_SIGCONTEXT_SC_IR) # define sigcontext_text(sc) ((void *)((sc)->sc_ir)) # else # if defined(HAVE_SIGCONTEXT_T_SC_JMPBUF) || defined(HAVE_STRUCT_SIGCONTEXT_SC_JMPBUF) # define sigcontext_data(sc) ((void *)((sc)->sc_jmpbuf.jmp_context.o_vaddr)) # define sigcontext_text(sc) ((void *)((sc)->sc_jmpbuf.jmp_context.iar)) # else # if defined(HAVE_SIGCONTEXT_T_SC_RIP) || defined(HAVE_STRUCT_SIGCONTEXT_SC_RIP) # define sigcontext_text(sc) ((void *)((sc)->sc_rip)) # else # if defined(HAVE_SIGCONTEXT_T_SC_EIP) || defined(HAVE_STRUCT_SIGCONTEXT_SC_EIP) # define sigcontext_text(sc) ((void *)((sc)->sc_eip)) # else # if defined(HAVE_SIGCONTEXT_T_RIP) || defined(HAVE_STRUCT_SIGCONTEXT_RIP) # define sigcontext_text(sc) ((void *)((sc)->rip)) # else # if defined(HAVE_SIGCONTEXT_T_EIP) || defined(HAVE_STRUCT_SIGCONTEXT_EIP) # define sigcontext_text(sc) ((void *)((sc)->eip)) # else # if defined(HAVE_SIGCONTEXT_T_ARM_PC) || defined(HAVE_STRUCT_SIGCONTEXT_ARM_PC) # define sigcontext_text(sc) ((void *)((sc)->arm_pc)) # else # if defined(HAVE_SIGCONTEXT_T_SREGS) || defined(HAVE_STRUCT_SIGCONTEXT_SREGS) # define sigcontext_text(sc) ((void *)((sc)->sregs->regs.psw.addr)) # else # if defined(HAVE_SIGCONTEXT_T_SC_TRAPARG_A0) || defined(HAVE_STRUCT_SIGCONTEXT_SC_TRAPARG_A0) # define sigcontext_text(cs) ((void *)((sc)->sc_traparg_a0)) # endif # endif # endif # endif # endif # endif # endif # endif # endif # endif #endif #if defined(ucontext_data) # define context_data(context) (ucontext_data((ucontext_t *)(context))) #else # if defined(sigcontext_data) # define context_data(context) (sigcontext_data((sigcontext_t *)(context))) # else # define context_data(context) ((void *)(((void)(context)), NULL)) # endif #endif #if defined(ucontext_text) # define context_text(context) (ucontext_text((ucontext_t *)(context))) #else # if defined(sigcontext_text) # define context_text(context) (sigcontext_text((sigcontext_t *)(context))) # else # define context_text(context) ((void *)(((void)(context)), NULL)) # endif #endif #if defined(LONG_BIT) && LONG_BIT == 64 # define MEMCHECK_SENTRY 0x5aa55aa55aa55aa5 #else # define MEMCHECK_SENTRY 0x5aa55aa5 #endif #define countof(x) (sizeof(x)/sizeof((x)[0])) #define MEMCHECK_INFO 3 #define MEMCHECK_WARN 2 #define MEMCHECK_ERROR 1 #define MEMCHECK_NONE 0 #define strdup_function 1 #define malloc_function 2 #define calloc_function 3 #define realloc_function 4 #define free_function 5 #define cfree_function 6 #define valloc_function 7 #define memalign_function 8 #define posix_memalign_function 9 #define alloc_type 1 #define free_type 2 #ifdef HAVE_SIGACTION # ifdef HAVE_SIGINFO_T typedef siginfo_t *sa_action_type_arg2; # else typedef int sa_action_type_arg2; # endif # if defined(HAVE_SIGACTION_T_SA_SIGACTION) \ || defined(HAVE_STRUCT_SIGACTION_SA_SIGACTION) typedef RETSIGTYPE (*sa_function)(int signum, sa_action_type_arg2 arg2, void *context); # elif defined(HAVE_SIGACTION_T_SA_HANDLER) \ || defined(HAVE_STRUCT_SIGACTION_SA_HANDLER) typedef RETSIGTYPE (*sa_function)(int signum); # endif #endif struct backtrace { int size; void *array[BACKTRACE_SIZE]; }; struct caller { #ifdef HAVE_BACKTRACE struct backtrace trace; #endif const void *addr; const char *file; int line; int func; }; struct memcheck_function { const char *name; int type; }; struct memcheck_info; struct memcheck_info { struct memcheck_info *next; struct memcheck_info *prev; void *ptr; /* Pointer to memory returned to application. */ void *sentry; /* Sentry to check for under/over runs. */ /* Really a long *, but this loses with gcc -O2 on sparc. */ int prot; /* Current mprotect value. */ int inuse; /* Set on initial allocate, cleared on final free. */ size_t size; /* In bytes. Doesn't include memcheck_info page or sentry. */ size_t pages; /* Includes memcheck_info page and realloc overflow area pages. */ size_t count; /* Count of times reallocated. */ struct caller *realloc2; /* Pointer to overflow realloc area. */ struct caller alloc; struct caller freed; struct caller realloc[1]; long sentry_reserve; /* Reserve space for the sentry to back into. */ /* Only used to calculate space in the memcheck_info page. */ /* Nothing is ever placed here. */ }; struct memcheck_config { int underruns; int restart; int reuse; int churn; int backtrace; int level; size_t large; size_t limit; }; void (cfree)(void *ptr); void *(memalign)(size_t boundary, size_t size); int (posix_memalign)(void **memptr, size_t alignment, size_t size); static char *memcheck_strdup_internal(const char *file, int line, const void *addr, const struct backtrace *trace, const char *str); static void *memcheck_malloc_internal(const char *file, int line, const void *addr, const struct backtrace *trace, size_t size); static void *memcheck_calloc_internal(const char *file, int line, const void *addr, const struct backtrace *trace, size_t n, size_t size); static void *memcheck_realloc_internal(const char *file, int line, const void *addr, const struct backtrace *trace, void *ptr, size_t size); static void memcheck_free_internal(const char *file, int line, const void *addr, const struct backtrace *trace, const void *ptr); static void memcheck_cfree_internal(const char *file, int line, const void *addr, const struct backtrace *trace, const void *ptr); static void *memcheck_valloc_internal(const char *file, int line, const void *addr, const struct backtrace *trace, size_t size); static void *memcheck_memalign_internal(const char *file, int line, const void *addr, const struct backtrace *trace, size_t boundary, size_t size); static int memcheck_posix_memalign_internal(const char *file, int line, const void *addr, const struct backtrace *trace, void **memptr, size_t alignment, size_t size); static void *memcheck_allocator(const char *file, int line, int func, const void *addr, const struct backtrace *trace, const void *ptr, size_t nsize); static int memcheck_check_sentry(struct memcheck_info *mem); static void memcheck_place_sentry(struct memcheck_info *mem); static size_t memcheck_user_pages(size_t size); static void memcheck_setup(struct memcheck_info *mem, size_t pages, size_t size); static int memcheck_delete(struct memcheck_info *mem, const struct caller *caller); static void memcheck_update(struct memcheck_info *mem, const struct caller *caller); static int memcheck_insert(struct memcheck_info *mem, const struct caller *caller); static void memcheck_log(int level, int flush, const char *fmt, ...); static void memcheck_log_trace(int level, int flush, const char *message, const struct caller *caller); static void memcheck_history(int level, int flush, struct memcheck_info *mem); static void memcheck_log_info(int level, int flush, const char *message, const void *ptr, size_t size, const struct caller *caller); static int memcheck_protect(struct memcheck_info *mem, int prot); static int memcheck_unprotect(struct memcheck_info *mem); static void memcheck_put(struct memcheck_info *ptr); static struct memcheck_info *memcheck_get(size_t pages); static void memcheck_initialize(void); static void memcheck_exit(void); static void memcheck_error(const char *s); #ifndef __GNUC__ static void *memcheck_get_return_address(void *stack); #endif static const struct backtrace *memcheck_get_backtrace(int skip_frames); static void memcheck_get_symbols(int level, int flush, const struct backtrace *addr); #ifdef HAVE_SIGACTION # if defined(HAVE_SIGACTION_T) || defined(HAVE_STRUCT_SIGACTION) static void memcheck_reraise(int signum, sa_action_type_arg2 arg2, void *context, void *address); static RETSIGTYPE memcheck_sig2(int signum, sa_action_type_arg2 arg2, void *context, void *address); static RETSIGTYPE memcheck_sig(int signum, sa_action_type_arg2 arg2, void *context, void *address); static sigaction_t memcheck_act_bus[3]; static sigaction_t memcheck_act_segv[3]; # endif #endif #ifdef HAVE_PTHREAD_MUTEX_T static pthread_mutex_t memcheck_mutex; #endif static int memcheck_pages_in_use; static struct memcheck_info *memcheck_head; static int memcheck_reentered_handler; static int memcheck_reinstalled_handler; static size_t memcheck_pagesize; static struct memcheck_config memcheck_config; static struct memcheck_function memcheck_functions[] = { { "", 0 }, { "strdup", alloc_type }, { "malloc", alloc_type }, { "calloc", alloc_type }, { "realloc", alloc_type | free_type }, { "free", free_type }, { "cfree", free_type }, { "valloc", alloc_type }, { "memalign", alloc_type }, { "posix_memalign", alloc_type } }; static const long memcheck_sentry = MEMCHECK_SENTRY; static int memcheck_devzero = -1; /* -1 == Unopened, closed, or not needed. */ /* -2 == Tried to open and failed. */ static int memcheck_file = -1; /* -1 == Unopened or closed. */ /* -2 == Tried to open and failed. */ char *(strdup)(const char *str) { return(memcheck_strdup_internal(NULL, 0, memcheck_return_address(str,str), memcheck_get_backtrace(2), str)); } void *(malloc)(size_t size) { return(memcheck_malloc_internal(NULL, 0, memcheck_return_address(size,size), memcheck_get_backtrace(2), size)); } void *(calloc)(size_t n, size_t size) { return(memcheck_calloc_internal(NULL, 0, memcheck_return_address(n,size), memcheck_get_backtrace(2), n, size)); } void *(realloc)(void *ptr, size_t size) { return(memcheck_realloc_internal(NULL, 0, memcheck_return_address(ptr,size), memcheck_get_backtrace(2), ptr, size)); } void (free)(void *ptr) { memcheck_free_internal(NULL, 0, memcheck_return_address(ptr,ptr), memcheck_get_backtrace(2), ptr); } void (cfree)(void *ptr) { memcheck_cfree_internal(NULL, 0, memcheck_return_address(ptr,ptr), memcheck_get_backtrace(2), ptr); } void *(valloc)(size_t size) { return(memcheck_valloc_internal(NULL, 0, memcheck_return_address(size,size), memcheck_get_backtrace(2), size)); } void *(memalign)(size_t boundary, size_t size) { return(memcheck_memalign_internal(NULL, 0, memcheck_return_address(boundary,size), memcheck_get_backtrace(2), boundary, size)); } int (posix_memalign)(void **memptr, size_t alignment, size_t size) { return(memcheck_posix_memalign_internal(NULL, 0, memcheck_return_address(memptr,size), memcheck_get_backtrace(2), memptr, alignment, size)); } #ifdef __GNU_LIBRARY__ int (atexit)(void (*function)(void)) { return memcheck_atexit(function); } extern int __cxa_atexit (void (*func) (void *), void *arg, void *d); int memcheck_atexit(void (*function)(void)) { /* Initialize, if first time. */ if(memcheck_pagesize == 0) { memcheck_initialize(); } return __cxa_atexit ((void (*) (void *)) function, NULL, NULL); } #endif char *memcheck_strdup(const char *file, int line, const void *addr, const char *str) { (void)addr; return(memcheck_strdup_internal(file, line, memcheck_return_address(file,str), memcheck_get_backtrace(2), str)); } void *memcheck_malloc(const char *file, int line, const void *addr, size_t size) { (void)addr; return(memcheck_malloc_internal(file, line, memcheck_return_address(file,size), memcheck_get_backtrace(2), size)); } void *memcheck_calloc(const char *file, int line, const void *addr, size_t n, size_t size) { (void)addr; return(memcheck_calloc_internal(file, line, memcheck_return_address(file,size), memcheck_get_backtrace(2), n, size)); } void *memcheck_realloc(const char *file, int line, const void *addr, void *ptr, size_t size) { (void)addr; return(memcheck_realloc_internal(file, line, memcheck_return_address(file,size), memcheck_get_backtrace(2), ptr, size)); } void memcheck_free(const char *file, int line, const void *addr, const void *ptr) { (void)addr; memcheck_free_internal(file, line, memcheck_return_address(file,ptr), memcheck_get_backtrace(2), ptr); } void memcheck_cfree(const char *file, int line, const void *addr, const void *ptr) { (void)addr; memcheck_cfree_internal(file, line, memcheck_return_address(file,ptr), memcheck_get_backtrace(2), ptr); } void *memcheck_valloc(const char *file, int line, const void *addr, size_t size) { (void)addr; return(memcheck_valloc_internal(file, line, memcheck_return_address(file,size), memcheck_get_backtrace(2), size)); } void *memcheck_memalign(const char *file, int line, const void *addr, size_t boundary, size_t size) { (void)addr; return(memcheck_memalign_internal(file, line, memcheck_return_address(file,size), memcheck_get_backtrace(2), boundary, size)); } int memcheck_posix_memalign(const char *file, int line, const void *addr, void **memptr, size_t alignment, size_t size) { (void)addr; return(memcheck_posix_memalign_internal(file, line, memcheck_return_address(file,size), memcheck_get_backtrace(2), memptr, alignment, size)); } static char *memcheck_strdup_internal(const char *file, int line, const void *addr, const struct backtrace *trace, const char *str) { char *ptr; if(str != NULL) { size_t len; len = strlen(str); ptr = memcheck_allocator(file, line, strdup_function, addr, trace, NULL, len + 1); if(len != 0) { memcpy(ptr, str, len); } ptr[len] = '\0'; } else { ptr = NULL; } return ptr; } static void *memcheck_malloc_internal(const char *file, int line, const void *addr, const struct backtrace *trace, size_t size) { return memcheck_allocator(file, line, malloc_function, addr, trace, NULL, size); } static void *memcheck_calloc_internal(const char *file, int line, const void *addr, const struct backtrace *trace, size_t n, size_t size) { void *ptr; ptr = memcheck_allocator(file, line, calloc_function, addr, trace, NULL, size * n); if(ptr != NULL && n * size != 0) { memset(ptr, 0, n * size); } return ptr; } static void *memcheck_realloc_internal(const char *file, int line, const void *addr, const struct backtrace *trace, void *ptr, size_t size) { return memcheck_allocator(file, line, realloc_function, addr, trace, ptr, size); } static void memcheck_free_internal(const char *file, int line, const void *addr, const struct backtrace *trace, const void *ptr) { memcheck_allocator(file, line, free_function, addr, trace, ptr, 0); } static void memcheck_cfree_internal(const char *file, int line, const void *addr, const struct backtrace *trace, const void *ptr) { memcheck_allocator(file, line, cfree_function, addr, trace, ptr, 0); } static void *memcheck_valloc_internal(const char *file, int line, const void *addr, const struct backtrace *trace, size_t size) { return memcheck_allocator(file, line, valloc_function, addr, trace, NULL, size); } static void *memcheck_memalign_internal(const char *file, int line, const void *addr, const struct backtrace *trace, size_t boundary, size_t size) { (void)boundary; return memcheck_allocator(file, line, memalign_function, addr, trace, NULL, size); } static int memcheck_posix_memalign_internal(const char *file, int line, const void *addr, const struct backtrace *trace, void **memptr, size_t alignment, size_t size) { int retval; void *ptr; if((alignment % sizeof(void *)) == 0 && (size & (size - 1)) == 0) { if((ptr = memcheck_memalign_internal(file, line, addr, trace, alignment, size)) != NULL) { *memptr = ptr; retval = 0; } else { retval = errno; } } else { retval = EINVAL; } return retval; } static void *memcheck_allocator(const char *file, int line, int func, const void *addr, const struct backtrace *trace, const void *ptr, size_t nsize) { #ifdef HAVE_SIGACTION sigaction_t act; #endif struct caller caller; struct memcheck_info *omem; struct memcheck_info *nmem; size_t opages; size_t npages; size_t upages; size_t osize; size_t ocount; void *optr; void *nptr; int logged; int history; logged = MEMCHECK_NONE; history = MEMCHECK_NONE; /* Initialize, if first time. */ if(memcheck_pagesize == 0) { memcheck_initialize(); } #ifdef HAVE_SIGACTION else { if(!sigaction(SIGSEGV, NULL, &act)) { # if defined(HAVE_SIGACTION_T_SA_SIGACTION) \ || defined(HAVE_STRUCT_SIGACTION_SA_SIGACTION) if(act.sa_sigaction != memcheck_act_segv[0].sa_sigaction) { memcheck_act_segv[0].sa_sigaction = (sa_function)memcheck_sig2; # elif defined(HAVE_SIGACTION_T_SA_HANDLER) \ || defined(HAVE_STRUCT_SIGACTION_SA_HANDLER) if(act.sa_handler != memcheck_act_segv[0].sa_handler) { memcheck_act_segv[0].sa_handler = (sa_function)memcheck_sig2; # endif /* Somebody removed our segv hander, put it back. */ if(memcheck_act_segv[2].sa_handler == NULL) { if(sigaction(SIGSEGV, &memcheck_act_segv[0], &memcheck_act_segv[2])) { memcheck_error("sigaction"); } } memcheck_reinstalled_handler = 1; } } else { memcheck_error("sigaction"); } if(!sigaction(SIGBUS, NULL, &act)) { # if defined(HAVE_SIGACTION_T_SA_SIGACTION) \ || defined(HAVE_STRUCT_SIGACTION_SA_SIGACTION) if(act.sa_sigaction != memcheck_act_bus[0].sa_sigaction) { memcheck_act_bus[0].sa_sigaction = (sa_function)memcheck_sig2; # elif defined(HAVE_SIGACTION_T_SA_HANDLER) \ || defined(HAVE_STRUCT_SIGACTION_SA_HANDLER) if(act.sa_handler != memcheck_act_bus[0].sa_handler) { memcheck_act_bus[0].sa_handler = (sa_function)memcheck_sig2; # endif /* Somebody removed our bus hander, put it back. */ if(memcheck_act_bus[2].sa_handler == NULL) { if(sigaction(SIGBUS, &memcheck_act_bus[0], &memcheck_act_bus[2])) { memcheck_error("sigaction"); } } memcheck_reinstalled_handler = 1; } } else { memcheck_error("sigaction"); } } #endif #ifdef HAVE_BACKTRACE if(trace != NULL) { memcpy(&caller.trace, trace, sizeof(caller.trace)); } else { memset(&caller.trace, 0, sizeof(caller.trace)); } #else (void)trace; #endif caller.addr = addr; caller.file = file; caller.line = line; caller.func = func; /* Check for NULL pointer on deallocations. */ if(ptr == NULL && memcheck_functions[func].type & free_type) { memcheck_log_info(MEMCHECK_INFO, 1, "NULL", ptr, nsize, &caller); logged = logged > MEMCHECK_INFO ? logged : MEMCHECK_INFO; } if(memcheck_functions[func].type & alloc_type) { /* Check for zero size on allocations. */ if(nsize == 0) { memcheck_log_info(MEMCHECK_INFO, 1, "Zero size", ptr, nsize, &caller); logged = logged > MEMCHECK_INFO ? logged : MEMCHECK_INFO; } else if(nsize >= memcheck_config.large && memcheck_config.large > 0) { memcheck_log_info(MEMCHECK_INFO, 1, "Large allocation", ptr, nsize, &caller); logged = logged > MEMCHECK_INFO ? logged : MEMCHECK_INFO; } } /* Retrieve info for old allocation. */ if(ptr != NULL && memcheck_functions[func].type & free_type) { omem = memcheck_info_from_ptr(ptr); if(!memcheck_unprotect(omem)) { if(memcheck_check_sentry(omem)) { memcheck_log_info(MEMCHECK_ERROR, 1, memcheck_config.underruns ? "Detected overrun" : "Detected underrun", ptr, nsize, &caller); history = history > MEMCHECK_ERROR ? history : MEMCHECK_ERROR; logged = logged > MEMCHECK_ERROR ? logged : MEMCHECK_ERROR; } optr = omem->ptr; if(optr != ptr) { memcheck_log_info(MEMCHECK_ERROR, 1, "Invalid", ptr, nsize, &caller); logged = logged > MEMCHECK_ERROR ? logged : MEMCHECK_ERROR; } osize = omem->size; opages = omem->pages; ocount = omem->count; /* Check for old allocation already freed. */ if(!omem->inuse) { memcheck_log_info(MEMCHECK_ERROR, 1, "Already freed", ptr, nsize, &caller); history = history > MEMCHECK_ERROR ? history : MEMCHECK_ERROR; logged = logged > MEMCHECK_ERROR ? logged : MEMCHECK_ERROR; } } else { omem = NULL; optr = NULL; osize = 0; opages = 0; ocount = 0; memcheck_log_info(MEMCHECK_ERROR, 1, "Invalid", ptr, nsize, &caller); logged = logged > MEMCHECK_ERROR ? logged : MEMCHECK_ERROR; } } else { omem = NULL; optr = NULL; osize = 0; opages = 0; ocount = 0; } /* Calculate number of pages for new allocation. */ if(memcheck_functions[func].type & alloc_type) { /* Always need an info page. */ npages = memcheck_info_pages; upages = memcheck_user_pages(nsize); if(nsize > 0) { npages += upages; } if(ocount >= MAX_REALLOC_CALL) { npages += memcheck_info_overflow_pages(ocount + 1); } if(opages == npages && !memcheck_config.churn) /* If page count isn't changing and we aren't "churning". */ { nmem = omem; if(osize == nsize) /* If byte count isn't changing, return the original pointer. */ { nptr = optr; } else /* Relocate the pointer on the same page. */ { if(ocount >= MAX_REALLOC_CALL && memcheck_info_overflow_pages(ocount) != memcheck_info_overflow_pages(ocount + 1)) { /* Continue tracking the realloc overflow area as well. */ memcpy(&((char *)nmem)[memcheck_pagesize * (memcheck_info_pages + upages)], nmem->realloc2, (ocount - MAX_REALLOC_CALL) * sizeof(struct caller)); /* Set the realloc overflow area after the end of the user area. */ nmem->realloc2 = (struct caller *)((char *)nmem + memcheck_pagesize * (memcheck_info_pages + upages)); } memcheck_update(nmem, &caller); memcheck_setup(nmem, npages, nsize); nptr = nmem->ptr; } } else /* The page count _is_ changing or we _are_ "churning". */ { if((nmem = memcheck_get(npages)) != NULL) /* Got a new block of memory. */ { if(omem != NULL) { /* Continue tracking, even if the actual data moves. */ memcpy(nmem, omem, memcheck_info_pages * memcheck_pagesize); if(ocount >= MAX_REALLOC_CALL) { /* Set the realloc overflow area after the end of the user area. */ nmem->realloc2 = (struct caller *)((char *)nmem + memcheck_pagesize * (memcheck_info_pages + upages)); /* Continue tracking the realloc overflow area as well. */ memcpy(nmem->realloc2, omem->realloc2, (ocount - MAX_REALLOC_CALL) * sizeof(struct caller)); } memcheck_update(nmem, &caller); } else { if(memcheck_insert(nmem, &caller)) { nmem = NULL; } } if(nmem != NULL) { memcheck_setup(nmem, npages, nsize); nptr = nmem->ptr; } else { nptr = NULL; } } else /* New block failed. */ { memcheck_log_info(MEMCHECK_ERROR, 1, "Out of memory", ptr, nsize, &caller); logged = logged > MEMCHECK_ERROR ? logged : MEMCHECK_ERROR; nptr = NULL; } } } else { nmem = NULL; nptr = NULL; nsize = 0; npages = 0; } if(logged) { if(memcheck_config.backtrace) { memcheck_get_symbols(logged, 1, trace); } if(history) { memcheck_history(history, 1, omem); } } /* Old and new block both have size and new pointer exists. */ if(osize > 0 && nsize > 0 && nptr != NULL && optr != nptr) { memmove(nptr, optr, osize > nsize ? nsize : osize); } /* Old block exists and isn't being retained. */ if(omem != NULL && omem != nmem && omem->inuse) { memcheck_delete(omem, &caller); memcheck_put(omem); } if(nmem != NULL) { memcheck_place_sentry(nmem); if(memcheck_protect(nmem, PROT_READ | PROT_WRITE)) { nptr = NULL; } } return nptr; } static int memcheck_check_sentry(struct memcheck_info *mem) { long sentry; int retval; if(mem->sentry != NULL) { /* Use memcpy instead of cast+assignment * * due to possible mis-alignment. */ memcpy(&sentry, mem->sentry, sizeof(sentry)); if(sentry != memcheck_sentry) { retval = 1; } else { retval = 0; } } else { retval = 0; } return retval; } static void memcheck_place_sentry(struct memcheck_info *mem) { if(mem->sentry != NULL) { /* Plant sentry. */ /* Use memcpy instead of cast+assignment * * due to possible mis-alignment. */ memcpy(mem->sentry, &memcheck_sentry, sizeof(memcheck_sentry)); } } static size_t memcheck_user_pages(size_t size) { size_t upages; if(memcheck_config.underruns && size % memcheck_pagesize != 0) /* Allow space on beginning(end) for sentry. */ { upages = (size - 1 + sizeof(long)) / memcheck_pagesize + 1; } else /* Sentry can back onto info page, or * * Requested size is _exact_ pagesize, no sentry needed. */ { upages = (size - 1) / memcheck_pagesize + 1; } return upages; } static void memcheck_setup(struct memcheck_info *mem, size_t pages, size_t size) { unsigned alignment; size_t i; mem->pages = pages; mem->size = size; /* Checking for underruns, so set ptr at beginning of data page. */ if(memcheck_config.underruns) { mem->ptr = (char *)mem + (memcheck_info_pages * memcheck_pagesize); /* Set the sentry. */ if(size % memcheck_pagesize != 0) { mem->sentry = (((char *)mem->ptr) + size); } else { mem->sentry = NULL; } } else /* Checking for overruns, so set ptr end at end of last page. */ { mem->ptr = (char *)mem + (memcheck_info_pages + memcheck_user_pages(size)) * memcheck_pagesize - size; /* Align the pointer. */ alignment = sizeof(void *) > sizeof(char *) ? sizeof(void *) : sizeof(char *); alignment = alignment > sizeof(long) ? alignment : sizeof(long); if(size > 0 && size < alignment) { alignment = size; i = 0; while(alignment != 1) { alignment >>= 1; ++i; } while(i > 0) { alignment <<= 1; --i; } } mem->ptr = (char *)((((unsigned long)mem->ptr) / alignment) * alignment); /* Set the sentry. */ if(size % memcheck_pagesize != 0) { mem->sentry = (((char *)mem->ptr) - sizeof(long)); } else { mem->sentry = NULL; } } } static int memcheck_delete(struct memcheck_info *mem, const struct caller *caller) { int retval; retval = 0; if(mem != NULL) { #ifdef HAVE_PTHREAD_MUTEX_LOCK pthread_mutex_lock(&memcheck_mutex); #endif if(memcheck_head == mem) { memcheck_head = mem->next; } #ifdef HAVE_PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock(&memcheck_mutex); #endif if(mem->next != NULL) { if(mprotect(mem->next, memcheck_info_pages * memcheck_pagesize, PROT_READ | PROT_WRITE)) { memcheck_error("mprotect"); retval = 1; } mem->next->prev = mem->prev; if(mprotect(mem->next, memcheck_info_pages * memcheck_pagesize, PROT_NONE)) { memcheck_error("mprotect"); retval = 1; } } if(mem->prev != NULL) { if(mprotect(mem->prev, memcheck_info_pages * memcheck_pagesize, PROT_READ | PROT_WRITE)) { memcheck_error("mprotect"); retval = 1; } mem->prev->next = mem->next; if(mprotect(mem->prev, memcheck_info_pages * memcheck_pagesize, PROT_NONE)) { memcheck_error("mprotect"); retval = 1; } } memcpy(&mem->freed, caller, sizeof(mem->freed)); mem->inuse = 0; } return retval; } static void memcheck_update(struct memcheck_info *mem, const struct caller *caller) { if(mem->count < MAX_REALLOC_CALL) { memcpy(&mem->realloc[mem->count], caller, sizeof(mem->realloc[0])); } else { memcpy(&mem->realloc2[mem->count - MAX_REALLOC_CALL], caller, sizeof(mem->realloc2[0])); } ++mem->count; } static int memcheck_insert(struct memcheck_info *mem, const struct caller *caller) { int retval; retval = 0; #ifdef HAVE_PTHREAD_MUTEX_LOCK pthread_mutex_lock(&memcheck_mutex); #endif mem->next = memcheck_head; memcheck_head = mem; #ifdef HAVE_PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock(&memcheck_mutex); #endif if(mem->next != NULL) { if(mprotect(mem->next, memcheck_info_pages * memcheck_pagesize, PROT_READ | PROT_WRITE)) { memcheck_error("mprotect"); retval = 1; } mem->next->prev = mem; if(mprotect(mem->next, memcheck_info_pages * memcheck_pagesize, PROT_NONE)) { memcheck_error("mprotect"); retval = 1; } } memcpy(&mem->alloc, caller, sizeof(mem->alloc)); mem->inuse = 1; return retval; } static void memcheck_log(int level, int flush, const char *fmt, ...) { static int in_logger; char buffer[256]; va_list ap; size_t len; if(level <= memcheck_config.level && !in_logger) { ++in_logger; va_start(ap, fmt); vsnprintf(buffer, sizeof(buffer), fmt, ap); va_end(ap); len = strlen(buffer); if(memcheck_file == -1) { unlink("memcheck.log"); memcheck_file = open("memcheck.log", O_CREAT | O_RDWR, 0666); if(memcheck_file == -1) { memcheck_error("open"); memcheck_file = -2; } } if(memcheck_file >= 0) { if(write(memcheck_file, buffer, len) < 0) { memcheck_error("write"); } if(flush) { #ifdef HAVE_FSYNC if(fsync(memcheck_file) < 0) { memcheck_error("fsync"); } #endif } } --in_logger; } } static void memcheck_log_trace(int level, int flush, const char *message, const struct caller *caller) { if(caller != NULL) { if(caller->file != NULL) { memcheck_log(level, flush, "\t%s by %s at (%s:%d) [%p]\n", message, memcheck_functions[caller->func].name, caller->file, caller->line, caller->addr); } else { memcheck_log(level, flush, "\t%s by %s [%p]\n", message, memcheck_functions[caller->func].name, caller->addr); } #ifdef HAVE_BACKTRACE if(memcheck_config.backtrace) { memcheck_get_symbols(level, flush, &caller->trace); } #endif } } static void memcheck_history(int level, int flush, struct memcheck_info *mem) { size_t i; if(mem != NULL) { if(mem->alloc.func) { memcheck_log_trace(level, flush, "first allocated", &mem->alloc); } for(i = 0; i < mem->count; ++i) { if(i < MAX_REALLOC_CALL) { if(mem->realloc[i].func) { memcheck_log_trace(level, flush, "reallocated", &mem->realloc[i]); } } else { if(mem->realloc2[i - MAX_REALLOC_CALL].func) { memcheck_log_trace(level, flush, "reallocated", &mem->realloc2[i - MAX_REALLOC_CALL]); } } } if(mem->freed.func) { memcheck_log_trace(level, flush, "first freed", &mem->freed); } } } static void memcheck_log_info(int level, int flush, const char *message, const void *ptr, size_t size, const struct caller *caller) { if(caller != NULL) { if(caller->file != NULL) { memcheck_log(level, flush, "%s pointer %p of size %d in %s at (%s:%d) [%p]\n", message, ptr, size, memcheck_functions[caller->func].name, caller->file, caller->line, caller->addr); } else { memcheck_log(level, flush, "%s pointer %p of size %d in %s at [%p]\n", message, ptr, size, memcheck_functions[caller->func].name, caller->addr); } } } static int memcheck_protect(struct memcheck_info *mem, int prot) { size_t size; int retval; if(mem != NULL) { if(mprotect(mem, memcheck_info_pages * memcheck_pagesize, PROT_READ | PROT_WRITE)) { memcheck_error("mprotect"); retval = 1; } else { if(mem->prot != prot) { mem->prot = prot; size = mem->size; if(size > 0 && prot != PROT_NONE) { if(mprotect((char *)mem + (memcheck_info_pages * memcheck_pagesize), memcheck_user_pages(size) * memcheck_pagesize, prot)) { memcheck_error("mprotect"); retval = 1; } else { retval = 0; } } else { retval = 0; } } else { retval = 0; } if(mem->realloc2 != NULL) { if(mprotect(mem->realloc2, memcheck_info_overflow_pages(mem->count) * memcheck_pagesize, PROT_NONE)) { memcheck_error("mprotect"); retval = 1; } } if(mprotect(mem, memcheck_info_pages * memcheck_pagesize, PROT_NONE)) { memcheck_error("mprotect"); retval = 1; } } } else { retval = 0; } return retval; } static int memcheck_unprotect(struct memcheck_info *mem) { int retval; if(mem != NULL) { if(mprotect(mem, memcheck_info_pages * memcheck_pagesize, PROT_READ | PROT_WRITE)) { memcheck_error("mprotect"); retval = 1; } else { if(mem->pages > 1) { if(mprotect((char *)mem + (memcheck_info_pages * memcheck_pagesize), (mem->pages - memcheck_info_pages) * memcheck_pagesize, PROT_READ | PROT_WRITE)) { memcheck_error("mprotect"); retval = 1; } else { retval = 0; } } else { retval = 0; } } } else { retval = 0; } return retval; } static void memcheck_put(struct memcheck_info *mem) { if(mem != NULL) { if(memcheck_config.reuse) { #ifdef HAVE_PTHREAD_MUTEX_LOCK pthread_mutex_lock(&memcheck_mutex); #endif memcheck_pages_in_use -= mem->pages; #ifdef HAVE_PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock(&memcheck_mutex); #endif if(munmap(mem, mem->pages * memcheck_pagesize)) { memcheck_error("munmap"); } } else { memcheck_protect(mem, PROT_NONE); } } } static struct memcheck_info *memcheck_get(size_t pages) { struct memcheck_info *mem; int flags; if(memcheck_config.limit == 0 || memcheck_config.limit - memcheck_pages_in_use >= pages) { #ifdef HAVE_PTHREAD_MUTEX_LOCK pthread_mutex_lock(&memcheck_mutex); #endif memcheck_pages_in_use += pages; #ifdef HAVE_PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock(&memcheck_mutex); #endif flags = MAP_PRIVATE; #ifdef MAP_ANONYMOUS flags |= MAP_ANONYMOUS; #else if(memcheck_devzero == -1) { memcheck_devzero = open("/dev/zero", O_RDWR); } if(memcheck_devzero >= 0) #endif { if((mem = (void *)mmap(NULL, pages * memcheck_pagesize, PROT_READ | PROT_WRITE, flags, memcheck_devzero, 0)) != MAP_FAILED) { memset(mem, 0, memcheck_info_pages * memcheck_pagesize); if(pages > 1) { memset((char *)mem + memcheck_info_pages * memcheck_pagesize, 0xa5, (pages - memcheck_info_pages) * memcheck_pagesize); } } else { errno = ENOMEM; mem = NULL; } } #ifndef MAP_ANONYMOUS else { if(memcheck_devzero == -1) { memcheck_devzero = -2; memcheck_error("open"); } errno = ENOMEM; mem = NULL; } #endif } else { errno = ENOMEM; mem = NULL; } return mem; } static void memcheck_initialize(void) { const char *val; const char *env; #ifdef HAVE_PTHREAD_MUTEX_INIT pthread_mutex_init(&memcheck_mutex, NULL); #endif memcheck_pagesize = getpagesize(); #ifdef HAVE_SIGACTION # if defined(HAVE_SIGACTION_T_SA_SIGACTION) \ || defined(HAVE_STRUCT_SIGACTION_SA_SIGACTION) memcheck_act_segv[0].sa_sigaction = (sa_function)memcheck_sig; memcheck_act_bus[0].sa_sigaction = (sa_function)memcheck_sig; # elif defined(HAVE_SIGACTION_T_SA_HANDLER) \ || defined(HAVE_STRUCT_SIGACTION_SA_HANDLER) memcheck_act_segv[0].sa_handler = (sa_function)memcheck_sig; memcheck_act_bus[0].sa_handler = (sa_function)memcheck_sig; # endif memcheck_act_segv[0].sa_flags = SA_SIGINFO; memcheck_act_bus[0].sa_flags = SA_SIGINFO; if(!sigemptyset(&memcheck_act_segv[0].sa_mask)) { if(sigaction(SIGSEGV, &memcheck_act_segv[0], &memcheck_act_segv[1])) { memcheck_error("sigaction"); } if(sigaction(SIGBUS, &memcheck_act_bus[0], &memcheck_act_bus[1])) { memcheck_error("sigaction"); } } #endif if((env = getenv("MEMCHECK"))) { if(strstr(env, "under")) { memcheck_config.underruns = 1; } if((val = strstr(env, "restart"))) { if(!strcmp(val, "restart=r")) { memcheck_config.restart = PROT_READ; } if(!strcmp(val, "restart=w")) { memcheck_config.restart = PROT_READ | PROT_WRITE; } } if(strstr(env, "reuse")) { memcheck_config.reuse = 1; } if(strstr(env, "churn")) { memcheck_config.churn = 1; } if(strstr(env, "backtrace")) { memcheck_config.backtrace = 1; } if((val = strstr(env, "level="))) { if(val[6] == 'i') { memcheck_config.level = MEMCHECK_INFO; } if(val[6] == 'w') { memcheck_config.level = MEMCHECK_WARN; } if(val[6] == 'e') { memcheck_config.level = MEMCHECK_ERROR; } } if((val = strstr(env, "large="))) { memcheck_config.large = atoi(&val[6]); } if((val = strstr(env, "limit="))) { char *modifier; memcheck_config.limit = strtol(&val[6], &modifier, 10); if(modifier[0] == 'k' || modifier[0] == 'K') { memcheck_config.limit *= 1024; } if(modifier[0] == 'm' || modifier[0] == 'M') { memcheck_config.limit *= 1024 * 1024; } if(modifier[0] == 'g' || modifier[0] == 'G') { memcheck_config.limit *= 1024 * 1024 * 1024; } memcheck_config.limit /= memcheck_pagesize; } } if(atexit(memcheck_exit)) { memcheck_error("atexit"); } } static void memcheck_exit(void) { struct memcheck_info *mem; struct memcheck_info *next; for(mem = memcheck_head; mem != NULL; mem = next) { if(!memcheck_unprotect(mem)) { next = mem->next; memcheck_log(MEMCHECK_WARN, 0, "Never freed pointer %p of size %ld\n", mem->ptr, mem->size); memcheck_history(MEMCHECK_WARN, 0, mem); } else { break; } } if(memcheck_file >= 0) { close(memcheck_file); } if(memcheck_devzero >= 0) { if(close(memcheck_devzero)) { memcheck_error("close"); } } memcheck_devzero = -1; memcheck_file = -1; #ifdef HAVE_PTHREAD_MUTEX_DESTROY pthread_mutex_destroy(&memcheck_mutex); #endif } static void memcheck_error(const char *s) { static int in_memcheck_error; if(!in_memcheck_error) { /* If we're already in error, don't bother recursing. */ in_memcheck_error = 1; #ifdef HAVE_PERROR perror(s); #else # ifdef HAVE_STRERROR fprintf(stderr, "%s: %s\n", s, strerror(errno)); # else # ifdef HAVE_SYS_ERRLIST fprintf(stderr, "%s: %s\n", s, sys_errlist[errno]); # else # ifdef HAVE__SYS_ERRLIST fprintf(stderr, "%s: %s\n", s, _sys_errlist[errno]); # else fprintf(stderr, "%s: errno=%d\n", s, errno); # endif # endif # endif #endif in_memcheck_error = 0; } } #ifdef HAVE_SIGACTION # if defined(HAVE_SIGACTION_T) || defined(HAVE_STRUCT_SIGACTION) static void memcheck_reraise(int signum, sa_action_type_arg2 arg2, void *context, void *address) # endif { sigaction_t *act; act = NULL; if(signum == SIGSEGV) { act = &memcheck_act_segv[2 - memcheck_reentered_handler]; } if(signum == SIGBUS) { act = &memcheck_act_bus[2 - memcheck_reentered_handler]; } if(memcheck_reinstalled_handler != 0) { memcheck_reentered_handler = 1; } if((act->sa_handler == SIG_DFL || act->sa_handler == SIG_IGN) && !act->sa_flags & SA_SIGINFO) { sigaction(signum, act, NULL); } else { # if defined(HAVE_SIGACTION_T_SA_SIGACTION) \ || defined(HAVE_STRUCT_SIGACTION_SA_SIGACTION) ((void (*)(int, sa_action_type_arg2, void *, void *)) (act->sa_sigaction))(signum, arg2, context, address); # elif defined(HAVE_SIGACTION_T_SA_HANDLER) \ || defined(HAVE_STRUCT_SIGACTION_SA_HANDLER) (void)arg2; (void)context; (void)address; ((void (*)(int))(act->sa_handler))(signum); # endif } } static RETSIGTYPE memcheck_sig2(int signum, sa_action_type_arg2 arg2, void *context, void *address) { memcheck_reentered_handler = 0; memcheck_sig(signum, arg2, context, address); } static RETSIGTYPE memcheck_sig(int signum, sa_action_type_arg2 arg2, void *context, void *address) { static void *prev_text; static void *prev_data; int flags; void *new; int leave_handler; void *text = NULL; void *data = NULL; # ifdef HAVE___UC_GET_IP uint64_t d; uint64_t t; __uc_get_cr(context, 20, &d); __uc_get_ip(context, &t); data = (void *)(long)d; text = (void *)(long)t; # endif # ifdef HAVE_SIGINFO_T if(arg2 != NULL && arg2 != (void *)-1) { # if !(defined(__linux__) \ || defined(__alpha) \ || defined(__digital__) \ || defined(_SCO_DS) \ || defined(__hpux) \ || defined(__sgi) \ || defined(__arm) \ || defined(__arm__) \ || defined(__sparc__) \ || defined(__OpenBSD) \ || defined(NetBSD) \ || defined(__NetBSD) \ || defined(__NetBSD__) \ || defined(__FreeBSD__)) # ifdef HAVE_SIGINFO_T_SI_ADDR if(text == NULL) { text = arg2->si_addr; } # endif # endif # if defined(_SCO_DS) \ || defined(__alpha) \ || defined(__digital__) \ || defined(__hppa) \ || defined(__hppa__) \ || defined(__sgi) \ || defined(__ia64__) \ || defined(__s390__) \ || defined(__sparc__) \ || defined(__OpenBSD) \ || defined(NetBSD) \ || defined(__NetBSD) \ || defined(__NetBSD__) \ || defined(__FreeBSD__) # ifdef HAVE_SIGINFO_T_SI_ADDR if(data == NULL) { data = arg2->si_addr; } # endif # ifdef HAVE_SIGINFO_T_SI_PTR if(data == NULL) { data = arg2->si_ptr; } # endif # endif } # endif if(context != NULL) { if(text == NULL) { text = context_text(context); } if(data == NULL) { if((data = context_data(context)) == NULL) { data = (void *)arg2; } } } leave_handler = 0; if(memcheck_reentered_handler == 0) { memcheck_log(MEMCHECK_ERROR, 1, "%srun of %p at %p\n", memcheck_config.underruns ? "Under" : "Over", data, text); if(memcheck_config.backtrace) { memcheck_get_symbols(MEMCHECK_ERROR, 1, memcheck_get_backtrace(2 + memcheck_reinstalled_handler)); } if(memcheck_config.restart && data != NULL) { if(prev_text != text || prev_data != data) { prev_text = text; prev_data = data; if(mprotect((void *)(((long)(data)) & -memcheck_pagesize), memcheck_pagesize, memcheck_config.restart)) { flags = MAP_PRIVATE; # ifdef MAP_ANONYMOUS flags |= MAP_ANONYMOUS; # endif if((new = mmap((void *)(((long)(data)) & -memcheck_pagesize), memcheck_pagesize, PROT_READ | PROT_WRITE, flags, memcheck_devzero, 0)) != MAP_FAILED && new == (void *)(((long)(data)) & -memcheck_pagesize)) { memset(new, 0xa5, memcheck_pagesize); leave_handler = 1; } } else { leave_handler = 1; } } } } if(leave_handler == 0) { memcheck_reraise(signum, arg2, context, address); } else { if(memcheck_reinstalled_handler != 0) { memcheck_reentered_handler = 1; } } } #endif #ifndef __GNUC__ static void *memcheck_get_return_address(void *stack) { /* Dig a return address off of the stack. */ return (void *)*((long *)(((char *)stack) + ((STACK_DIRECTION) * STACK_OFFSET * ((int)sizeof(void *))))); } #endif static const struct backtrace *memcheck_get_backtrace(int skip_frames) { struct backtrace *retval; #ifdef HAVE_BACKTRACE /* Support a single recursion of malloc for implementations * of atexit that need to call that function. */ static struct backtrace bt; static size_t sw = 0; /* After the first call to the malloc family, * skip the backtrace so we don't recurse infinitely. * (backtrace itself may call malloc) */ if(++sw <= 1 && (bt.size = backtrace(bt.array, BACKTRACE_SIZE)) > skip_frames) { bt.size -= skip_frames; memmove(&bt.array[0], &bt.array[skip_frames], bt.size * sizeof(bt.array[0])); retval = &bt; } else #else (void)skip_frames; #endif { retval = NULL; } #ifdef HAVE_BACKTRACE --sw; #endif return retval; } static void memcheck_get_symbols(int level, int flush, const struct backtrace *addr) { #ifdef HAVE_BACKTRACE if(level <= memcheck_config.level && addr != NULL && addr->size != 0) { if(write(memcheck_file, "------------------------------------------------------------\n", 61) < 0) { memcheck_error("write"); } backtrace_symbols_fd(addr->array, addr->size, memcheck_file); if(write(memcheck_file, "============================================================\n", 61) < 0) { memcheck_error("write"); } if(flush) { #ifdef HAVE_FSYNC if(fsync(memcheck_file) < 0) { memcheck_error("fsync"); } #endif } } else { #else { (void)flush; #endif (void)level; (void)addr; } }