id:isshikiさんと、valgrindは素晴らしいけどWindowsでは使えん的な話をしてて。Electric Fenceみたいなものだったらすぐできんじゃないかと思って、超やっつけで書いた↓

enum chunk_state_t {
	CHUNK_FREE,
	CHUNK_ALLOCATED,
};

struct chunk
{
	enum chunk_state_t state;

	size_t page_size;
	size_t mem_size;
	void *mem_addr;
	void *page_addr;
};

#define PAGE_SIZE 4096
#define CHUNK_NUM 1024

static struct chunk chunks[CHUNK_NUM];
static int initialized;

static struct chunk *
find_chunk( void *p )
{
	int i;
	for ( i=0; i<CHUNK_NUM; i++ ) {
		if ( chunks[i].state == CHUNK_ALLOCATED &&
		     chunks[i].mem_addr == p )
			return chunks+i;
	}

	return NULL;	
}

static struct chunk *
alloc_chunk( void )
{
	int i;
	for ( i=0; i<CHUNK_NUM; i++ ) {
		if ( chunks[i].state == CHUNK_FREE )
			return chunks+i;
	}

	MessageBox( NULL, "oh! memory wariatesugidayo!", "orz OTL", MB_OK );
	*(char*)0 = 0;
	return NULL;
}

void *
malloc( size_t sz )
{
	size_t page_aligned_size;
	char *page_memory;
	char *mem_addr;
	DWORD old_prot;
	struct chunk *chunk;

	page_aligned_size = (sz+(PAGE_SIZE-1))&~(PAGE_SIZE-1);
	page_memory = VirtualAlloc( NULL,
				    page_aligned_size+PAGE_SIZE,
				    MEM_RESERVE, PAGE_EXECUTE_READWRITE );
	VirtualAlloc( page_memory,
		      page_aligned_size,
		      MEM_COMMIT, PAGE_EXECUTE_READWRITE );

	mem_addr = page_memory + page_aligned_size-sz;

	chunk = alloc_chunk( );
	chunk->state = CHUNK_ALLOCATED;
	chunk->page_size = page_aligned_size + PAGE_SIZE;
	chunk->mem_size = sz;
	chunk->page_addr = page_memory;
	chunk->mem_addr = mem_addr;

	return mem_addr;
}

void *
realloc( void *ptr, size_t resize )
{
	struct chunk *c;
	void *new_ptr;

	if ( ptr == NULL )
		return malloc( resize );

	c = find_chunk( ptr );
	if ( c == NULL ) {
		MessageBox( NULL, "WARNING: hennna realloc", "orz", MB_OK );
		return malloc( resize );
	}
	if ( c->mem_size > resize )
		return c->mem_addr;

	new_ptr = malloc( resize );
	memcpy( new_ptr, ptr, c->mem_size );
	free( ptr );
	return new_ptr;
}

void
free( void *ptr )
{
	int i;
	for ( i=0; i<CHUNK_NUM; i++ ) {
		if ( chunks[i].mem_addr == ptr ) {
			if ( chunks[i].state != CHUNK_ALLOCATED ) {
				MessageBox( NULL, "WARNING: double free", "orz", MB_OK );
				return;
			}

			VirtualFree( chunks[i].page_addr, chunks[i].page_size, MEM_RELEASE );

			chunks[i].state = CHUNK_FREE;
			return;
		}
	}

	MessageBox( NULL, "WARNING: free not allocated memory.", "orz", MB_OK );
}
int main()
{
	char *p = malloc( 3 );
	p[0] = 'a';
	puts("0");
	p[1] = 'a';
	puts("1");
	p[2] = 'a';
	puts("2");
	p[3] = 'a';  /* ここで */
	puts("3");
	free( p );

	return 0;
}

こういうプログラムを書くと、p[3] = 'a'のところで落ちます。これを一緒にリンクして、なんだかんだ。
超簡単にgdbの使い方も書いておきます。

$ gdb xxx.exe     # gdbをこれで実行
> run             # これでプログラムが開始
recieve SIGSEGV   # (とかそんな感じのメッセージ)
> bt              # backtraceコマンド実行
# バックトレースがだらだらと

これでどうでしょう?