mtrack/mtrack.c

1376 lines
38 KiB
C

/*
gcc -O2 mtrack.c -shared -o mtrack.so -ldl -pthread
*/
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <execinfo.h>
#include <pthread.h>
#ifdef __GNUC__
# if __GNUC__ >= 4
# define EAPI __attribute__ ((visibility("default")))
# else
# define EAPI
# endif
#else
# define EAPI
#endif
static void _mhook_do_init(void);
/*============================================================================*
* Local *
*============================================================================*/
enum {
MHOOK_NONE = 0,
MHOOK_FILL = 1,
MHOOK_TRACE = 2,
MHOOK_TRACK = 3,
MHOOK_DEBUG = 4
};
/**
* @cond LOCAL
*/
#define MHOOK_CANARY_MAGIC 0xf0
static int _mhook_mode = MHOOK_NONE;
static unsigned char _mhook_fill_pat_malloc = 0xaa;
static unsigned char _mhook_fill_pat_free = 0xbb;
static unsigned char _mhook_canary_size = 0x00;
static unsigned char _mhook_canary = 0xfe;
static int _mhook_trace_fd = -1;
static int _mhook_track_fd = -1;
static unsigned char _mhook_buf[256];
static int _mhook_first = 1;
static void *(*_mhook_orig_malloc) (size_t) = NULL;
static void (*_mhook_orig_free) (void *) = NULL;
static void *(*_mhook_orig_realloc) (void *, size_t) = NULL;
static void *(*_mhook_orig_memalign) (size_t, size_t) = NULL;
static void *(*_mhook_orig_calloc) (size_t, size_t) = NULL;
static pthread_mutex_t lk;
/**
* @endcond
*/
/*============================================================================*
* Global *
*============================================================================*/
/*****************************************************************************/
/* memory null implementation */
static void *
_mhook_null_malloc(size_t size)
{
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_malloc) return _mhook_buf;
result = _mhook_orig_malloc(size);
return result;
}
static void
_mhook_null_free(void *ptr)
{
// FIXME: hack around dlsym doing a malloc/calloc
if (ptr == _mhook_buf) return;
_mhook_orig_free(ptr);
}
static void *
_mhook_null_realloc(void *ptr, size_t size)
{
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (ptr == _mhook_buf)
{
fprintf(stderr, "mtrack: realloc()ing initial _mhook_buf\n");
abort();
}
result = _mhook_orig_realloc(ptr, size);
return result;
}
static void *
_mhook_null_memalign(size_t alignment, size_t size)
{
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_memalign) return _mhook_buf;
result = _mhook_orig_memalign(alignment, size);
return result;
}
static void *
_mhook_null_calloc(size_t nmemb, size_t size)
{
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_malloc)
{
memset(_mhook_buf, 0, nmemb * size);
return _mhook_buf;
}
result = _mhook_orig_calloc(nmemb, size);
return result;
}
/*****************************************************************************/
/* memory filler implementation */
#define MHOOK_FILL_HEADER_SPACE (sizeof(void *) * 2)
typedef struct _Mhook_Mem_Fill
{
size_t size;
void *magic;
} Mhook_Mem_Fill;
static void *
_mhook_fill_malloc(size_t size)
{
Mhook_Mem_Fill *m;
size_t ms = size + MHOOK_FILL_HEADER_SPACE;
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_malloc) return _mhook_buf;
m = _mhook_orig_malloc(ms);
if (m)
{
memset(m, _mhook_fill_pat_malloc, MHOOK_FILL_HEADER_SPACE);
result = ((unsigned char *)m) + MHOOK_FILL_HEADER_SPACE;
m->size = size;
memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *));
memset(result, _mhook_fill_pat_malloc, size);
}
return result;
}
static void
_mhook_fill_free(void *ptr)
{
Mhook_Mem_Fill *m;
// FIXME: hack around dlsym doing a malloc/calloc
if (ptr == _mhook_buf) return;
if (ptr)
{
m = (Mhook_Mem_Fill *)(((unsigned char *)ptr) - MHOOK_FILL_HEADER_SPACE);
memset(ptr, _mhook_fill_pat_free, m->size);
memset(m, _mhook_fill_pat_free, MHOOK_FILL_HEADER_SPACE);
_mhook_orig_free(m);
}
}
static void *
_mhook_fill_realloc(void *ptr, size_t size)
{
Mhook_Mem_Fill *m, *mold;
void *result = NULL;
size_t ms = size + MHOOK_FILL_HEADER_SPACE;
// FIXME: hack around dlsym doing a malloc/calloc
if (ptr == _mhook_buf)
{
fprintf(stderr, "mtrack: realloc()ing initial _mhook_buf\n");
abort();
}
if (size == 0)
{
if (ptr)
{
m = (Mhook_Mem_Fill *)(((unsigned char *)ptr) - MHOOK_FILL_HEADER_SPACE);
memset(ptr, _mhook_fill_pat_free, m->size);
memset(m, _mhook_fill_pat_free, MHOOK_FILL_HEADER_SPACE);
_mhook_orig_free(m);
}
}
else if (ptr)
{
m = _mhook_orig_malloc(ms);
if (m)
{
mold = (Mhook_Mem_Fill *)(((unsigned char *)ptr) - MHOOK_FILL_HEADER_SPACE);
memset(m, _mhook_fill_pat_malloc, MHOOK_FILL_HEADER_SPACE);
result = ((unsigned char *)m) + MHOOK_FILL_HEADER_SPACE;
m->size = size;
memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *));
if (mold->size < m->size)
{
memcpy(result, ptr, mold->size);
memset(((unsigned char *)result) + mold->size,
_mhook_fill_pat_malloc, m->size - mold->size);
}
else
memcpy(result, ptr, m->size);
memset(ptr, _mhook_fill_pat_free, mold->size);
memset(mold, _mhook_fill_pat_free, MHOOK_FILL_HEADER_SPACE);
_mhook_orig_free(mold);
}
}
else
{
m = _mhook_orig_malloc(ms);
if (m)
{
result = ((unsigned char *)m) + MHOOK_FILL_HEADER_SPACE;
m->size = size;
memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *));
memset(result, _mhook_fill_pat_malloc, size);
}
}
return result;
}
static void *
_mhook_fill_memalign(size_t alignment, size_t size)
{
Mhook_Mem_Fill *m;
size_t ms = size + MHOOK_FILL_HEADER_SPACE + alignment;
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_memalign) return _mhook_buf;
m = _mhook_orig_malloc(ms);
if (m)
{
unsigned char *p = ((unsigned char *)m) + MHOOK_FILL_HEADER_SPACE;
unsigned char *palign;
size_t align, mask = ~(alignment - 1);
palign = (unsigned char *)((unsigned long)((p + (alignment - 1))) & mask);
align = palign - p;
memset(m, _mhook_fill_pat_malloc, align);
result = ((unsigned char *)m) + align;
m->size = size;
memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *));
memset(result, _mhook_fill_pat_malloc, size);
}
return result;
}
static void *
_mhook_fill_calloc(size_t nmemb, size_t size)
{
Mhook_Mem_Fill *m;
size_t ms = (size * nmemb) + MHOOK_FILL_HEADER_SPACE;
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_malloc)
{
memset(_mhook_buf, 0, nmemb * size);
return _mhook_buf;
}
m = _mhook_orig_malloc(ms);
if (m)
{
memset(m, _mhook_fill_pat_malloc, MHOOK_FILL_HEADER_SPACE);
result = ((unsigned char *)m) + MHOOK_FILL_HEADER_SPACE;
m->size = nmemb * size;
memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *));
memset(result, 0, nmemb * size);
}
return result;
}
/*****************************************************************************/
/* memory trace implementation */
static void
_mhook_trace_log(const char *str)
{
void *bt[256];
char buf[16384], *p;
int n, skip, i, tot;
static int inside = 0;
if (inside)
{
if (write(_mhook_trace_fd, str, strlen(str)) < 0) perror("write");
if (write(_mhook_trace_fd, " XX\n", 4) < 0) perror("write");
return;
}
inside = 1;
tot = sizeof(buf);
strcpy(buf, str);
p = buf + strlen(str); tot -= p - buf;
n = backtrace(bt, 250);
strcpy(p, " =="); p += 3; tot -= 3;
if (n > 0)
{
skip = 3;
for (i = skip; i < n; i++)
{
int len = 0;
Dl_info info;
if (dladdr(bt[i], &info))
{
if (info.dli_sname)
{
len = strlen(info.dli_sname);
p[0] = ' ';
strcpy(p + 1, info.dli_sname);
len = 1 + len;
}
else
{
strcpy(p, " #");
len = 2;
}
}
else
{
strcpy(p, " ?");
len = 2;
}
p += len; tot -= len;
}
}
p[0] = '\n'; p += 1; tot -= 1;
if (write(_mhook_trace_fd, buf, p - buf) < 0) perror("write");
inside = 0;
}
static void
intstr(unsigned long value, char *str, int radix)
{
static char dig[] = "0123456789abcdefghijklmnopqrstuvwxyz";
int n = 0, neg = 0;
unsigned long v;
char *p, *q;
char c;
if ((radix == 10) && (value < 0))
{
value = -value;
neg = 1;
}
v = value;
do
{
str[n++] = dig[v % radix];
v /= radix;
}
while (v);
if (neg) str[n++] = '-';
str[n] = 0;
for (p = str, q = p + (n - 1); p < q; p++, q--)
{
c = *p; *p = *q; *q = c;
}
}
static void *
_mhook_trace_malloc(size_t size)
{
char buf[256], b[64];
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_malloc) return _mhook_buf;
result = _mhook_orig_malloc(size);
strcpy(buf, "M ");
intstr((unsigned long)size, b, 16);
strcat(buf, b);
strcat(buf, " > ");
intstr((unsigned long)result, b, 16);
strcat(buf, b);
_mhook_trace_log(buf);
return result;
}
static void
_mhook_trace_free(void *ptr)
{
char buf[256], b[64];
// FIXME: hack around dlsym doing a malloc/calloc
if (ptr == _mhook_buf) return;
_mhook_orig_free(ptr);
strcpy(buf, "F ");
intstr((unsigned long)ptr, b, 16);
strcat(buf, b);
_mhook_trace_log(buf);
}
static void *
_mhook_trace_realloc(void *ptr, size_t size)
{
char buf[256], b[64];
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (ptr == _mhook_buf)
{
fprintf(stderr, "mtrack: realloc()ing initial _mhook_buf\n");
abort();
}
result = _mhook_orig_realloc(ptr, size);
strcpy(buf, "R ");
intstr((unsigned long)ptr, b, 16);
strcat(buf, b);
strcat(buf, " ");
intstr((unsigned long)size, b, 16);
strcat(buf, b);
strcat(buf, " > ");
intstr((unsigned long)result, b, 16);
strcat(buf, b);
_mhook_trace_log(buf);
return result;
}
static void *
_mhook_trace_memalign(size_t alignment, size_t size)
{
char buf[256], b[64];
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_memalign) return _mhook_buf;
result = _mhook_orig_memalign(alignment, size);
strcpy(buf, "A ");
intstr((unsigned long)alignment, b, 16);
strcat(buf, b);
strcat(buf, " ");
intstr((unsigned long)size, b, 16);
strcat(buf, b);
strcat(buf, " > ");
intstr((unsigned long)result, b, 16);
strcat(buf, b);
_mhook_trace_log(buf);
return result;
}
static void *
_mhook_trace_calloc(size_t nmemb, size_t size)
{
char buf[256], b[64];
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_malloc)
{
memset(_mhook_buf, 0, nmemb * size);
return _mhook_buf;
}
result = _mhook_orig_calloc(nmemb, size);
strcpy(buf, "C ");
intstr((unsigned long)(nmemb * size), b, 16);
strcat(buf, b);
strcat(buf, " > ");
intstr((unsigned long)result, b, 16);
strcat(buf, b);
_mhook_trace_log(buf);
return result;
}
/*****************************************************************************/
/* memory tracker implementation */
#define MALLOC (void *)0
#define REALLOC (void *)1
#define CALLOC (void *)2
#define MEMALIGN (void *)3
#define MHOOK_TRACK_HEADER_SPACE (sizeof(void *) * 80)
typedef struct _Mhook_Mem_Track Mhook_Mem_Track;
struct _Mhook_Mem_Track
{
size_t size;
void *type;
void *magic;
void *bt[64];
int btnum;
time_t alloctime;
Mhook_Mem_Track *prev, *next;
};
static Mhook_Mem_Track *mems = NULL;
static int allocs = 0;
static int frees = 0;
static int recurse = 0;
#define BAD_CANARY \
fprintf(stderr, "BAD CANARY for %p\n", ptr); \
if (getenv("MTRACK_CANARY_ABORT")) abort(); \
return
static void
_mhook_track_dump(void)
{
Mhook_Mem_Track *m;
if (ftruncate(_mhook_track_fd, 0) < 0) perror("truncate");
for (m = mems; m; m = m->next)
{
char buf[512];
int i;
char *type[] = { "MALLOC", "REALLOC", "CALLOC", "MEMALIGN" };
snprintf(buf, sizeof(buf), "== %s %lu @ %lu\n",
type[(unsigned long)m->type],
(unsigned long)m->size,
(unsigned long)m->alloctime);
if (write(_mhook_track_fd, buf, strlen(buf)) < 0)
perror("write");
if (m->btnum > 0)
backtrace_symbols_fd(m->bt, m->btnum, _mhook_track_fd);
}
}
static void *
_mhook_track_malloc(size_t size)
{
Mhook_Mem_Track *m;
size_t ms = size + MHOOK_TRACK_HEADER_SPACE;
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_malloc) return _mhook_buf;
recurse++;
m = _mhook_orig_malloc(ms);
if (m)
{
memset(m, _mhook_fill_pat_malloc, MHOOK_TRACK_HEADER_SPACE);
result = ((unsigned char *)m) + MHOOK_TRACK_HEADER_SPACE;
m->size = size;
memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *));
memset(result, _mhook_fill_pat_malloc, size);
memset(m->bt, 0, sizeof(m->bt));
if (recurse < 2)
{
m->btnum = backtrace
(m->bt, sizeof(m->bt) / sizeof(void *));
m->alloctime = time(NULL);
}
m->next = m->prev = NULL;
m->type = MALLOC;
m->next = mems;
if (mems) mems->prev = m;
mems = m;
allocs++;
}
recurse--;
// printf("MALLOC %p\n", result);fflush(stdout);
return result;
}
static void
_mhook_track_free(void *ptr)
{
Mhook_Mem_Track *m;
if (ptr == _mhook_buf) return;
recurse++;
if (ptr)
{
void *magic;
memset(&(magic), MHOOK_CANARY_MAGIC, sizeof(void *));
m = (Mhook_Mem_Track *)(((unsigned char *)ptr) - MHOOK_TRACK_HEADER_SPACE);
if (memcmp(&magic, &m->magic, sizeof(void *)))
{
BAD_CANARY;
}
if (m->prev) m->prev->next = m->next;
else mems = m->next;
if (m->next) m->next->prev = m->prev;
memset(ptr, _mhook_fill_pat_free, m->size);
memset(m, _mhook_fill_pat_free, MHOOK_TRACK_HEADER_SPACE);
_mhook_orig_free(m);
frees++;
}
// printf("FREE %p\n", ptr);fflush(stdout);
recurse--;
}
static void *
_mhook_track_realloc(void *ptr, size_t size)
{
Mhook_Mem_Track *m, *mold;
void *result = NULL;
size_t ms = size + MHOOK_TRACK_HEADER_SPACE;
if (ptr == _mhook_buf)
{
fprintf(stderr, "mtrack: realloc()ing initial _mhook_buf\n");
abort();
}
recurse++;
if (size == 0)
{
if (ptr)
{
void *magic;
memset(&(magic), MHOOK_CANARY_MAGIC, sizeof(void *));
m = (Mhook_Mem_Track *)(((unsigned char *)ptr) - MHOOK_TRACK_HEADER_SPACE);
if (memcmp(&magic, &m->magic, sizeof(void *)))
{
BAD_CANARY NULL;
}
if (m->prev) m->prev->next = m->next;
else mems = m->next;
if (m->next) m->next->prev = m->prev;
memset(ptr, _mhook_fill_pat_free, m->size);
memset(m, _mhook_fill_pat_free, MHOOK_TRACK_HEADER_SPACE);
_mhook_orig_free(m);
frees++;
// printf("R1:FREE %p\n", ptr);fflush(stdout);
}
}
else if (ptr)
{
m = _mhook_orig_malloc(ms);
if (m)
{
void *magic;
mold = (Mhook_Mem_Track *)(((unsigned char *)ptr) - MHOOK_TRACK_HEADER_SPACE);
memset(&(magic), MHOOK_CANARY_MAGIC, sizeof(void *));
if (memcmp(&magic, &mold->magic, sizeof(void *)))
{
BAD_CANARY NULL;
}
memset(m, _mhook_fill_pat_malloc, MHOOK_TRACK_HEADER_SPACE);
result = ((unsigned char *)m) + MHOOK_TRACK_HEADER_SPACE;
m->size = size;
memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *));
if (mold->size < m->size)
{
memcpy(result, ptr, mold->size);
memset(((unsigned char *)result) + mold->size, _mhook_fill_pat_malloc, m->size - mold->size);
}
else
memcpy(result, ptr, m->size);
memset(m->bt, 0, sizeof(m->bt));
if (recurse < 2)
{
m->btnum = backtrace
(m->bt, sizeof(m->bt) / sizeof(void *));
m->alloctime = time(NULL);
}
m->type = REALLOC;
m->next = m->prev = NULL;
m->next = mems;
if (mems) mems->prev = m;
mems = m;
if (mold->prev) mold->prev->next = mold->next;
else mems = mold->next;
if (mold->next) mold->next->prev = mold->prev;
memset(ptr, _mhook_fill_pat_free, mold->size);
memset(mold, _mhook_fill_pat_free, MHOOK_TRACK_HEADER_SPACE);
_mhook_orig_free(mold);
allocs++;
frees++;
// printf("R2:FREE %p\n", ptr);fflush(stdout);
// printf("R2:MALLOC %p\n", result);fflush(stdout);
}
}
else
{
m = _mhook_orig_malloc(ms);
if (m)
{
result = ((unsigned char *)m) + MHOOK_TRACK_HEADER_SPACE;
m->size = size;
memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *));
memset(result, _mhook_fill_pat_malloc, size);
memset(m->bt, 0, sizeof(m->bt));
if (recurse < 2)
{
m->btnum = backtrace
(m->bt, sizeof(m->bt) / sizeof(void *));
m->alloctime = time(NULL);
}
m->type = REALLOC;
m->next = m->prev = NULL;
m->next = mems;
if (mems) mems->prev = m;
mems = m;
allocs++;
// printf("R3:MALLOC %p\n", result);fflush(stdout);
}
}
recurse--;
return result;
}
static void *
_mhook_track_memalign(size_t alignment, size_t size)
{
Mhook_Mem_Track *m;
size_t ms;
void *result = NULL;
if (alignment < 1) alignment = 1;
ms = size + MHOOK_TRACK_HEADER_SPACE + alignment;
if (!_mhook_orig_memalign) return _mhook_buf;
recurse++;
m = _mhook_orig_malloc(ms);
if (m)
{
// unsigned char *p = ((unsigned char *)m) + MHOOK_TRACK_HEADER_SPACE;
// unsigned char *palign;
// size_t align, mask = ~(alignment - 1);
// align = MHOOK_TRACK_HEADER_SPACE;
// for now lets never align as asked
// palign = (unsigned char *)((unsigned long)((p + (alignment - 1))) & mask);
// align = palign - p;
// memset(m, _mhook_fill_pat_malloc, align);
// result = ((unsigned char *)m) + align;
memset(m, _mhook_fill_pat_malloc, MHOOK_TRACK_HEADER_SPACE);
result = ((unsigned char *)m) + MHOOK_TRACK_HEADER_SPACE;
m->size = size;
memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *));
memset(result, _mhook_fill_pat_malloc, size);
memset(m->bt, 0, sizeof(m->bt));
if (recurse < 2)
{
m->btnum = backtrace
(m->bt, sizeof(m->bt) / sizeof(void *));
m->alloctime = time(NULL);
}
m->type = MEMALIGN;
m->next = m->prev = NULL;
m->next = mems;
if (mems) mems->prev = m;
mems = m;
allocs++;
}
recurse--;
// printf("MEMALIGN %p\n", result);fflush(stdout);
return result;
}
static void *
_mhook_track_calloc(size_t nmemb, size_t size)
{
Mhook_Mem_Track *m;
size_t ms = (size * nmemb) + MHOOK_TRACK_HEADER_SPACE;
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_malloc)
{
memset(_mhook_buf, 0, nmemb * size);
return _mhook_buf;
}
recurse++;
m = _mhook_orig_malloc(ms);
if (m)
{
memset(m, _mhook_fill_pat_malloc, MHOOK_TRACK_HEADER_SPACE);
result = ((unsigned char *)m) + MHOOK_TRACK_HEADER_SPACE;
m->size = nmemb * size;
memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *));
memset(result, 0, nmemb * size);
memset(m->bt, 0, sizeof(m->bt));
if (recurse < 2)
{
m->btnum = backtrace
(m->bt, sizeof(m->bt) / sizeof(void *));
m->alloctime = time(NULL);
}
m->type = CALLOC;
m->next = m->prev = NULL;
m->next = mems;
if (mems) mems->prev = m;
mems = m;
allocs++;
}
recurse--;
// printf("CALLOC %p\n", result);fflush(stdout);
return result;
}
/*****************************************************************************/
/* memory debug implementation */
#define MHOOK_DEBUG_CANARY_SPACE (_mhook_canary_size * 8)
#define MHOOK_DEBUG_HEADER_SPACE ((sizeof(void *) * 4) + MHOOK_DEBUG_CANARY_SPACE)
typedef struct _Mhook_Mem_Debug
{
size_t size;
unsigned char *realptr;
void *magic;
void *blah;
} Mhook_Mem_Debug;
#define MHOOK_DEBUG_FREEBUF_SIZE (1024 * 256)
//#define MHOOK_DEBUG_FREEBUF_SIZE (1)
static unsigned int _mhook_debug_freebuf_slot = 0;
static void *_mhook_debug_freebuf[MHOOK_DEBUG_FREEBUF_SIZE] = { NULL };
static void
_mhook_debug_canary_check(Mhook_Mem_Debug *m)
{
unsigned char *head = ((unsigned char *)m) + (sizeof(void *) * 4);
unsigned char *tail = head + MHOOK_DEBUG_CANARY_SPACE + m->size;
int i;
for (i = 0; i < (_mhook_canary_size * 8); i++)
{
if (head[i] != _mhook_canary)
{
fprintf(stderr, "CANARY ERROR HEAD @ %p = %x\n",
head + MHOOK_DEBUG_CANARY_SPACE, head[i]);
abort();
}
}
for (i = 0; i < (_mhook_canary_size * 8); i++)
{
if (tail[i] != _mhook_canary)
{
fprintf(stderr, "CANARY ERROR TAIL @ %p = %x\n",
head + MHOOK_DEBUG_CANARY_SPACE, tail[i]);
abort();
}
}
}
static void
_mhook_debug_canary_fill(void *ptr, size_t size)
{
unsigned char *head = ptr;
unsigned char *tail = ptr;
head -= MHOOK_DEBUG_CANARY_SPACE;
tail += size;
memset(head, _mhook_canary, MHOOK_DEBUG_CANARY_SPACE);
memset(tail, _mhook_canary, MHOOK_DEBUG_CANARY_SPACE);
}
static void
_mhook_debug_free_check(void *ptr, size_t size)
{
size_t i;
unsigned char *p = ptr;
for (i = 0; i < size; i++)
{
if (p[i] != _mhook_fill_pat_free)
{
fprintf(stderr, "CANARY ERROR BODY @ %p = %x\n",
ptr, p[i]);
abort();
}
}
}
static void
_mhook_debug_magic_fill(Mhook_Mem_Debug *m)
{
m->blah = (void *)0xf0f0f0f0;
memset(&(m->magic), MHOOK_CANARY_MAGIC, sizeof(void *));
}
static void
_mhook_debug_real_free(void *ptr)
{
Mhook_Mem_Debug *m;
unsigned char *realptr;
size_t psize;
m = (Mhook_Mem_Debug *)(((unsigned char *)ptr) - MHOOK_DEBUG_HEADER_SPACE);
realptr = m->realptr;
psize = m->size;
// fprintf(stderr, "real free %p size %lu @ slot %i\n", ptr, psize, _mhook_debug_freebuf_slot);
_mhook_debug_canary_check(m);
_mhook_debug_free_check(ptr, psize);
memset(m, _mhook_fill_pat_free, MHOOK_DEBUG_HEADER_SPACE);
memset(((unsigned char *)ptr) + psize, _mhook_fill_pat_free, MHOOK_DEBUG_CANARY_SPACE);
_mhook_orig_free(realptr);
}
static void
_mhook_debug_free2(void *ptr)
{
// _mhook_debug_real_free(ptr);
// return;
_mhook_debug_freebuf[_mhook_debug_freebuf_slot] = ptr;
_mhook_debug_freebuf_slot++;
if (_mhook_debug_freebuf_slot >= MHOOK_DEBUG_FREEBUF_SIZE)
{
_mhook_debug_freebuf_slot = 0;
}
if (_mhook_debug_freebuf[_mhook_debug_freebuf_slot])
{
_mhook_debug_real_free(_mhook_debug_freebuf[_mhook_debug_freebuf_slot]);
_mhook_debug_freebuf[_mhook_debug_freebuf_slot] = NULL;
}
}
static void *
_mhook_debug_malloc(size_t size)
{
Mhook_Mem_Debug *m;
size_t ms = size + MHOOK_DEBUG_HEADER_SPACE + MHOOK_DEBUG_CANARY_SPACE;
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_malloc) return _mhook_buf;
m = _mhook_orig_malloc(ms);
if (m)
{
result = ((unsigned char *)m) + MHOOK_DEBUG_HEADER_SPACE;
m->realptr = (unsigned char *)m;
m->size = size;
_mhook_debug_magic_fill(m);
memset(result, _mhook_fill_pat_malloc, size);
_mhook_debug_canary_fill(result, size);
}
return result;
}
static void
_mhook_debug_free(void *ptr)
{
Mhook_Mem_Debug *m;
size_t psize;
// FIXME: hack around dlsym doing a malloc/calloc
if (ptr == _mhook_buf) return;
if (ptr)
{
m = (Mhook_Mem_Debug *)(((unsigned char *)ptr) - MHOOK_DEBUG_HEADER_SPACE);
psize = m->size;
_mhook_debug_canary_check(m);
memset(ptr, _mhook_fill_pat_free, psize);
_mhook_debug_free2(ptr);
}
}
static void *
_mhook_debug_realloc(void *ptr, size_t size)
{
Mhook_Mem_Debug *m, *mold;
void *result = NULL;
size_t ms = size + MHOOK_DEBUG_HEADER_SPACE + MHOOK_DEBUG_CANARY_SPACE;
size_t psize;
// FIXME: hack around dlsym doing a malloc/calloc
if (ptr == _mhook_buf)
{
fprintf(stderr, "mtrack: realloc()ing initial _mhook_buf\n");
abort();
}
if (size == 0)
{
if (ptr)
{
m = (Mhook_Mem_Debug *)(((unsigned char *)ptr) - MHOOK_DEBUG_HEADER_SPACE);
psize = m->size;
_mhook_debug_canary_check(m);
memset(ptr, _mhook_fill_pat_free, psize);
_mhook_debug_free2(ptr);
}
}
else if (ptr)
{
m = _mhook_orig_malloc(ms);
if (m)
{
mold = (Mhook_Mem_Debug *)(((unsigned char *)ptr) - MHOOK_DEBUG_HEADER_SPACE);
psize = mold->size;
_mhook_debug_canary_check(mold);
result = ((unsigned char *)m) + MHOOK_DEBUG_HEADER_SPACE;
m->realptr = (unsigned char *)m;
m->size = size;
_mhook_debug_magic_fill(m);
_mhook_debug_canary_fill(result, size);
if (psize < size)
{
memcpy(result, ptr, psize);
memset(((unsigned char *)result) + psize,
_mhook_fill_pat_malloc, size - psize);
}
else memcpy(result, ptr, size);
_mhook_debug_canary_check(mold);
memset(ptr, _mhook_fill_pat_free, psize);
_mhook_debug_free2(ptr);
}
}
else
{
m = _mhook_orig_malloc(ms);
if (m)
{
result = ((unsigned char *)m) + MHOOK_DEBUG_HEADER_SPACE;
m->realptr = (unsigned char *)m;
m->size = size;
_mhook_debug_magic_fill(m);
memset(result, _mhook_fill_pat_malloc, size);
_mhook_debug_canary_fill(result, size);
}
}
return result;
}
static void *
_mhook_debug_memalign(size_t alignment, size_t size)
{
Mhook_Mem_Debug *m;
size_t ms = size + MHOOK_DEBUG_HEADER_SPACE + MHOOK_DEBUG_CANARY_SPACE + alignment;
unsigned char *mem;
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_memalign) return _mhook_buf;
mem = _mhook_orig_malloc(ms);
if (mem)
{
unsigned char *palign = mem + MHOOK_DEBUG_HEADER_SPACE;
size_t valign = ((size_t)palign) % alignment;
if (valign > 0) valign = alignment - valign;
palign += valign;
m = (Mhook_Mem_Debug *)palign - (MHOOK_DEBUG_HEADER_SPACE);
result = palign;
m->realptr = (unsigned char *)mem;
m->size = size;
_mhook_debug_magic_fill(m);
memset(result, _mhook_fill_pat_malloc, size);
_mhook_debug_canary_fill(result, size);
}
return result;
}
static void *
_mhook_debug_calloc(size_t nmemb, size_t size)
{
Mhook_Mem_Debug *m;
size_t ms = (size * nmemb) + MHOOK_DEBUG_HEADER_SPACE + MHOOK_DEBUG_CANARY_SPACE;
void *result = NULL;
// FIXME: hack around dlsym doing a malloc/calloc
if (!_mhook_orig_malloc)
{
memset(_mhook_buf, 0, nmemb * size);
return _mhook_buf;
}
m = _mhook_orig_malloc(ms);
if (m)
{
result = ((unsigned char *)m) + MHOOK_DEBUG_HEADER_SPACE;
m->realptr = (unsigned char *)m;
m->size = nmemb * size;
_mhook_debug_magic_fill(m);
memset(result, 0, nmemb * size);
_mhook_debug_canary_fill(result, nmemb * size);
}
return result;
}
/*****************************************************************************/
static void *
_mhook_malloc(size_t size)
{
void *result = NULL;
if (!_mhook_first) pthread_mutex_lock(&lk);
_mhook_do_init();
switch (_mhook_mode)
{
case MHOOK_NONE: result = _mhook_null_malloc(size); break;
case MHOOK_FILL: result = _mhook_fill_malloc(size); break;
case MHOOK_TRACE: result = _mhook_trace_malloc(size); break;
case MHOOK_TRACK: result = _mhook_track_malloc(size); break;
case MHOOK_DEBUG: result = _mhook_debug_malloc(size); break;
}
if (!_mhook_first) pthread_mutex_unlock(&lk);
_mhook_first = 0;
return result;
}
static void
_mhook_free(void *ptr)
{
if (!ptr) return;
if (!_mhook_first) pthread_mutex_lock(&lk);
_mhook_do_init();
switch (_mhook_mode)
{
case MHOOK_NONE: _mhook_null_free(ptr); break;
case MHOOK_FILL: _mhook_fill_free(ptr); break;
case MHOOK_TRACE: _mhook_trace_free(ptr); break;
case MHOOK_TRACK: _mhook_track_free(ptr); break;
case MHOOK_DEBUG: _mhook_debug_free(ptr); break;
}
if (!_mhook_first) pthread_mutex_unlock(&lk);
_mhook_first = 0;
}
static void *
_mhook_realloc(void *ptr, size_t size)
{
void *result = NULL;
if ((!ptr) && (size == 0)) return NULL;
if (!_mhook_first) pthread_mutex_lock(&lk);
_mhook_do_init();
switch (_mhook_mode)
{
case MHOOK_NONE: result = _mhook_null_realloc(ptr, size); break;
case MHOOK_FILL: result = _mhook_fill_realloc(ptr, size); break;
case MHOOK_TRACE: result = _mhook_trace_realloc(ptr, size); break;
case MHOOK_TRACK: result = _mhook_track_realloc(ptr, size); break;
case MHOOK_DEBUG: result = _mhook_debug_realloc(ptr, size); break;
}
if (!_mhook_first) pthread_mutex_unlock(&lk);
_mhook_first = 0;
return result;
}
static void *
_mhook_memalign(size_t alignment, size_t size)
{
void *result = NULL;
if (!_mhook_first) pthread_mutex_lock(&lk);
_mhook_do_init();
switch (_mhook_mode)
{
case MHOOK_NONE: result = _mhook_null_memalign(alignment, size); break;
case MHOOK_FILL: result = _mhook_fill_memalign(alignment, size); break;
case MHOOK_TRACE: result = _mhook_trace_memalign(alignment, size); break;
case MHOOK_TRACK: result = _mhook_track_memalign(alignment, size); break;
case MHOOK_DEBUG: result = _mhook_debug_memalign(alignment, size); break;
}
if (!_mhook_first) pthread_mutex_unlock(&lk);
_mhook_first = 0;
return result;
}
static void *
_mhook_calloc(size_t nmemb, size_t size)
{
void *result = NULL;
if (!_mhook_first) pthread_mutex_lock(&lk);
_mhook_do_init();
switch (_mhook_mode)
{
case MHOOK_NONE: result = _mhook_null_calloc(nmemb, size); break;
case MHOOK_FILL: result = _mhook_fill_calloc(nmemb, size); break;
case MHOOK_TRACE: result = _mhook_trace_calloc(nmemb, size); break;
case MHOOK_TRACK: result = _mhook_track_calloc(nmemb, size); break;
case MHOOK_DEBUG: result = _mhook_debug_calloc(nmemb, size); break;
}
if (!_mhook_first) pthread_mutex_unlock(&lk);
_mhook_first = 0;
return result;
}
/*****************************************************************************/
static void
_mhook_signal(int sig, siginfo_t *si, void *foo)
{
switch (_mhook_mode)
{
case MHOOK_NONE: break;
case MHOOK_FILL: break;
case MHOOK_TRACE: break;
case MHOOK_TRACK: _mhook_track_dump(); break;
case MHOOK_DEBUG: break;
}
return;
sig = 0; si = NULL; foo = NULL;
}
/*============================================================================*
* API *
*============================================================================*/
static void
_mhook_init(void)
{
const char *s = getenv("MTRACK");
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&lk, &attr);
if (s)
{
if (!strcmp(s, "fill"))
{
_mhook_mode = MHOOK_FILL;
s = getenv("MTRACK_ALLOC_FILL");
if (s) _mhook_fill_pat_malloc = atoi(s);
s = getenv("MTRACK_FREE_FILL");
if (s) _mhook_fill_pat_free = atoi(s);
}
else if (!strcmp(s, "debug"))
{
_mhook_mode = MHOOK_DEBUG;
s = getenv("MTRACK_ALLOC_FILL");
if (s) _mhook_fill_pat_malloc = atoi(s);
s = getenv("MTRACK_FREE_FILL");
if (s) _mhook_fill_pat_free = atoi(s);
s = getenv("MTRACK_CANARY_SIZE");
if (s) _mhook_canary_size = atoi(s);
s = getenv("MTRACK_CANARY");
if (s) _mhook_canary = atoi(s);
}
else if (!strcmp(s, "trace"))
{
_mhook_mode = MHOOK_TRACE;
s = getenv("MTRACK_TRACE_FILE");
if (s)
{
_mhook_trace_fd = open(s,
O_CREAT | O_WRONLY | O_TRUNC,
S_IRUSR | S_IWUSR);
if (_mhook_trace_fd < 0)
_mhook_mode = MHOOK_NONE;
else
{
if (fcntl(_mhook_trace_fd, F_SETFD, FD_CLOEXEC) < 0)
{
close(_mhook_trace_fd);
_mhook_trace_fd = -1;
_mhook_mode = MHOOK_NONE;
}
}
}
}
else if (!strcmp(s, "track"))
{
_mhook_mode = MHOOK_TRACK;
s = getenv("MTRACK_TRACE_FILE");
if (s)
{
_mhook_track_fd = open(s,
O_CREAT | O_WRONLY | O_TRUNC,
S_IRUSR | S_IWUSR);
if (_mhook_track_fd < 0)
_mhook_mode = MHOOK_NONE;
else
{
if (fcntl(_mhook_track_fd, F_SETFD, FD_CLOEXEC) < 0)
{
close(_mhook_track_fd);
_mhook_track_fd = -1;
_mhook_mode = MHOOK_NONE;
}
else
{
struct sigaction sa;
sa.sa_sigaction = _mhook_signal;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sigaction(SIGURG, &sa, NULL);
}
}
}
}
else _mhook_mode = MHOOK_NONE;
}
}
EAPI void *__libc_malloc (size_t size) { return _mhook_malloc(size); }
EAPI void *malloc (size_t size) { return _mhook_malloc(size); }
EAPI void __libc_free (void *ptr) { _mhook_free(ptr); }
EAPI void free (void *ptr) { _mhook_free(ptr); }
EAPI void *__libc_realloc (void *ptr, size_t size) { return _mhook_realloc(ptr, size); }
EAPI void *realloc (void *ptr, size_t size) { return _mhook_realloc(ptr, size); }
EAPI void *__libc_memalign(size_t algn, size_t size) { return _mhook_memalign(algn, size); }
EAPI void *memalign (size_t algn, size_t size) { return _mhook_memalign(algn, size); }
EAPI void *__libc_calloc (size_t num, size_t size) { return _mhook_calloc(num, size); }
EAPI void *calloc (size_t num, size_t size) { return _mhook_calloc(num, size); }
static void
_mhook_do_init(void)
{
static int initted = 0;
if (initted) return;
unsetenv("LD_PRELOAD");
initted = 1;
_mhook_init();
_mhook_orig_malloc = dlsym(RTLD_NEXT, "__libc_malloc");
_mhook_orig_free = dlsym(RTLD_NEXT, "__libc_free");
_mhook_orig_realloc = dlsym(RTLD_NEXT, "__libc_realloc");
_mhook_orig_memalign = dlsym(RTLD_NEXT, "__libc_memalign");
_mhook_orig_calloc = dlsym(RTLD_NEXT, "__libc_calloc");
if (!_mhook_orig_malloc) _mhook_orig_malloc = dlsym(RTLD_NEXT, "malloc");
if (!_mhook_orig_free) _mhook_orig_free = dlsym(RTLD_NEXT, "free");
if (!_mhook_orig_realloc) _mhook_orig_realloc = dlsym(RTLD_NEXT, "realloc");
if (!_mhook_orig_memalign) _mhook_orig_memalign = dlsym(RTLD_NEXT, "memalign");
if (!_mhook_orig_calloc) _mhook_orig_calloc = dlsym(RTLD_NEXT, "calloc");
_mhook_first = 0;
}
/**
* @}
*/