Index: src/lib/Makefile.am =================================================================== RCS file: /var/lib/cvs/dovecot/src/lib/Makefile.am,v retrieving revision 1.62.2.2 diff -u -r1.62.2.2 Makefile.am --- src/lib/Makefile.am 16 Aug 2006 15:54:56 -0000 1.62.2.2 +++ src/lib/Makefile.am 4 Nov 2006 18:55:51 -0000 @@ -45,6 +45,7 @@ lib-signals.c \ md4.c \ md5.c \ + memdebug.c \ mempool.c \ mempool-alloconly.c \ mempool-datastack.c \ @@ -122,6 +123,7 @@ macros.h \ md4.h \ md5.h \ + memdebug.h \ mempool.h \ mkdir-parents.h \ mmap-util.h \ Index: src/lib/data-stack.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/lib/data-stack.c,v retrieving revision 1.24.2.2 diff -u -r1.24.2.2 data-stack.c --- src/lib/data-stack.c 10 Sep 2006 17:59:47 -0000 1.24.2.2 +++ src/lib/data-stack.c 4 Nov 2006 18:55:52 -0000 @@ -3,6 +3,7 @@ /* @UNSAFE: whole file */ #include "lib.h" +#include "memdebug.h" #include "data-stack.h" #include @@ -13,6 +14,12 @@ # include #endif +#ifdef MEMDEBUG +# define malloc(size) memdebug_malloc(size) +# define calloc(count, size) memdebug_calloc(count, size) +# define free(ptr) memdebug_free(ptr) +#endif + /* Use malloc() and free() for all memory allocations. Useful for debugging memory corruption. */ /* #define DISABLE_DATA_STACK */ Index: src/lib/memdebug.c =================================================================== RCS file: src/lib/memdebug.c diff -N src/lib/memdebug.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/lib/memdebug.c 4 Nov 2006 18:55:52 -0000 @@ -0,0 +1,193 @@ +/* Copyright (c) 2005 Timo Sirainen */ + +#include "lib.h" +#include "ioloop.h" +#include "memdebug.h" + +#ifdef MEMDEBUG +#include +#ifdef __sun__ +# include +#endif + +#define FREE_DELAY 3 +#define MAX_STACK_SIZE 1024 + +struct malloc_block { + struct malloc_block *prev, *next; + size_t size; + + void **free_stack; + time_t free_time; + unsigned int checksum; + + unsigned char data[]; +}; + +static struct timeout *to; +static struct malloc_block *used_head; +static struct malloc_block *unused_head, *unused_tail; + +void *memdebug_malloc(size_t size) +{ + struct malloc_block *block; + + block = calloc(1, sizeof(*block) + size); + block->size = size; + block->next = used_head; + if (used_head != NULL) + used_head->prev = block; + used_head = block; + return block + 1; +} + +void *memdebug_calloc(size_t count, size_t size) +{ + struct malloc_block *block; + + i_assert((size_t)-1 / size > count); + + block = calloc(sizeof(*block) + count*size, 1); + block->size = count*size; + block->next = used_head; + if (used_head != NULL) + used_head->prev = block; + used_head = block; + return block + 1; +} + +void *memdebug_realloc(void *ptr, size_t size) +{ + struct malloc_block *block = PTR_OFFSET(ptr, -sizeof(*block)); + void *new_ptr; + + new_ptr = memdebug_malloc(size); + if (ptr != NULL) { + memcpy(new_ptr, ptr, block->size); + memdebug_free(ptr); + } + return new_ptr; +} + +static void block_check(struct malloc_block *block) +{ + unsigned int i, h = 0, g; + + for (i = 0; i < block->size; i++) { + h = (h << 4) + block->data[i]; + if ((g = h & 0xf0000000UL)) { + h = h ^ (g >> 24); + h = h ^ g; + } + } + + if (block->checksum != h) { + if (block->free_stack != NULL) { + i_error("Memory corruption, free() backtrace:"); + for (i = 0; block->free_stack[i] != NULL; i++) { + i_error("%p[%u]: %p", (void *)(block + 1), i, + block->free_stack[i]); + } + } + i_panic("memory corruption"); + } +} + +static void memdebug_timeout(void *context __attr_unused__) +{ + time_t delete_time = ioloop_time - FREE_DELAY; + + while (unused_head != NULL && unused_head->free_time <= delete_time) { + struct malloc_block *block = unused_head; + + block_check(block); + + unused_head = block->next; + if (unused_head == NULL) + unused_tail = NULL; + else + unused_head->prev = NULL; + free(block->free_stack); + free(block); + } +} + +#ifdef __sun__ +struct walk_stack { + void *stack[MAX_STACK_SIZE+1]; + unsigned int pos; +}; + +static int walk_callback(uintptr_t ptr, int signo __attr_unused__, + void *context) +{ + struct walk_stack *stack = context; + + if (stack->pos < MAX_STACK_SIZE) + stack->stack[stack->pos++] = (void *)ptr; + return 0; +} +#endif + +void memdebug_free(void *ptr) +{ + struct malloc_block *block; + size_t i; + unsigned int g, h; +#ifdef __sun__ + ucontext_t uc; + struct walk_stack stack; +#endif + + if (ptr == NULL) + return; + block = PTR_OFFSET(ptr, -sizeof(*block)); + +#ifdef __sun__ + if (getcontext(&uc) == 0) { + stack.pos = 0; + walkcontext(&uc, walk_callback, &stack); + stack.stack[stack.pos++] = NULL; + block->free_stack = malloc(sizeof(void *) * stack.pos); + memcpy(block->free_stack, stack.stack, + sizeof(void *) * stack.pos); + } +#endif + + /* remove from used list */ + if (block->prev == NULL) + used_head = block->next; + else + block->prev->next = block->next; + + if (block->next != NULL) + block->next->prev = block->prev; + + /* add to unused list */ + block->prev = unused_tail; + block->next = NULL; + + if (unused_tail != NULL) + unused_tail->next = block; + else + unused_head = block; + unused_tail = block; + + /* fill the memory area with random data and calculate its checksum */ + h = 0; + for (i = 0; i < block->size; i++) { + block->data[i] = rand(); + + h = (h << 4) + block->data[i]; + if ((g = h & 0xf0000000UL)) { + h = h ^ (g >> 24); + h = h ^ g; + } + } + block->checksum = h; + + block->free_time = ioloop_time; + if (to == NULL && current_ioloop != NULL) + to = timeout_add(1000, memdebug_timeout, NULL); +} +#endif Index: src/lib/memdebug.h =================================================================== RCS file: src/lib/memdebug.h diff -N src/lib/memdebug.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/lib/memdebug.h 4 Nov 2006 18:55:52 -0000 @@ -0,0 +1,11 @@ +#ifndef __MEMDEBUG_H +#define __MEMDEBUG_H + +#define MEMDEBUG + +void *memdebug_malloc(size_t size); +void *memdebug_calloc(size_t count, size_t size); +void *memdebug_realloc(void *ptr, size_t size); +void memdebug_free(void *ptr); + +#endif Index: src/lib/mempool-alloconly.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/lib/mempool-alloconly.c,v retrieving revision 1.35.2.3 diff -u -r1.35.2.3 mempool-alloconly.c --- src/lib/mempool-alloconly.c 10 Sep 2006 17:49:06 -0000 1.35.2.3 +++ src/lib/mempool-alloconly.c 4 Nov 2006 18:55:52 -0000 @@ -2,6 +2,7 @@ /* @UNSAFE: whole file */ #include "lib.h" +#include "memdebug.h" #include "mempool.h" #include @@ -12,6 +13,11 @@ # include #endif +#ifdef MEMDEBUG +# define calloc(count, size) memdebug_calloc(count, size) +# define free(ptr) memdebug_free(ptr) +#endif + #define MAX_ALLOC_SIZE SSIZE_T_MAX struct alloconly_pool { Index: src/lib/mempool-system.c =================================================================== RCS file: /var/lib/cvs/dovecot/src/lib/mempool-system.c,v retrieving revision 1.18.2.1 diff -u -r1.18.2.1 mempool-system.c --- src/lib/mempool-system.c 10 Sep 2006 17:49:06 -0000 1.18.2.1 +++ src/lib/mempool-system.c 4 Nov 2006 18:55:52 -0000 @@ -3,6 +3,7 @@ /* @UNSAFE: whole file */ #include "lib.h" +#include "memdebug.h" #include "mempool.h" #include @@ -13,6 +14,13 @@ # include #endif +#ifdef MEMDEBUG +# define malloc(size) memdebug_malloc(size) +# define calloc(count, size) memdebug_calloc(count, size) +# define realloc(ptr, size) memdebug_realloc(ptr, size) +# define free(ptr) memdebug_free(ptr) +#endif + static const char *pool_system_get_name(pool_t pool); static void pool_system_ref(pool_t pool); static void pool_system_unref(pool_t *pool);