tbamud/src/zmalloc.c

416 lines
10 KiB
C
Raw Normal View History

2006-12-19 22:56:18 +00:00
/*
** Zmalloc, a simple memory-allocation monitor.
**
** Version 2. added detection for writing off the end or beginning
** of buffers, freeing the same block multiple times. Also now use the
** env variable to set the name of a file to write output to.
**
**
** Copyright 1996,1998,1999,2000 Eric Murray, ericm@lne.com
**
** You may make free use of this code but please give me credit.
**
**
** Documentation: http://www.lne.com/ericm/zmalloc
**
** $Id: zmalloc.c,v 1.3 2000/06/26 18:38:53 ericm Exp $
** $Log: zmalloc.c,v $
** Revision 1.3 2000/06/26 18:38:53 ericm
** updated copyright
** fixed a bug in the test code
** added a test for unfreed memory
**
** Revision 1.2 1998/05/25 16:38:22 ericm
** added more checking: multiple frees, overwriting buffers
** use env variable to set output to stdin, stdout, file
**
** Revision 1.1 1998/05/25 16:31:35 ericm
** Initial revision
**
**
*/
/* protect our calloc() and free() calls from recursive redefinition: */
#define ZMALLOC_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#define NO_MEMORY_PADDING
#ifndef NO_MEMORY_PADDING
static unsigned char beginPad[4] = {
0xde, 0xad, 0xde, 0xad };
static unsigned char endPad[4] = {
0xde, 0xad, 0xde, 0xad };
#endif
extern int errno;
FILE * zfd;
typedef struct meminfo {
struct meminfo *next;
int size; /* number of bytes malloced */
long addr; /* address of memory returned */
int frees; /* number of times that 'free' was called on this memory */
char *file; /* file where malloc was called */
int line; /* line in the code where malloc was called */
} meminfo;
static meminfo memlist;
/* -1 not checked yet, 0 = no zmalloc, 1 = zmalloc */
int zmallocstatus = -1;
/* functions: */
int getzmallocstatus(void);
int * zmalloc(int len,char * file,int line);
void zfree_special(int * what, char * file,int line);
void zfree(int * what, char * file,int line);
char *zstrdup(const char *src, char *file, int line);
void zmalloc_check( void );
void pad_check(meminfo *m);
void zmalloc_free_list(meminfo *m);
/* a chopped down version that works */
int getzmallocstatus()
{
if (zmallocstatus == -1) {
zfd = fopen("test.log","w+");
zmallocstatus = 1;
}
return(zmallocstatus);
}
int * zmalloc(int len,char * file,int line)
{
unsigned char * ret;
meminfo *m;
if (!zmallocstatus) {
/* zmalloc turned off */
ret = (unsigned char *)calloc(1,len);
return((int *)ret);
}
#ifndef NO_MEMORY_PADDING
ret = (unsigned char *)calloc(1,len + sizeof(beginPad) + sizeof(endPad));
#else
ret = (unsigned char *)calloc(1,len);
#endif
if (!ret)
fprintf(zfd,"zmalloc: malloc FAILED");
#ifndef NO_MEMORY_PADDING
/* insert begin and end padding to detect buffer under/overruns: */
memcpy(ret,beginPad,sizeof(beginPad));
ret += sizeof(beginPad); /* make ret skip begin pad */
memcpy(ret + len,endPad,sizeof(endPad));
#endif
if (getzmallocstatus() > 1)
fprintf(zfd,"zmalloc: 0x%4.4x %d bytes %s:%d\n",(int)ret,len,file,line);
m = (meminfo *)calloc(1,sizeof(meminfo));
if (!m) {
fprintf(zfd,"zmalloc: FAILED mem alloc for zmalloc struct... bailing!\n");
return((int*)0);
}
m->next = &memlist;
m->addr = (long)ret;
m->size = (int)len;
m->file = (char *)strdup(file);
m->frees = 0;
if (!m->file) {
fprintf(zfd,"zmalloc: FAILED mem alloc for zmalloc struct... bailing!\n");
return((int*)0);
}
m->line = line;
return((int *)ret);
}
void zfree_special(int * what, char * file,int line)
{
meminfo *m, *old;
unsigned char *addr;
int gotit = 0;
if (what == 0) {
fprintf(zfd,"zmalloc: Null pointer free'd: %s:%d\n", file, line);
return;
}
if (!getzmallocstatus()) {
free(what);
} else {
/* look up allocated mem in list: */
old = m = &memlist;
if (m == (meminfo *)0) {
/* no memlist */
free(what);
} else {
for(; m ; m = m->next) {
if (m->addr == (long)what) {
/* got it. Print it if verbose: */
addr = (unsigned char *)m->addr;
fprintf(zfd,"zmalloc: Freed 0x%4.4x %d bytes mallocd@%s:%d\n",
(int)addr,m->size,m->file,m->line);
fprintf(zfd," free'd from %s:%d\n",
file, line);
/* check the padding: */
pad_check(m);
/* note that we freed the memory */
m->frees++;
/* check to see if it was freed > once */
if (m->frees > 1) {
fprintf(zfd," ERR: multiple frees! 0x%4.4x %d bytes: %s:%d\n",
(int)addr, m->size, m->file, m->line);
fprintf(zfd," free'd from %s:%d\n",
file, line);
}
gotit++;
if (!m)
break;
}
}
if (!gotit && m) {
fprintf(zfd,"zmalloc: ERR: Freed unallocated memory");
fprintf(zfd," @0x%4.4x %d bytes %s:%d\n",
(int)what,m->size,m->file,m->line);
}
else if (gotit > 1) {
/* this shouldn't happen, eh? */
fprintf(zfd," ERR: Multiply-allocd memory!\n");
}
}
}
}
void zfree(int * what, char * file,int line)
{
meminfo *m, *old;
unsigned char *addr;
int gotit = 0;
if (what == 0) {
fprintf(zfd,"zmalloc: Null pointer free'd: %s:%d\n", file, line);
return;
}
if (!getzmallocstatus()) {
free(what);
} else {
/* look up allocated mem in list: */
old = m = &memlist;
if (m == (meminfo *)0) {
/* no memlist */
free(what);
} else {
for(; m ; m = m->next) {
if (m->addr == (long)what) {
/* got it. Print it if verbose: */
addr = (unsigned char *)m->addr;
if (getzmallocstatus() > 1)
fprintf(zfd,"zmalloc: Freed 0x%4.4x %d bytes mallocd@%s:%d\n",
(int)addr,m->size,m->file,m->line);
/* check the padding: */
pad_check(m);
/* note that we freed the memory */
m->frees++;
/* check to see if it was freed > once */
if (m->frees > 1) {
fprintf(zfd," ERR: multiple frees! 0x%4.4x %d bytes: %s:%d\n",
(int)addr, m->size, m->file, m->line);
fprintf(zfd," free'd from %s:%d\n",
file, line);
}
gotit++;
if (!m)
break;
}
}
if (!gotit && m) {
fprintf(zfd,"zmalloc: ERR: Freed unallocated memory");
fprintf(zfd," @0x%4.4x %d bytes %s:%d\n",
(int)what,m->size,m->file,m->line);
}
else if (gotit > 1) {
/* this shouldn't happen, eh? */
fprintf(zfd," ERR: Multiply-allocd memory!\n");
}
}
}
}
/* zstrdup */
char *zstrdup(const char *src, char *file, int line)
{
char *result;
#ifndef NO_MEMORY_STRDUP
if (!getzmallocstatus()) {
result = (char*)malloc(strlen(src) + 1);
if (result == (char*)0)
return (char*)0;
strcpy(result, src); /* strcpy ok, size checked above */
return result;
} else {
result = (char*)zmalloc(strlen(src) + 1, file, line);
if (result == (char*)0)
return (char*)0;
strcpy(result, src);
return result;
}
#else
result = (char*)malloc(strlen(src) + 1);
if (result == (char*)0)
return (char*)0;
strcpy(result, src); /* strcpy ok, size checked above */
return result;
#endif
}
void zmalloc_check()
{
meminfo *m;
char *admonishemnt;
int total_leak = 0;
int num_leaks = 0;
if (getzmallocstatus() > 0) {
/* look up allocated mem in list: */
for(m = &memlist; m ; m = m->next) {
if (m->addr != 0 && m->frees <= 0) {
fprintf(zfd,"zmalloc: UNfreed memory 0x%4.4x %d bytes mallocd at %s:%d\n",
(int)m->addr,m->size,m->file,m->line);
/* check padding on un-freed memory too: */
pad_check(m);
total_leak += m->size;
num_leaks ++;
}
}
if (total_leak) {
if (total_leak > 10000)
admonishemnt = "you must work for Microsoft.";
else if (total_leak > 5000)
admonishemnt = "you should be ashamed!";
else if (total_leak > 2000)
admonishemnt = "you call yourself a programmer?";
else if (total_leak > 1000)
admonishemnt = "the X consortium has a job for you...";
else
admonishemnt = "close, but not there yet.";
fprintf(zfd,"zmalloc: %d leaks totalling %d bytes... %s\n",num_leaks,total_leak,
admonishemnt);
}
else {
fprintf(zfd,"zmalloc: Congratulations: leak-free code!\n");
}
}
/* free up our own internal list */
zmalloc_free_list(&memlist);
}
void pad_check(meminfo *m)
{
#ifndef NO_MEMORY_PADDING
unsigned char *addr = (unsigned char *)m->addr;
/* check the padding: */
if (memcmp((int *)(addr - sizeof(beginPad)),
beginPad, sizeof(beginPad)) != 0)
fprintf(zfd," ERR: beginPad was modified!\n");
if (memcmp((int *)(addr + m->size),endPad,
sizeof(endPad)) != 0)
fprintf(zfd," ERR: endPad was modified!\n");
#endif
}
void zmalloc_free_list(meminfo *m)
{
meminfo *old;
for( m = m->next ; m ; ) {
old = m;
m = m->next;
free(old->file);
free(old);
}
}
#ifdef ZTEST
#undef ZMALLOC_H
#include "zmalloc.h"
main()
{
unsigned char * tmp;
2006-12-19 22:56:18 +00:00
printf("Testing Zmalloc.\n");
printf("Malloc test..");
printf("You should see no error here.\n");
tmp = (unsigned char*)malloc(200);
free(tmp);
2006-12-19 22:56:18 +00:00
printf("Free free mem test...\n");
printf("You should see an ERR: multiple frees here\n");
tmp = (unsigned char*)malloc(200);
free(tmp);
free(tmp);
2006-12-19 22:56:18 +00:00
/*
fprintf(zfd,"\nFree unallocated mem test \n");
tmp += 4;
free(tmp);
2006-12-19 22:56:18 +00:00
*/
printf("Unfreed mem test...\n");
printf("You should see an \"UNfreed mem at line %d\" (at end) because of this\n",__LINE__+1);
tmp = (unsigned char*)malloc(200);
2006-12-19 22:56:18 +00:00
printf("Buffer overrun test 1...\n");
printf("You should see an ERR:endPad here\n");
tmp = (unsigned char*)malloc(200);
tmp[200] = 0xfa;
free(tmp);
2006-12-19 22:56:18 +00:00
printf("Buffer overrun test 2...\n");
printf("You should see an ERR:endPad here\n");
tmp = (unsigned char*)malloc(200);
tmp[215] = 0xbb;
free(tmp);
2006-12-19 22:56:18 +00:00
printf("Buffer underrun test 1...\n");
printf("You should see an ERR:beginPad here\n");
tmp = (unsigned char*)malloc(200);
tmp[-10] = 0x0f;
free(tmp);
2006-12-19 22:56:18 +00:00
printf("Buffer underrun test 2...\n");
printf("You should see an ERR:beginPad here\n");
tmp = (unsigned char*)malloc(200);
tmp[-1] = 0x00;
free(tmp);
2006-12-19 22:56:18 +00:00
zmalloc_check();
exit(0);
}
#endif /* ZTEST */