Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!sun-barr!lll-winken!elroy.jpl.nasa.gov!ncar!gatech!usenet.ins.cwru.edu!agate!ucbvax!BRL.MIL!mike From: mike@BRL.MIL (Mike Muuss) Newsgroups: comp.sys.sgi Subject: Re: Pointer validation code Message-ID: <9102041731.aa11984@WOLF.BRL.MIL> Date: 4 Feb 91 22:31:51 GMT Sender: daemon@ucbvax.BERKELEY.EDU Organization: The Internet Lines: 446 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 #include "machine.h" #include "vmath.h" #include "raytrace.h" #include "./debug.h" #ifdef BSD # include #else # include #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"); }