📄 yamd.c
字号:
/* Yet Another Malloc Debugger *//* For now, error checking mostly causes bombs. Later, it will handle things gracefully. *//* This file and the rest of YAMD is copyright (C) 1999 by Nate Eldredge. *//* There is no warranty whatever; I disclaim responsibility for any *//* damage caused. Released under the GNU General Public License (see the *//* file COPYING). *//* Headers */#define _GNU_SOURCE#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdarg.h>#include <unistd.h>#include <fcntl.h>#include <signal.h>#include <malloc.h>#include <errno.h>#include <assert.h>#include <limits.h>#include <sys/time.h>#include <sys/resource.h>#include <sys/mman.h>#include <sys/wait.h>#include <math.h>/* Configuration info */#if ((defined(__GLIBC__)) && (__GLIBC__ >= 2))#define GLIBC2 42#endif #if (defined(GLIBC2) && (__GLIBC_MINOR__ >= 1))#define GLIBC21#endif#ifdef GLIBC2#define HAVE_MEMALIGN#endif#ifdef GLIBC21#define HAVE_BACKTRACE_SYMBOLS#include <execinfo.h>#endif#ifdef __linux__#define HAVE_VALLOC#ifndef GLIBC2/* Might define sigcontext_struct instead. Deal with it. */#define sigcontext_struct sigcontext#include <asm/sigcontext.h>#endif#endif#ifdef __DJGPP__#undef HAVE_VALLOC#undef HAVE_MEMALIGN#include <dpmi.h>#include <sys/nearptr.h>#include <sys/segments.h>#include <crt0.h>#include <setjmp.h>#include <sys/exceptn.h>#undef DJGPP_SIGNAL /* See comment at the #ifdef. */#endif/* #define DEBUG *//* #define HASH_PROFILE *//* Traceback stuff. *//* Integer type which is like a pointer, and can be cast to and from. */typedef unsigned long addr;/* Shorthand. */typedef unsigned char uchar;/* Doesn't point at anything, but is distinct from NULL. */#define BAD_POINTER ((void *)-1)#define MAX_TRACEBACK_LEVELS 50typedef addr TRACEBACK[MAX_TRACEBACK_LEVELS];#define ENVIRONMENT_PREFIX "YAMD_"/* Keeping track of which entry points did what. */#define BY_NONE 0#define BY_MALLOC 1#define BY_REALLOC 2#define BY_FREE 3#define BY_MEMALIGN 4#define BY_ALLOCA 5#define BY_AUTO 6 /* Automatic freeing of alloca'ed blocks-- unimplemented */#define BY_LITE 7#define BYBITS 3/* New algorithm. We have several interlocking structures: - Hash table. Chained with the hash_next field. - Linked list of all blocks, chained by all_next. - Linked lists of blocks allocated by malloc or memalign, chained by alignment_next. - The realloc backlink. */ /* This thing is now rather large, but it's easier if it contains all the allocations we need to do. */typedef struct block { /* Some of these may be redundant */ addr block_addr; /* The address of the first page of the block */ size_t block_size; /* Number of bytes we got, altogether */ addr user_addr; /* Address we told the user */ size_t user_size; /* Size the user is allowed */ addr suffix_addr; /* Where the unmapped suffix pages start */ size_t alignment; TRACEBACK where_alloced; /* Address of the function that allocated it. */ TRACEBACK where_freed; /* Address where it was freed, or NULL */ struct block *realloc_backlink; struct block *hash_next; struct block *all_next; struct block *alignment_next; unsigned who_alloced : BYBITS; unsigned who_freed : BYBITS;} block; /* Should maybe be BLOCK or something? */#define HASH_SIZE 499 /* Probably should be a large prime. */static block *hash[HASH_SIZE];static block *all_blocks = NULL;static block *aligned_blocks = NULL; /* Those with non-default alignment */static block *unaligned_blocks = NULL; /* With default alignment. */#define HAS_DEFAULT_ALIGNMENT(b) ((b)->who_alloced != BY_MEMALIGN)/* Magic. */#define MAGIC_SIZE 8static uchar magic[MAGIC_SIZE] = { 0xde, 0xad, 0xbe, 0xef, 0xba, 0xad, 0xca, 0xfe };#ifdef __i386__#define PGSZ 4096UL#else /* well, maybe it'll happen someday */#define PGSZ ((unsigned long)getpagesize()) /* Shorthand */#endif#define PAGEMASK (PGSZ - 1)/* Variables corresponding to options. *//* Alignment requirement for user blocks; should be a power of 2. One could even make it 1; that would give a speed penalty for the unaligned accesses, but should catch all overruns. */static int default_alignment = 1; static int check_front = 0; /* as opposed to end */#ifdef COMPLETE_MAGIC/* At present, we just magic-fill the bytes between the end we're interested in (dependent on check_front) and the unmapped page. This option will magic-fill the bytes at the other end as well. But there are a lot of them, and so this is slow and involves some tedious arithmetic. Implement it later if there seems to be a need for it. */static int complete_magic = 0;#endif/* Fix corrupted blocks? */static int repair_corrupted = 0;/* Die if a corrupted block is found? */static int die_on_corrupted = 1;/* Filename to which we output. */static const char *logfile_name = "-";/* Logging */#define LOG_INFO 1#define LOG_WARN 2#define LOG_ERR 3static int min_log_level = LOG_INFO;/* Ugly way to make lack of snprintf a little safer */#define MAX_PRINTF (PATH_MAX + 1024) /* let's be liberal */#define LOG_BUF_SIZE (MAX_PRINTF * 4)static char log_buf[LOG_BUF_SIZE];static int log_buf_pos = 0;static int log_fd = -1;/* Some statistics. */static size_t user_currently_allocated = 0; /* and not freed */static size_t max_user_allocated = 0; /* max value of the above */static size_t user_allocated = 0; /* whether freed or not */static unsigned long n_allocations = 0;static size_t internal_allocated = 0;static size_t internal_mapped = 0;static size_t max_internal_mapped = 0;/* Anything much bigger than this becomes a negative int, which confuses the libc allocators. It probably should never happen anyway. */#define WAY_TOO_BIG ((unsigned long)(2 * 1000 * 1000 * 1000))#define YAMD_SO_NAME "yamd.so"#define LD_PRELOAD_ENV "LD_PRELOAD"#define CAST_ASSIGN(d,s) ((d) = ((typeof (d))(s)))#define POINTER_FORMAT "%#08lx"/* Symbol control */#ifdef USE_LIBC_HOOKS#define WRAPPER_LINKAGE static#define WRAP(name) wrap_ ## name#define REAL(name) name#endif#ifdef USE_LD_PRELOAD#define WRAPPER_LINKAGE /* global */#define WRAP(name) name #define REAL(name) __libc_ ## name#endif#ifdef USE_LD_WRAP#define WRAPPER_LINKAGE /* global */#define WRAP(name) __wrap_ ## name #define REAL(name) __real_ ## name#endifextern void * REAL(malloc) (size_t s);WRAPPER_LINKAGE void * WRAP(malloc) (size_t s);extern void * REAL(realloc) (void *p, size_t s);WRAPPER_LINKAGE void * WRAP(realloc) (void * p, size_t s);extern void REAL(free) (void *p);WRAPPER_LINKAGE void WRAP(free) (void * p);#ifdef HAVE_MEMALIGNextern void * REAL(memalign) (size_t align, size_t size);WRAPPER_LINKAGE void * WRAP(memalign) (size_t align, size_t size);#endif/* Hook to ensure we get linked. The asm is to avoid underscore troubles. */int __yamd_hook_1 asm ("__yamd_hook_1") = 0;/* Perhaps someday we can use this to check a binary for containing YAMD. In the meantime it's just a few bytes. */static char some_text[] __attribute__((unused));static char some_text[] = "YAMD version " YAMD_VERSION " was here";/* Declarations */static void die(void);static int zap(addr p, size_t nb);static addr do_valloc(size_t n);static void mem_fill(uchar *dest, size_t dest_size, const uchar *src, size_t src_size);static addr magic_check_range(addr start, addr end);static void magic_fill_range(addr start, addr end);static void insert_block(block *b);static block *find_block_by_user_addr(addr a);static block *find_block_by_any_addr(addr a);static const char *get_entry_name(unsigned by_who);static void log_flush(void);static void log_vprintf(const char *fmt, va_list va);static void log_printf(const char *fmt, ...);static void log_event(int level, const char *desc);static void log_detail(int level, const char *fmt, ...);static void generate_any_traceback(TRACEBACK tb, addr start_eip, addr start_ebp, int eip_on_stack);static void generate_traceback(TRACEBACK tb, addr eip);static void dump_traceback(int level, TRACEBACK tb);static void do_any_traceback(int level, addr eip, addr ebp, int eip_os);static void do_traceback(int level, addr eip);static void describe_block(int level, block *b);static void check_block(block *b);static void check_heap(void);static void *do_malloc(size_t nbytes, size_t alignment, addr orig_caller, unsigned by_who, block *backlink);static block *block_to_free(void *user, addr orig_caller, unsigned by_who);static void do_free_block(block *b, addr orig_caller, unsigned by_who);static void print_footer(void);static void print_header(void);static void handle_page_fault(addr address, int write, addr eip, addr ebp);static void describe_address(int level, addr a);static void *lite_malloc(size_t n);static void lite_free(void *p);static void lite_free_block(block *b);/* --------------------------END DECLS------------------------------ *//* Number of times we call __yamd_maybe_finish. */#define TRIES_FOR_FINISH 3static void startup(void);static void finish(void);void __yamd_maybe_startup(void);void __yamd_maybe_finish(void);static void die(void){ log_flush(); abort();}#ifdef USE_LIBC_HOOKStypedef struct { void * (*malloc_hook)(size_t s); void * (*realloc_hook)(void *p , size_t s ); void (*free_hook)(void *p ); void * (*memalign_hook)(size_t al, size_t s );} hookset;static hookset yamd_hooks = { WRAP(malloc), WRAP(realloc), WRAP(free), WRAP(memalign)};static hookset old_hooks = { NULL, NULL, NULL, NULL };static void set_hooks(hookset *h){ CAST_ASSIGN(__malloc_hook, h->malloc_hook); CAST_ASSIGN(__realloc_hook, h->realloc_hook); CAST_ASSIGN(__free_hook, h->free_hook); CAST_ASSIGN(__memalign_hook, h->memalign_hook);}static void get_hooks(hookset *h){ CAST_ASSIGN(h->malloc_hook, __malloc_hook); CAST_ASSIGN(h->realloc_hook, __realloc_hook); CAST_ASSIGN(h->free_hook, __free_hook); CAST_ASSIGN(h->memalign_hook, __memalign_hook);}#elsetypedef int hookset; /* should never be used */#define set_hooks(h)#define get_hooks(h)#endif#define FULL_MODE 1#define LITE_MODE 2#define NOCATCH_MODE 3static int mode = LITE_MODE;static int old_mode = LITE_MODE; /* so we can call OK_CATCH at first. */#define NO_CATCH() do { \ old_mode = mode; \ mode = NOCATCH_MODE; \ set_hooks(&old_hooks); \} while (0)#define OK_CATCH() do { \ mode = old_mode; \ get_hooks(&old_hooks); \ set_hooks(&yamd_hooks); \} while (0)/* Am I trying too hard to make this transparently changeable? */#define MODE_VAR int __yamd_temp_mode#define ENTER() do { __yamd_temp_mode = mode; mode = LITE_MODE; } while (0)#define LEAVE() do { \ mode = __yamd_temp_mode; \ if (mode == FULL_MODE) log_flush(); \} while (0)#define WAS_LITE_MODE (__yamd_temp_mode == LITE_MODE)#define SHOULD_NOT_CATCH (mode == NOCATCH_MODE)/* Utility. */static inline unsigned longround_down(unsigned long x, unsigned long mul){ return x - (x % mul);}static inline unsigned longround_up(unsigned long x, unsigned long mul){ return round_down(x + (mul - 1), mul);}/* Make region untouchable. */static intzap(addr p, size_t nbytes){ int v; /* fprintf(stderr, "Doing mprotect(%p, %u, PROT_NONE)\n", p, nb); */ v = mprotect((void *)p, nbytes, PROT_NONE); if (v != 0) perror("unmap: mprotect"); return v;}#if 0 /* Not used now. */static intremap_pages(void *p, size_t np){ int v; v = mprotect(p, np * PGSZ, PROT_READ | PROT_WRITE); if (v != 0) perror("unmap: mprotect"); return v;}#endif/* The low-level routines we use to get memory. FIXME: The naming has become misleading. */#if defined(__linux__)/* We know Linux has a good, fast mmap. Use it, so as not to incur malloc's memory overhead. */static void *system_valloc(size_t nbytes){ void *p; p = mmap(NULL, round_up(nbytes, PGSZ), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (p == MAP_FAILED) return NULL; else return p;}#elif defined(HAVE_VALLOC)#define system_valloc valloc#else /* generic implementation */static void *system_valloc(size_t nbytes){ addr a; a = (addr)REAL(malloc)(nbytes + PGSZ); return (void *)((a + PAGEMASK) & ~PAGEMASK);}#endifstatic addrdo_valloc(size_t nbytes){ void *p; NO_CATCH(); p = system_valloc(nbytes); OK_CATCH();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -