/* 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 rel='nofollow' onclick='return false;'>
#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 rel='nofollow' onclick='return false;'>
#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 50
typedef 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 8
static 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 3
static 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
#endif
extern 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_MEMALIGN
extern 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_w