Pointer validation code

Mike Muuss mike at BRL.MIL
Tue Feb 5 09:31:51 AEST 1991


I handle memory corruption and pointer mis-handling in a rather
different manner.  I have "wrapper" subroutines called
rt_malloc(), rt_free(), rt_calloc() that take an extra string
argument which indicates the "purpose" of this allocation/free.

Depending on the setting of some global variables, you can
independently enable memory activity logging, and memory
checking.  The checking includes adding a "barrier" word at the
end of the allocation, to ensure that the application is not
running off the end.  It also maintains a full table of what memory
is being used.  This table can be printed at any time, by calling
a subroutine (either from the application, or via a "dbx -P").

One final note is that most of our application's data structures have
been designed with "magic numbers" as their first word, so that
subroutines can validate that they have been given pointers to the
correct kind of object.

These two techniques have been very powerful, and have virtually eliminated
the programming difficulties associated with using very complex
dynamic memory allocation in C.

	Best,
	 -Mike

------
/*
 *			S T O R A G E . C
 *
 * Ray Tracing program, storage manager.
 *
 *  Functions -
 *	rt_malloc	Allocate storage, with visibility & checking
 *	rt_free		Similarly, free storage
 *	rt_realloc	Reallocate storage, with visibility & checking
 *	rt_calloc	Allocate zero'ed storage
 *	rt_prmem	When debugging, print memory map
 *	rt_strdup	Duplicate a string in dynamic memory
 *
 *  Author -
 *	Michael John Muuss
 *  
 *  Source -
 *	SECAD/VLD Computing Consortium, Bldg 394
 *	The U. S. Army Ballistic Research Laboratory
 *	Aberdeen Proving Ground, Maryland  21005-5066
 *  
 */
#ifndef lint
static char RCSstorage[] = "@(#)$Header: /m/cad/librt/RCS/storage.c,v 9.14 91/01/29 10:40:24 cjohnson Exp $";
#endif

#include <stdio.h>
#include "machine.h"
#include "vmath.h"
#include "raytrace.h"
#include "./debug.h"

#ifdef BSD
# include <strings.h>
#else
# include <string.h>
#endif

#define MDB_MAGIC	0x12348969
struct memdebug {
	char	*mdb_addr;
	char	*mdb_str;
	int	mdb_len;
};
static struct memdebug	*rt_memdebug;
static int		rt_memdebug_len = 0;
#define MEMDEBUG_NULL	((struct memdebug *)0)

/*
 *			R T _ M E M D E B U G _ A D D
 *
 *  Add another entry to the memory debug table
 */
HIDDEN void
rt_memdebug_add( ptr, cnt, str )
char	*ptr;
unsigned int cnt;
char	*str;
{
	register struct memdebug *mp;
top:
	if( rt_g.rtg_parallel )  {
		RES_ACQUIRE( &rt_g.res_syscall );		/* lock */
	}
	if( rt_memdebug )  {
		mp = &rt_memdebug[rt_memdebug_len-1];
		for( ; mp >= rt_memdebug; mp-- )  {
			/* Search for an empty slot */
			if( mp->mdb_len > 0 )  continue;
			mp->mdb_addr = ptr;
			mp->mdb_len = cnt;
			mp->mdb_str = str;
			if( rt_g.rtg_parallel ) {
				RES_RELEASE( &rt_g.res_syscall ); /* unlock */
			}
			return;
		}
	}

	/* Need to make more slots */
	if( rt_memdebug_len <= 0 )  {
		rt_memdebug_len = 510;
		rt_memdebug = (struct memdebug *)calloc(
			rt_memdebug_len, sizeof(struct memdebug) );
	} else {
		int	old_len = rt_memdebug_len;
		rt_memdebug_len *= 4;
		rt_memdebug = (struct memdebug *)realloc(
			(char *)rt_memdebug,
			sizeof(struct memdebug) * rt_memdebug_len );
		bzero( (char *)&rt_memdebug[old_len],
			(rt_memdebug_len-old_len) * sizeof(struct memdebug) );
	}
	if( rt_g.rtg_parallel ) {
		RES_RELEASE( &rt_g.res_syscall );		/* unlock */
	}
	if( rt_memdebug == (struct memdebug *)0 )
		rt_bomb("rt_memdebug_add() malloc failure\n");
	goto top;
}

/*
 *			R T _ M E M D E B U G _ C H E C K
 *
 *  Check an entry against the memory debug table, based upon it's address.
 */
HIDDEN struct memdebug *
rt_memdebug_check( ptr, str )
register char	*ptr;
char		*str;
{
	register struct memdebug *mp = &rt_memdebug[rt_memdebug_len-1];
	register long	*ip;

	if( rt_memdebug == (struct memdebug *)0 )  {
		rt_log("rt_memdebug_check(x%x, %s)  no memdebug table yet\n",
			ptr, str);
		return MEMDEBUG_NULL;
	}
	for( ; mp >= rt_memdebug; mp-- )  {
		if( mp->mdb_len <= 0 )  continue;
		if( mp->mdb_addr != ptr )  continue;
		ip = (long *)(ptr+mp->mdb_len-sizeof(long));
		if( *ip != MDB_MAGIC )  {
			rt_log("ERROR rt_memdebug_check(x%x, %s) barrier word corrupted!\nbarrier at x%x was=x%x s/b=x%x, len=%d\n",
				ptr, str, ip, *ip, MDB_MAGIC, mp->mdb_len);
		}
		return(mp);		/* OK */
	}
	return MEMDEBUG_NULL;
}

/*
 *			R T _ M E M D E B U G _ M O V E
 *
 *  realloc() has moved to a new memory block.
 *  Update our notion as well.
 */
HIDDEN void
rt_memdebug_move( old_ptr, new_ptr, new_cnt, new_str )
char	*old_ptr;
char	*new_ptr;
int	new_cnt;
char	*new_str;
{
	register struct memdebug *mp = &rt_memdebug[rt_memdebug_len-1];

	if( rt_memdebug == (struct memdebug *)0 )  {
		rt_log("rt_memdebug_move(x%x, x%x, %d., %s)  no memdebug table yet\n",
			old_ptr, new_ptr, new_cnt, new_str);
		return;
	}
	for( ; mp >= rt_memdebug; mp-- )  {
		if( mp->mdb_len > 0 && (mp->mdb_addr == old_ptr) ) {
			mp->mdb_addr = new_ptr;
			mp->mdb_len = new_cnt;
			mp->mdb_str = new_str;
			return;
		}
	}
	rt_log("rt_memdebug_move(): old memdebug entry not found!\n");
	rt_log(" old_ptr=x%x, new_ptr=x%x, new_cnt=%d., new_str=%s\n",
		old_ptr, new_ptr, new_cnt, new_str );
}

/*
 *			R T _ M A L L O C
 *
 *  This routine only returns on successful allocation.
 *  Failure results in rt_bomb() being called.
 */
char *
rt_malloc(cnt, str)
unsigned int cnt;
char	*str;
{
	register char *ptr;

	if( cnt == 0 )  {
		rt_log("ERROR: rt_malloc count=0 %s\n", str );
		rt_bomb("ERROR: rt_malloc(0)\n");
	}
	if( rt_g.debug&DEBUG_MEM_FULL )  {
		/* Pad, plus full int for magic number */
		cnt = (cnt+2*sizeof(long)-1)&(~(sizeof(long)-1));
	}
	if( rt_g.rtg_parallel )  {
		RES_ACQUIRE( &rt_g.res_syscall );		/* lock */
	}
	ptr = malloc(cnt);
	if( rt_g.rtg_parallel ) {
		RES_RELEASE( &rt_g.res_syscall );		/* unlock */
	}

	if( ptr==(char *)0 || rt_g.debug&DEBUG_MEM )
		rt_log("%7x malloc%6d %s\n", ptr, cnt, str);
	if( ptr==(char *)0 )  {
		rt_log("rt_malloc: Insufficient memory available, sbrk(0)=x%x\n", sbrk(0));
		rt_bomb("rt_malloc: malloc failure");
	}
	if( rt_g.debug&DEBUG_MEM_FULL )  {
		rt_memdebug_add( ptr, cnt, str );

		/* Install a barrier word at the end of the dynamic arena */
		/* Correct location depends on 'cnt' being rounded up, above */

		*((long *)(ptr+cnt-sizeof(long))) = MDB_MAGIC;
	}
	return(ptr);
}

/*
 *			R T _ F R E E
 */
void
rt_free(ptr,str)
char	*ptr;
char	*str;
{
	if(rt_g.debug&DEBUG_MEM) rt_log("%7x free %s\n", ptr, str);
	if(ptr == (char *)0)  {
		rt_log("%7x free ERROR %s\n", ptr, str);
		return;
	}
	if( rt_g.debug&DEBUG_MEM_FULL )  {
		struct memdebug	*mp;
		if( (mp = rt_memdebug_check( ptr, str )) == MEMDEBUG_NULL )  {
			rt_log("ERROR rt_free(x%x, %s) pointer bad, or not allocated with rt_malloc!\n",
				ptr, str);
		} else {
			mp->mdb_len = 0;	/* successful delete */
		}
	}
	if( rt_g.rtg_parallel ) {
		RES_ACQUIRE( &rt_g.res_syscall );		/* lock */
	}
	*((int *)ptr) = -1;	/* zappo! */
	free(ptr);
	if( rt_g.rtg_parallel ) {
		RES_RELEASE( &rt_g.res_syscall );		/* unlock */
	}
}

/*
 *			R T _ R E A L L O C
 */
char *
rt_realloc(ptr, cnt, str)
register char *ptr;
unsigned int cnt;
char *str;
{
	char	*original_ptr = ptr;

	if( rt_g.debug&DEBUG_MEM_FULL )  {
		if( rt_memdebug_check( ptr, str ) == MEMDEBUG_NULL )  {
			rt_log("%7x realloc%6d %s ** barrier check failure\n",
				ptr, cnt, str );
		}
		/* Pad, plus full int for magic number */
		cnt = (cnt+2*sizeof(long)-1)&(~(sizeof(long)-1));
	}

	if( rt_g.rtg_parallel ) {
		RES_ACQUIRE( &rt_g.res_syscall );		/* lock */
	}
	ptr = realloc(ptr,cnt);
	if( rt_g.rtg_parallel ) {
		RES_RELEASE( &rt_g.res_syscall );		/* unlock */
	}

	if( ptr==(char *)0 || rt_g.debug&DEBUG_MEM )  {
		rt_log("%7x realloc%6d %s %s\n", ptr, cnt, str,
			ptr == original_ptr ? "[grew in place]" : "[moved]" );
	}
	if( ptr==(char *)0 )  {
		rt_log("rt_realloc: Insufficient memory available, sbrk(0)=x%x\n", sbrk(0));
		rt_bomb("rt_realloc: malloc failure");
	}
	if( rt_g.debug&DEBUG_MEM_FULL )  {
		/* Even if ptr didn't change, need to update cnt & barrier */
		rt_memdebug_move( original_ptr, ptr, cnt, str );

		/* Install a barrier word at the end of the dynamic arena */
		/* Correct location depends on 'cnt' being rounded up, above */
		*((long *)(ptr+cnt-sizeof(long))) = MDB_MAGIC;
	}
	return(ptr);
}

/*
 *			R T _ C A L L O C
 */
char *
rt_calloc( nelem, elsize, str )
unsigned int	nelem;
unsigned int	elsize;
char		*str;
{
	unsigned	len;
	char		*ret;

	ret = rt_malloc( (len = nelem*elsize), str );
#ifdef SYSV
	(void)memset( ret, '\0', len );
#else
	bzero( ret, len );
#endif
	return(ret);
}

/*
 *			R T _ P R M E M
 * 
 *  Print map of memory currently in use.
 */
void
rt_prmem(str)
char *str;
{
	register struct memdebug *mp;
	register int *ip;

	rt_log("\nrt_prmem(): LIBRT memory use (%s)\n", str);
	if( (rt_g.debug&DEBUG_MEM_FULL) == 0 )  {
		rt_log("\tMemory debugging is now OFF\n");
	}
	rt_log("\t%d elements in memdebug table\n", rt_memdebug_len);
	if( rt_memdebug_len <= 0 )  return;

	mp = &rt_memdebug[rt_memdebug_len-1];
	for( ; mp >= rt_memdebug; mp-- )  {
		if( mp->mdb_len <= 0 )  continue;
		ip = (int *)(mp->mdb_addr+mp->mdb_len-sizeof(int));
		rt_log("%7x %5x %s %s\n",
			mp->mdb_addr, mp->mdb_len, mp->mdb_str,
			*ip!=MDB_MAGIC ? "-BAD-" : "" );
		if( *ip != MDB_MAGIC )
			rt_log("\t%x\t%x\n", *ip, MDB_MAGIC);
	}
}

/*
 *			R T _ S T R D U P
 *
 * Given a string, allocate enough memory to hold it using rt_malloc(),
 * duplicate the strings, returns a pointer to the new string.
 */
char *
rt_strdup( cp )
register char *cp;
{
	register char	*base;
	register int	len;

	if(rt_g.debug&DEBUG_MEM) rt_log("rt_strdup(%s) x%x\n", cp, cp);

	len = strlen( cp )+2;
	if( (base = rt_malloc( len, "rt_strdup" )) == (char *)0 )
		rt_bomb("rt_strdup:  unable to allocate memory");

#ifdef BSD
	bcopy( cp, base, len );
#else
	memcpy( base, cp, len );
#endif
	return(base);
}

/*	R T _ C K _ M A L L O C _ P T R
 *
 *	Check the magic number stored with memory allocated with rt_malloc
 *	when DEBUG_MEM_FULL is set.
 *
 *	return:
 *		0	pointer good or DEBUG_MEM_FULL not set
 *		other	memory corrupted.
 */
void
rt_ck_malloc_ptr( ptr, str )
char	*ptr;
char	*str;
{
	register struct memdebug *mp = &rt_memdebug[rt_memdebug_len-1];
	register long	*ip;


	/* if memory debugging isn't turned on, we have no way
	 * of knowing if the pointer is good or not
	 */
	if ((rt_g.debug&DEBUG_MEM_FULL) == 0) return;


	if (ptr == (char *)NULL) {
		rt_log("rt_ck_malloc_ptr(x%x, %s) null pointer\n\n", ptr, str);
		rt_bomb("Goodbye");
	}

	if( rt_memdebug == (struct memdebug *)0 )  {
		rt_log("rt_ck_malloc_ptr(x%x, %s)  no memdebug table yet\n",
			ptr, str);
		rt_bomb("Goodbye");
	}

	for( ; mp >= rt_memdebug; mp-- )  {
		if( mp->mdb_len <= 0 || mp->mdb_addr != ptr )  continue;

		ip = (long *)(ptr+mp->mdb_len-sizeof(long));
		if( *ip != MDB_MAGIC )  {
			rt_log("ERROR rt_ck_malloc_ptr(x%x, %s) barrier word corrupted! was=x%x s/b=x%x\n",
				ptr, str, *ip, MDB_MAGIC);
			rt_bomb("Goodbye");
		}
		return;		/* OK */
	}
	rt_log("ERROR rt_ck_malloc_ptr(x%x, %s)\n\
	pointer not in table of allocated memory.\n", ptr, str);

	rt_bomb("Goodbye");
}



More information about the Comp.sys.sgi mailing list