Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 18 Sep 1997 19:22:10 +0200 (SAT)
From:      Graham Wheeler <gram@cdsec.com>
To:        phk@critter.freebsd.dk (Poul-Henning Kamp)
Cc:        gram@gram.cdsec.com (Graham Wheeler), hackers@freebsd.org
Subject:   Re: Bug in malloc/free (was: Memory leak in getservbyXXX?)
Message-ID:  <199709181722.TAA00606@cdsec.com>
In-Reply-To: <10666.874602560@critter.freebsd.dk> from "Poul-Henning Kamp" at Sep 18, 97 07:09:20 pm

next in thread | previous in thread | raw e-mail | index | archive | help
Howdy again

> >the problem only started when we moved to from FreeBSD 2.1 to 2.2.2,
> >is why I suggested that the bug may be in the malloc code. Let me state
> >quite clearly though that this is a suggestion, certainly not an accusation.
> >I'm just running out of things to suspect...
> 
> I'm not taking it as such, don't worry :-)  It's just that my work on 
> phkmalloc has taught me far more about how subtle malloc related problems
> can manifest themselves, and I now realize why Purify costs what it does.
> If I'd written it, I wouldn't charge any less.

Well, then maybe you'll find the code below useful. It isn't Purify, but
then it doesn't cost anything near the price 8-).  I'd use it
myself now, except that these days I write everything in C++. Mostly I'm
happy about that, but not right now...





------------ gwdebug.h ---------------------------------------------------
/*
 * A drop-in debugging library for C
 *
 * (c) 1994 by Graham Wheeler, All Rights Reserved
 *
 * This code may be freely redistributed, provided that it is
 * not modified in any way. If you make changes to this code 
 * that you feel are useful, and would like to see them 
 * incorporated, please send mail to gram@cdsec.com
 */

#ifndef __GWDEBUG_H__
#define __GWDEBUG_H__

#ifdef GW_DEBUG

/* Memory debugging */

/* undefine them first in case we included heap.h */

#undef malloc
#undef calloc
#undef realloc
#undef free

extern void *my_malloc(unsigned n, char *f, int l);
extern void *my_calloc(unsigned n, char *f, int l);
extern void *my_realloc(void *p, unsigned n, char *f, int l);
extern void my_free(void *p, char *f, int l);

#define malloc(n)	my_malloc(n, __FILE__, __LINE__)
#define calloc(n)	my_calloc(n, __FILE__, __LINE__)
#define realloc(p,n)	my_realloc(p, n, __FILE__, __LINE__)
#define free(p)	my_free(p, __FILE__, __LINE__)

/* String and memory debugging */

extern char *my_memcpy(char *d, int hint, int limit, char *s, int shint, int flag, char *f, int l);
extern char *my_strcpy(char *d, int hint, int limit, char *s, int shint, int flag, char *f, int l);
extern char *my_memset(char *d, int hint, int limit, char c, char *f, int l);
extern int   my_memcmp(char *s1, int siz1, char *s2, int siz2, int n, int flag, char *f, int l);
extern int   my_strcmp(char *s1, int siz1, char *s2, int siz2, int n, int flag, char *f, int l);
extern int   my_strlen(char *s, int siz, char *f, int l);
extern char *my_strdup(char *s, int siz, int n, char *f, int l);
extern char *my_strstr(char *s1, int siz1, char *s2, int siz2, char *f, int l);
extern char *my_strpbrk(char *s1, int siz1, char *s2, int siz2, char *f, int l);
extern char *my_strchr(char *s, int siz, int c, char *f, int l);
extern char *my_strrchr(char *s, int siz, int c, char *f, int l);
extern int   my_strspn(char *s1, int siz1, char *s2, int siz2, char *f, int l);
extern int   my_strcspn(char *s1, int siz1, char *s2, int siz2, char *f, int l);

#define strcpy(d,s)	my_strcpy(d, sizeof(d), 0, s, sizeof(s), 0, __FILE__, __LINE__)
#define stpcpy(d,s)	my_strcpy(d, sizeof(d), 0, s, sizeof(s), 2, __FILE__, __LINE__)
#define strncpy(d,s,n)	my_strcpy(d, sizeof(d), n, s, sizeof(s), 0, __FILE__, __LINE__)
#define strcat(d,s)	my_strcpy(d, sizeof(d), 0, s, sizeof(s), 4, __FILE__, __LINE__)
#define strncat(d,s,n)	my_strcpy(d, sizeof(d), n, s, sizeof(s), 4, __FILE__, __LINE__)
#define memcpy(d,s,n)	my_memcpy(d, sizeof(d), n, s, sizeof(s), 0, __FILE__, __LINE__)
#define memmove(d,s,n)	my_memcpy(d, sizeof(d), n, s, sizeof(s), 1, __FILE__, __LINE__)
#define memset(d,c,n)	my_memset(d, sizeof(d), n, c, __FILE__, __LINE__)
#define strcmp(s1,s2)	my_strcmp(s1, sizeof(s1), s2, sizeof(s2), 0, 0, __FILE__, __LINE__)
#define strncmp(s1,s2,n) my_strcmp(s1, sizeof(s1), s2, sizeof(s2), n, 0, __FILE__, __LINE__)
#define memcmp(s1,s2,n)	my_memcmp(s1, sizeof(s1), s2, sizeof(s2), n, 1, __FILE__, __LINE__)
#define strlen(s)	my_strlen(s, sizeof(s), __FILE__, __LINE__)
#define strdup(s)	my_strdup(s, sizeof(s), __FILE__, __LINE__)
#define strstr(s1,s2)	my_strstr(s1, sizeof(s1), s2, sizeof(s2), __FILE__, __LINE__)
#define strpbrk(s1,s2)	my_strpbrk(s1, sizeof(s1), s2, sizeof(s2), __FILE__, __LINE__)
#define strchr(s,c)	my_strchr(s, sizeof(s1), c, __FILE__, __LINE__)
#define strrchr(s,c)	my_strrchr(s, sizeof(s1), c, __FILE__, __LINE__)
#define strspn(s1,s2)	my_strspn(s1, sizeof(s1), s2, sizeof(s2), __FILE__, __LINE__)
#define strcspn(s1,s2)	my_strcspn(s1, sizeof(s1), s2, sizeof(s2), __FILE__, __LINE__)

/* File debugging */

extern FILE *my_fopen(char *n, char *m, char *f, int l);
extern int   my_fclose(FILE *fp, char *f, int l);
extern int   my_open(char *n, int m, int a, char *f, int l);
extern int   my_close(int h, char *f, int l);
extern int   my_dup(int h, char *f, int l);
extern int   my_read(int h, void *buf, unsigned len, int hint, char *f, int l);
extern size_t my_fread(void *buf, size_t size, size_t n, FILE *fp, int space_avail, char *f, int l);
extern char *my_fgets(void *buf, int n, FILE *fp, int space_avail, char *f, int l);

#define fopen(n,m)	my_fopen(n,m,__FILE__,__LINE__)
#define fclose(f)	my_fclose(f,__FILE__,__LINE__)
#define open(n,m,a)	my_open(n,m,a,__FILE__,__LINE__)
#define close(h)	my_close(h,__FILE__,__LINE__)
#define dup(h)		my_dup(h,__FILE__,__LINE__)
#define read(h,b,n)	my_read(h, b, n, sizeof(b), __FILE__, __LINE__)
#define fread(b,s,n,f)	my_fread(b, s, n, f, sizeof(b), __FILE__, __LINE__)
#define fgets(b,n,f)	my_fgets(b, n, f, sizeof(b), __FILE__, __LINE__)

#endif /* GW_DEBUG */

#endif /* __GWDEBUG_H__ */





---------- gwdebug.c -------------------------------------------------------
/*
 * A drop-in debugging library for C v2.0
 *
 * (c) 1994 by Graham Wheeler, All Rights Reserved
 *
 * This code may be freely redistributed, provided that it is
 * not modified in any way. If you make changes to this code 
 * that you feel are useful, and would like to see them 
 * incorporated, please send mail to gram@cdsec.com
 *
 * Only change since v1.1 is that DOS support has been removed.
 *
 * This library provides drop-in wrappers for the following
 * common C library routines:
 *
 * Memory allocation routines:
 *	malloc(), calloc(), free(), realloc()
 *
 * String and memory routines:
 *	strcpy(), strncpy(), memcpy(), memset(), memmove(),
 *	strcmp(), strncmp(), memcmp(), strlen(), strdup(),
 *	strstr(), strpbrk(), stpcpy(), strcat(), strncat(),
 *	strchr(), strrchr(), strspn(), strcspn()
 *
 * File I/O routines:
 *	open(), close(), dup(), fopen(), fclose(),
 *	read(), fread(), fgets()
 * 
 * The library catches:
 *
 * - failed mallocs/callocs/reallocs and bad frees
 * - memory and file leaks
 * - some bad parameters passed to these routines
 *
 * In addition:
 *
 * - some overruns of dynamic, global and auto buffers are caught,
 *	reported and recovered from (memcpy, memset,
 *	strcpy, strncpy, stpcpy, read, fread, fgets)
 * - some potential overruns are warned about
 * - attempts to memcpy/strcpy/etc uninitialised memory is reported
 * - all overruns of dynamic buffers are reported when the memory
 *	is freed. Overruns of less than 5 bytes will not clobber the
 *	heap.
 *
 * To use the library, link this file with your application.
 * Add a line `#include "gwdebug.h" to each source file of
 * your application, AFTER any other #includes.
 * Then compile with GW_DEBUG defined to use the library.
 *
 * You can define DEBUG_LOG to be a log file name; else
 * standard error is used.
 *
 */

#ifdef GW_DEBUG

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <assert.h>

static FILE *logfile = NULL;
static char *store_name(char *name);
static void my_initialise(void);
static void my_report(void);

/*******************************/
/* Memory allocation debugging */
/*******************************/

#define MAGIC			(0x24681357L)

/* Freeing a blk_info header will usually result in the memory
   manager using the first few bytes to store the block on the
   free list. The fields at the start of blk_info have been
   chosen to be the ones we don't mind being clobbered.
*/

typedef struct
{
    long	magic;
    void 	*next;
    long	nbytes;
    char       *file;
    short	line;
} blk_info;

#define GET_BLKP(p, o)		(( (blk_info *)(p) ) + (o))
#define GET_BLK(p)		(blk_info *)GET_BLKP(p, -1)
#define GET_DATA(p)		((void *)GET_BLKP(p, 1))
#define BUMPSIZE(n)		((n)+sizeof(blk_info) + sizeof(long))
#define SET_ENDMAGIC(p,n)	*(long *)( ((char *)p)+n ) = MAGIC
#define TST_ENDMAGIC(p,n)	(*(long *)( ((char *)p)+n ) == MAGIC)

void my_free(void *p, char *f, int l);

static void *heap_list_head = NULL;

void my_memory_report(int is_last)
{
    void *p;
    /* walk dat list... */
    p = heap_list_head;
    if (p)
    {
    	fprintf(logfile,is_last ? "MEMORY LEAKS:\n" : "Allocated Memory Blocks:\n");
    	while (p)
    	{
    	    blk_info *bp = GET_BLK(p);
    	    fprintf(logfile,"\tSize %8ld File %16s Line %d\n",
    		    bp->nbytes, bp->file, bp->line);
	    p = bp->next;
	}
    }
}

static void log_alloc(void *rtn, unsigned long n, char *f, int l)
{
    char *savedname;
    blk_info *bp = (blk_info *)rtn;
    assert(rtn);
    if (!logfile) my_initialise();
    /* Get the pointer that is returned to the user */
    rtn = GET_DATA(rtn);
    /* Save the size information */
    bp->nbytes = n;
    /* Prepend to front of heap list */
    bp->next = heap_list_head;
    heap_list_head = rtn;
    /* Save file name and line number, and put in bounding magic markers */
    bp->file = store_name(f);
    bp->line = l;
    bp->magic = MAGIC;
    SET_ENDMAGIC(rtn, n);
}

void *my_calloc(unsigned n, char *f, int l)
{
    /* Allocate the memory with space enough for our info */
    void *rtn = calloc(BUMPSIZE(n), 1);
    log_alloc((void *)rtn, (unsigned long)n, f, l);
    return GET_DATA(rtn);
}

void *my_malloc(unsigned n, char *f, int l)
{
    void *rtn = my_calloc(n,f,l);
    if (n >= sizeof(long))
        *((unsigned long *)rtn) = MAGIC; /* mark as unitialised */
    return rtn;
}

static int log_free(void *p, char *f, int l)
{
    void *tmp, *last = NULL;
    blk_info *bp;
    if (!logfile) my_initialise();
    /* Search the list for the block */
    tmp = heap_list_head;
    while (tmp)
    {
    	bp = GET_BLK(tmp);
    	if (tmp == p) break;
    	if (bp->magic != MAGIC) goto error;
    	else
    	{
    	    last = tmp;
    	    tmp = bp->next;
    	}
    }
    if (tmp)
    {
    	if (!TST_ENDMAGIC(p, bp->nbytes))
    	    fprintf(logfile,"Block of size %ld allocated at %s, line %d, freed at %s, line %d, has been overrun\n",
    			bp->nbytes, bp->file, bp->line, f, l);
    	/* Unlink from chain */
    	if (last==NULL)
    	    heap_list_head = bp->next;
    	else
    	    (GET_BLK(last))->next = bp->next;
    	/* save who freed */
    	bp->file = store_name(f);
    	bp->line = l;
    	/* trash contents */
	if (bp->nbytes >= sizeof(long))
            *((unsigned long *)GET_DATA(bp)) = MAGIC;
	return 0;
    }
    else bp = GET_BLK(p);
error:
    fprintf(logfile,"Bad call to free from file %s, line %d\n",f,l);
    /* Next check can cause seg violation on some UNIXes; if so change 1 to 0 */
#if 1
    if (bp && bp->magic==MAGIC)
    	fprintf(logfile,"Possibly freed before at %s, line %d, size %ld\n",
    		bp->file, bp->line, bp->nbytes);
#endif
    return -1;
}

void my_free(void *p, char *f, int l)
{
    if (log_free((void *)p, f, l) == 0)
        free(GET_BLK(p));
}

static blk_info *my_find_block(void *p)
{
    blk_info *bp = GET_BLK(p);
    void *tmp = heap_list_head;
    /* Search the list for the block. We don't just check for the magic
    	number under UNIX as this can cause a segmentation violation */
    while (tmp)
    {
    	if ((void *)tmp == (void *)p)
	    return GET_BLK(p);
    	tmp = GET_BLK(tmp)->next;
    }
    return NULL;
}

static int my_sizehint(void *p, int size)
{
    void *tmp;
    blk_info *bp;
    if (p == NULL) return -1;
    bp = my_find_block(p);
    if (bp)
    {
    	assert(size == sizeof(char *));
    	return (int)(bp->nbytes);
    }
    else if (size == sizeof(char *))
    	return -1; /* no clues */
    else return size;
}

/*****************************/
/* String function debugging */
/*****************************/

/* flags for additional checks */

#define INIT1	1	/* arg s1 must be initialised */
#define INIT2	2	/* arg s2 must be initialised */
#define NULLT	4	/* must be initialised with NUL-terminated strings */

/* Validate a single pointer arg */

static int my_init_check(char *p, int space, int nul)
{
    blk_info *bp = my_find_block(p);
    if (bp)
    {
	if (bp->nbytes>=sizeof(long) && *((unsigned long *)p)==MAGIC)
	    return 0;
	return 1;
    }
    else if (space >= 0 && nul)
    {
	int i;
	for (i = 0; i < space; i++)
	    if (p[i] == 0)
		return 1;
	return 0;
    }
    return 1; /* we don't know, so we assume OK */
}

static int my_validate1(char *name, char *s, int space, char *f, int l,
	unsigned flags)
{
    if (s==NULL)
    {
    	fprintf(logfile,"%s(NULL) at file %s, line %d\n", name, f, l);
        return -1;
    }
    else if (flags & INIT1)
    {
	if (my_init_check(s, space, (flags&NULLT)!=0) == 0)
	{
    	    fprintf(logfile,"%s(uninitialised) at file %s, line %d\n", name, f, l);
            return -1;
	}
    }
    return 0;
}

/* Validate a pair of pointer args */

static int my_validate2(char *name, char *s1, int siz1, char *s2,
	int siz2, char *f, int l, unsigned flags)
{
    /* Just validate arguments */
    if (s1==NULL && s2==NULL)
    	fprintf(logfile,"%s(NULL,NULL) at file %s, line %d\n",
    		name, f, l);
    else if (s1==NULL)
    	fprintf(logfile,"%s(NULL,...) at file %s, line %d\n", name, f, l);
    else if (s2==NULL)
    	fprintf(logfile,"%s(...,NULL) at file %s, line %d\n", name, f, l);
    else if ((flags&INIT1) && my_init_check(s1, siz1, (flags&NULLT)!=0) == 0)
        fprintf(logfile,"%s(invalid,...) at file %s, line %d\n", name, f, l);
    else if ((flags&INIT2) && my_init_check(s2, siz2, (flags&NULLT)!=0) == 0)
        fprintf(logfile,"%s(...,invalid) at file %s, line %d\n", name, f, l);
    else return 0;
    return -1;
}

/* Handler for strcpy, stpcpy, strncpy, strcat, strncat, memcpy,
	and memmove */

char *my_copy(char *d, int space_avail, int limit, char *s,
	int sspace, int flag, char *f, int l, unsigned strflags)
{
    char *rtn = d;
    int space_needed, i, dlen;
    if (!logfile) my_initialise();
    space_avail = my_sizehint(d, space_avail);
    sspace = my_sizehint(s, sspace);
    if (my_validate2(strflags?"String copy":"Mem copy", d, space_avail,
		s, sspace, f,l,INIT2|strflags))
	return d;
    /* how much space do we need? */
    space_needed = limit ? limit : (strflags ? (strlen(s)+1) : space_avail);
    /* Are we cat'ing? If so, how long is the existing stuff? */
    dlen = (flag&4) ? strlen(d) : 0;
    /* space enuf? */
    limit = space_needed;
    if (space_avail >= 0 && space_needed > (space_avail-dlen))
    {
    	fprintf(logfile,"String/memory copy overrun at file %s, line %d - truncating\n\ttarget space %d, source length %d\n",
    		f, l, (space_avail-dlen), space_needed);
    	limit = space_avail-dlen;
    }
    else if (space_avail >= 0 && sspace >= 0 && sspace > (space_avail-dlen))
    	fprintf(logfile,"Potential string/memory copy overrun at file %s, line %d\n\ttarget space %d, source size %d\n",
    		f, l, (space_avail-dlen), sspace);
    d += dlen;
    if (s<d && (s+limit)>=d && (flag&1)==0) /* forward copy would clobber source */
    	fprintf(logfile,"String/memory copy clobber in %s, line %d\n", f, l);
    memmove(d,s,limit);
    return rtn + ( (flag & 2) ? (limit-1) : 0); /* hax for stpcpy */
}

char *my_memcpy(char *d, int space_avail, int limit, char *s,
	int sspace, int flag, char *f, int l)
{
    return my_copy(d, space_avail, limit, s, sspace, flag, f, l, 0);
}

char *my_strcpy(char *d, int space_avail, int limit, char *s,
	int sspace, int flag, char *f, int l)
{
    return my_copy(d, space_avail, limit, s, sspace, flag, f, l, NULLT);
}

char *my_memset(char *d, int space_avail, int limit, char c, char *f, int l)
{
    char *rtn = d;
    int space_needed, i;
    if (!logfile) my_initialise();
    /* how much space do we have? */
    space_avail = my_sizehint(d, space_avail);
    if (my_validate1("memset", d, space_avail, f, l, 0)) goto done;
    /* space enuf? */
    if (space_avail >= 0 && limit > space_avail)
    {
    	fprintf(logfile,"memset overrun at file %s, line %d - truncating\n\ttarget length %d, source length %d\n",
    		f, l, space_avail, limit);
    	limit = space_avail;
    }
    memset(d,limit,c);
done:
    return rtn;
}

static int my_compare(char *s1, int siz1, char *s2, int siz2, int n,
	int flag, char *f, int l, unsigned strflags)
{
    if (!logfile) my_initialise();
    /* Just validate arguments */
    siz1 = my_sizehint(s1, siz1);
    siz2 = my_sizehint(s2, siz2);
    if (my_validate2("memcmp", s1, siz1, s2, siz2, f, l, INIT1|INIT2|strflags))
    	return ((s1?1:0)-(s2?1:0));
    if (n==0) return strcmp(s1,s2);
    else if (flag) return strncmp(s1,s2,n);
    else return memcmp(s1,s2,n);
}

int my_memcmp(char *s1, int siz1, char *s2, int siz2, int n, int flag, char *f, int l)
{
    return my_compare(s1, siz1, s2, siz2, n, flag, f, l, 0);
}

int my_strcmp(char *s1, int siz1, char *s2, int siz2, int n, int flag, char *f, int l)
{
    return my_compare(s1, siz1, s2, siz2, n, flag, f, l, NULLT);
}

int my_strlen(char *s, int siz, char *f, int l)
{
    if (!logfile) my_initialise();
    siz = my_sizehint(s, siz);
    return my_validate1("strlen", s, siz, f, l, INIT1|NULLT) ? 0 : strlen(s);
}

char *my_strdup(char *s, int siz, char *f, int l)
{
    if (!logfile) my_initialise();
    siz = my_sizehint(s, siz);
    if (my_validate1("strdup", s, siz, f, l, INIT1|NULLT)==0)
    {
    	int n = strlen(s)+1;
    	char *rtn = my_calloc(n,f,l);
    	assert(rtn);
    	my_memcpy(rtn, n, n, s, n, 0, f, l);
    	return rtn;
    }
    return NULL;
}

char *my_strstr(char *s1, int siz1, char *s2, int siz2, char *f, int l)
{
    if (!logfile) my_initialise();
    siz1 = my_sizehint(s1, siz1);
    siz2 = my_sizehint(s2, siz2);
    return my_validate2("strstr", s1, siz1, s2, siz2, f, l, INIT1|INIT2|NULLT)
	? NULL : strstr(s1,s2);
}

char *my_strpbrk(char *s1, int siz1, char *s2, int siz2, char *f, int l)
{
    if (!logfile) my_initialise();
    siz1 = my_sizehint(s1, siz1);
    siz2 = my_sizehint(s2, siz2);
    return my_validate2("strpbrk", s1, siz1, s2, siz2, f, l, INIT1|INIT2|NULLT)
	? NULL : strpbrk(s1,s2);
}

char *my_strchr(char *s, int siz, int c, char *f, int l)
{
    if (!logfile) my_initialise();
    siz = my_sizehint(s, siz);
    return my_validate1("strchr", s, siz, f, l, INIT1|NULLT)
	? NULL : strchr(s,c);
}

char *my_strrchr(char *s, int siz, int c, char *f, int l)
{
    if (!logfile) my_initialise();
    siz = my_sizehint(s, siz);
    return my_validate1("strrchr", s, siz, f, l, INIT1|NULLT)
	? NULL : strrchr(s,c);
}

int my_strspn(char *s1, int siz1, char *s2, int siz2, char *f, int l)
{
    if (!logfile) my_initialise();
    siz1 = my_sizehint(s1, siz1);
    siz2 = my_sizehint(s2, siz2);
    return my_validate2("strspn", s1, siz1, s2, siz2, f, l, INIT1|INIT2|NULLT)
	? 0 : strspn(s1,s2);
}

int my_strcspn(char *s1, int siz1, char *s2, int siz2, char *f, int l)
{
    if (!logfile) my_initialise();
    siz1 = my_sizehint(s1, siz1);
    siz2 = my_sizehint(s2, siz2);
    return my_validate2("strcspn", s1, siz1, s2, siz2, f, l, INIT1|INIT2|NULLT)
	? 0 : strcspn(s1,s2);
}

/***************************/
/* File function debugging */
/***************************/

#ifndef MAX_FILES
#define MAX_FILES		64
#endif

typedef struct
{
    char *name;
    char *fname;
    int line;
    FILE *fp;
} file_info_t;

static file_info_t file_info[MAX_FILES];

#include <fcntl.h>

FILE *my_fopen(char *n, char *m, char *f, int l)
{
    FILE *rtn;
    if (!logfile) my_initialise();
    rtn = fopen(n,m);
    if (rtn)
    {
    	int h = fileno(rtn);
    	if (file_info[h].line>0)
    	    /* shouldn't happen unless system fopen is broken */
    	    fprintf(logfile,"%2d (%s) fopened at %s, line %d was already opened at %s, line %d\n",
    	    	h, n, f, l, file_info[h].fname, file_info[h].line);
#ifdef GW_TRACE
    	else
    	    fprintf(logfile,"File %2d (%s) fopened at %s, line %d\n", h, n, f, l);
#endif
    	file_info[h].name = store_name(n);
    	file_info[h].fname = store_name(f);
    	file_info[h].line = l;
    	file_info[h].fp = rtn;
    }
    else fprintf(logfile,"fopen of %s at %s, line %d failed!\n", n, f, l);
    return rtn;
}

int my_fclose(FILE *fp, char *f, int l)
{
    if (!logfile) my_initialise();
    if (fp)
    {
    	int h = fileno(fp);
    	if (file_info[h].line <= 0)
    	    fprintf(logfile,"Bad close(%d) at %s, line %d; already closed at %s, line %d\n",
    	    	    h, f, l, file_info[h].fname, -file_info[h].line);
    	else
    	{
#ifdef GW_TRACE
    	    fprintf(logfile,"File %2d (%s) opened at %s, line %d fclosed at %s, line %d\n",
    	    			h, file_info[h].name, file_info[h].fname,
    	    			file_info[h].line, f, l);
#endif
    	    file_info[h].fname = store_name(f);
    	    file_info[h].line = -l;
    	    return fclose(fp);
    	}
    }
    else fprintf(logfile,"Illegal fclose(NULL) by %s line %d\n",
			 f, l);
    return 0;
}

int my_open(char *n, int m, int a, char *f, int l)
{
    int rtn;
    if (!logfile) my_initialise();
    rtn = open(n,m,a);
    if (rtn >= 0)
    {
    	if (file_info[rtn].line>0)
    	    /* shouldn't happen unless system fopen is broken */
    	    fprintf(logfile,"%2d (%s) fopened at %s, line %d was already open at %s, line %d\n",
    	    	rtn, n, f, l, file_info[rtn].fname, file_info[rtn].line);
#ifdef GW_TRACE
    	else
    	    fprintf(logfile,"File %2d (%s) fopened at %s, line %d\n", rtn, n, f, l);
#endif
    	file_info[rtn].name = store_name(n);
    	file_info[rtn].fname = store_name(f);
    	file_info[rtn].line = l;
    	file_info[rtn].fp = NULL;
    }
    else fprintf(logfile,"open of %s at %s, line %d failed!\n\t(%s)\n",
			n, f, l, strerror(errno));
    return rtn;
}

int my_close(int h, char *f, int l)
{
    if (!logfile) my_initialise();
    if (h>=0)
    {
    	if (file_info[h].line <= 0)
    	    fprintf(logfile,"Bad close(%d) by %s, line %d; already closed at %s, line %d\n",
    	    	h, f, l, file_info[h].fname, -file_info[h].line);
    	else
    	{
#ifdef GW_TRACE
    	    fprintf(logfile,"File %2d (%s) opened at %s, line %d, fclosed at %s, line %d\n",
    	    			h, file_info[h].name, file_info[h].fname,
    	    			file_info[h].line, f, l);
#endif
    	    file_info[h].fname = store_name(f);
    	    file_info[h].line = -l;
    	    return close(h);
    	}
    }
    else fprintf(logfile,"Illegal close(%d) by %s line %d\n", h, f, l);
    return 0;
}

int my_dup(int h, char *f, int l)
{
    int rtn = -1;
    if (!logfile) my_initialise();
    if (h>=0)
    {
    	if (file_info[h].line <= 0)
    	    fprintf(logfile,"Illegal dup(%d) by %s line %d\n", h, f, l);
    	else
    	{
    	    rtn = dup(h);
    	    if (rtn >= 0)
    	    {
#ifdef GW_TRACE
    	    	fprintf(logfile,"File %2d (%s) opened at %s, line %d, dup'ed to %d at %s, line %d\n",
    	    		h,
    	    		file_info[h].name,
    	    		file_info[h].fname,
    	    		file_info[h].line,
    	    		rtn, f, l);
#endif
    	    	file_info[rtn].name = file_info[h].name;
    	    	file_info[rtn].fname = store_name(f);
    	    	file_info[rtn].line = l;
    	    	file_info[rtn].fp = NULL;
    	    }
    	    else fprintf(logfile,"dup(%d) by %s line %d failed!\n\t(%s)\n",
    	    		h, f, l, strerror(errno));
    	}
    }
    else fprintf(logfile,"Illegal dup(%d) by %s line %d!\n", h, f, l);
    return rtn;
}

static int my_readcheck(char *name, void *buf, unsigned len, int space_avail, char *f, int l)
{
    if (buf==NULL)
    {
    	fprintf(logfile,"%s with NULL buffer by %s line %d!\n", name, f, l);
    	return 0;
    }
    space_avail = my_sizehint(buf, space_avail);
    if (space_avail >= 0 && space_avail < len)
    {
    	fprintf(logfile,"%s with count (%d)  > available space (5d) by %s line %d!\n",
    		name, len, space_avail, f, l);
    	return space_avail;
    }
    else return len;
}

int my_read(int h, void *buf, unsigned len, int space_avail, char *f, int l)
{
    if (!logfile) my_initialise();
    len = my_readcheck("read", buf, len, space_avail, f, l);
    return len ? read(h,buf,len) : 0;
}

size_t my_fread(void *buf, size_t size, size_t n, FILE *fp, int space_avail, char *f, int l)
{
    if (!logfile) my_initialise();
    if (size==0)
    {
    	fprintf(logfile,"fread with size zero at %s line %d!\n", f, l);
    	return 0;
    }
    n = my_readcheck("fread", buf, n*size, space_avail, f, l) / size;
    return n ? fread(buf,size,n,fp) : 0;
}

char *my_fgets(void *buf, int n, FILE *fp, int space_avail, char *f, int l)
{
    if (!logfile) my_initialise();
    n = my_readcheck("fgets", buf, n, space_avail, f, l);
    return n ? fgets(buf,n,fp) : buf;
}

static void my_file_report(int is_last)
{
    int i, done_heading=0;
    for (i=0;i<40;i++)
    {
    	if (file_info[i].line>0)
    	{
    	    if (is_last && !done_heading)
    	    {
    		done_heading = 1;
    		fprintf(logfile,"FILE LEAKS:\n");
    	    }
    	    fprintf(logfile,"\tFile `%s' (handle %d) opened at %s, line %d\n",
	    		file_info[i].name,i,file_info[i].fname,	file_info[i].line);
	}
    }
}

/*************************/
/* Building on the past! */
/*************************/

void *my_realloc(void *p, unsigned n, char *f, int l)
{
    void *rtn = my_calloc(n,f,l);
    if (p)
    {
    	my_memcpy(rtn, n, 0, p, 0, 0, f, l);
    	my_free(p,f,l);
    }
    return rtn;
}

/**********************/
/* Managed name store */
/**********************/

#ifndef MAX_NAMES
#define MAX_NAMES	32
#endif

#ifndef MAX_NAME_LEN
#define MAX_NAME_LEN	16
#endif

static char file_names[MAX_NAMES][MAX_NAME_LEN];

static int filename_index = 0;

static char *store_name(char *name)
{
    int filename_index;
    for (filename_index=0 ; filename_index<MAX_NAMES ; filename_index++)
    {
    	if (file_names[filename_index][0]=='\0')
    	{
    	    strcpy(file_names[filename_index], name);
    	    return file_names[filename_index];
    	}
    	else if (strcmp(file_names[filename_index],name)==0)
    	    return file_names[filename_index];
    }
    return NULL;
}

/********************************/
/* Generate a report at the end */
/********************************/

void my_report(void)
{
    time_t tm = time(NULL);
    if (!logfile) my_initialise();
    fprintf(logfile,"\n\n================ END-OF-PROGRAM DEBUG LOG ===================\n");
    fprintf(logfile,"Log date: %s\n\n", ctime(&tm));
    my_memory_report(1);
    fprintf(logfile,"\n\n");
    my_file_report(1);
    fprintf(logfile,"\n\n================ END OF LOG ===================\n\n");
#ifdef DEBUG_LOG
    fclose(logfile);
#endif
    logfile=NULL;
}

/*****************************/
/* First-time initialisation */
/*****************************/

void my_initialise(void)
{
    atexit(my_report);
#ifdef DEBUG_LOG
    logfile = fopen(DEBUG_LOG,"w");
    assert(logfile);
#else
    logfile = stderr;
#endif
}

#endif /* GW_DEBUG */





--------- gwtest.c ------------------------------------------------------

/* Sample program illustrating some of the features of the gwdebug library */

#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>

#include "gwdebug.h"

typedef void *heap_ptr;

void leakTest(void)
{
    (void)malloc(10);
}

void badFreeTest(void)
{
    free((void *)26304);
}

void doubleFreeTest(void)
{
    heap_ptr p = malloc(10);
    free(p);
    free(p);
}

void overrunTest1(void)
{
    heap_ptr p = malloc(10);
    char *d = (char *)p,
    	 *s = "Hello World";
    while ((*d++ = *s++) != 0);
    free(p);
}

void overrunTest2(void)
{
    heap_ptr *p = malloc(10);
    strcpy((char *)p, "Hello world");
    free(p);
}

void overrunTest3(void)
{
    char dest[8];
    strcpy(dest,"Hello world");
    strncpy(dest,"Hello world",8);
    strncpy(dest,"Hello world",9);
}

void fileTest(void)
{
	FILE *fp;
	int h = open("junk", O_CREAT, S_IWRITE), h2;
	close(h);
	h2 = dup(h);
	close(h);
	close(h2);
	fp = fopen("leak", "w");
	(void)fp;
}

void initTest(void)
{
    /* the tests here marked ** will only succeed is junk[] has no
	zero bytes in it after being created on the stack; luck of the
	draw, unfortunately */
    heap_ptr *p = malloc(10);
    char junk[10];
/**/(void)strlen(junk);
    (void)strlen((char *)p);
    (void)strcpy(junk, (char *)p);
    (void)memcpy(junk, (char *)p, 10);
/**/(void)strcpy((char *)p, junk);
    (void)memcpy((char *)p, junk, 10); /* this is undetectable */
    free(p);
}

void main(void)
{
    int ch = 0;
    while (ch != 'q')
    {
    	printf("Enter:\n\tq - to quit\n\t1 - for leak test\n");
    	printf("\t2 - for overrun test 1\n\t3 - for overrun test 2\n");
    	printf("\t4 - for overrun test 3\n");
    	printf("\t5 - for bad free\n\t6 - for double free\n");
    	printf("\t7 - for file test\n\t8 - for uninit test\n");
    	ch=getchar();
    	switch(ch)
    	{
    	case '1': leakTest();		break;
    	case '2': overrunTest1();	break;
    	case '3': overrunTest2();	break;
    	case '4': overrunTest3();	break;
    	case '5': badFreeTest();	break;
    	case '6': doubleFreeTest(); 	break;
    	case '7': fileTest(); 		break;
    	case '8': initTest(); 		break;
    	}
    	while (getchar() != '\n');
    }
}

--------- Makefile ------------------------------------------------------
DEBUG=-DGW_DEBUG
#DEBUG=

CC=cc
CFLAGS=-g $(DEBUG)
LOG=\"gwdebug.log\"

all: gwtest

gwtest: gwtest.o gwdebug.o
	$(CC) $(CFLAGS) gwtest.o gwdebug.o -o gwtest

gwtest.o: gwtest.c gwdebug.h
	$(CC) -c $(CFLAGS) gwtest.c

gwdebug.o: gwdebug.c gwdebug.h
	$(CC) -c $(CFLAGS) -DDEBUG_LOG=$(LOG) gwdebug.c




-- 
Dr Graham Wheeler                          E-mail: gram@cdsec.com
Citadel Data Security                      Phone:  +27(21)23-6065/6/7
Internet/Intranet Network Specialists      Mobile: +27(83)-253-9864
Firewalls/Virtual Private Networks         Fax:    +27(21)24-3656
Data Security Products                     WWW:    http://www.cdsec.com/






Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199709181722.TAA00606>