/* gcc -O2 mtrack.c -shared -o mtrack.so -ldl -pthread */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #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; } /** * @} */