/* * Article 879 of net.sources: * Relay-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site unisoft.UUCP * Posting-Version: version B 2.10.2 9/18/84; site dataioDataio.UUCP * Path: unisoft!lll-lcc!ucdavis!ucbvax!decvax!tektronix!uw-beaver!uw-june!entropy!dataio!bjorn * From: bjorn@dataioDataio.UUCP (Bjorn Benson) * Newsgroups: net.sources * Subject: Solution to "oops, corrupted memory again!" * Message-ID: <975@dataioDataio.UUCP> * Date: 29 Apr 86 03:04:49 GMT * Date-Received: 2 May 86 18:38:02 GMT * References: <4495@cbrma.UUCP> * Organization: Data I/O Corp., Redmond WA * Lines: 268 * Keywords: malloc free stack tools core memory */ /* * [This posting refers to an article entitled "oops, corrupted memory * again!" in net.lang.c. I am posting it here because it is source.] * * My tool for approaching this problem is to build another level of data * abstraction on top of malloc() and free() that implements some checking. * This does a number of things for you: * - Checks for overruns and underruns on allocated data * - Keeps track of where in the program the memory was malloc'ed * - Reports on pieces of memory that were not free'ed * - Records some statistics such as maximum memory used * - Marks newly malloc'ed and newly free'ed memory with special values * You can use this scheme to: * - Find bugs such as overrun, underrun, etc because you know where * a piece of data was malloc'ed and where it was free'ed * - Find bugs where memory was not free'ed * - Find bugs where newly malloc'ed memory is used without initializing * - Find bugs where newly free'ed memory is still used * - Determine how much memory your program really uses * - and other things */ /* * To implement my scheme you must have a C compiler that has __LINE__ and * __FILE__ macros. If your compiler doesn't have these then (a) buy another: * compilers that do are available on UNIX 4.2bsd based systems and the PC, * and probably on other machines; or (b) change my scheme somehow. I have * recomendations on both these points if you would like them (e-mail please). * * There are 3 functions in my package: * char *NEW( uSize ) Allocate memory of uSize bytes * (equivalent to malloc()) * FREE( pPtr ) Free memory allocated by NEW * (equivalent to free()) * TERMINATE() End system, report errors and stats * I personally use two more functions, but have not included them here: * char *STRSAVE( sPtr ) Save a copy of the string in dynamic memory * char *RENEW( pPtr, uSize ) * (equivalent to realloc()) */ #ifdef PUT_THIS_IN_ROUTINE_CALLING_ME /* * Memory sub-system, written by Bjorn Benson */ extern char *_mymalloc (); #define NEW(SZ) _mymalloc( SZ, __FILE__, __LINE__ ) #define FREE(PTR) _myfree( PTR, __FILE__, __LINE__ ) #endif /* * Memory sub-system, written by Bjorn Benson */ #include typedef unsigned uns; /* Shorthand */ struct irem { struct remember *_pNext; /* Linked list of structures */ struct remember *_pPrev; /* Other link */ char *_sFileName; /* File in which memory was new'ed */ uns _uLineNum; /* Line number in above file */ uns _uDataSize; /* Size requested */ uns _lSpecialValue; /* Underrun marker value */ }; struct remember { struct irem tInt; char aData[1]; }; #define pNext tInt._pNext #define pPrev tInt._pPrev #define sFileName tInt._sFileName #define uLineNum tInt._uLineNum #define uDataSize tInt._uDataSize #define lSpecialValue tInt._lSpecialValue /* * Note: both these refer to the NEW'ed * data only. They do not include * malloc() roundoff or the extra * space required by the remember * structures. */ static long lCurMemory = 0; /* Current memory usage */ static long lMaxMemory = 0; /* Maximum memory usage */ static uns cNewCount = 0; /* Number of times NEW() was called */ /* Root of the linked list of remembers */ static struct remember *pRememberRoot = NULL; #define ALLOC_VAL 0xA5 /* NEW'ed memory is filled with this */ /* value so that references to it will */ /* end up being very strange. */ #define FREE_VAL 0x8F /* FREE'ed memory is filled with this */ /* value so that references to it will */ /* also end up being strange. */ #define MAGICKEY 0x14235296 /* A magic value for underrun key */ #define MAGICEND0 0x68 /* Magic values for overrun keys */ #define MAGICEND1 0x34 /* " */ #define MAGICEND2 0x7A /* " */ #define MAGICEND3 0x15 /* " */ /* Warning: do not change the MAGICEND? values to */ /* something with the high bit set. Various C */ /* compilers (like the 4.2bsd one) do not do the */ /* sign extension right later on in this code and */ /* you will get erroneous errors. */ static void _sanity (); static void _checkchunk (); /* * char * _mymalloc( uns uSize, char *sFile, uns uLine ) * Allocate some memory. */ char *_mymalloc (uSize, sFile, uLine) uns uSize; char *sFile; uns uLine; { extern char *malloc (); struct remember *pTmp; char *pPtr; _sanity (sFile, uLine); /* Allocate the physical memory */ pTmp = (struct remember *) malloc ( sizeof (struct irem) /* remember data */ + uSize /* size requested */ + 4 /* overrun mark */ ); /* Check if there isn't anymore memory avaiable */ if (pTmp == NULL) { fprintf (stderr, "Out of memory at line %d, \"%s\"\n", uLine, sFile); fprintf (stderr, "\t(memory in use: %ld bytes (%ldk))\n", lMaxMemory, (lMaxMemory + 1023L) / 1024L); fflush (stderr); return ((char *) NULL); } /* Fill up the structure */ pTmp -> lSpecialValue = MAGICKEY; pTmp -> aData[uSize + 0] = MAGICEND0; pTmp -> aData[uSize + 1] = MAGICEND1; pTmp -> aData[uSize + 2] = MAGICEND2; pTmp -> aData[uSize + 3] = MAGICEND3; pTmp -> sFileName = sFile; pTmp -> uLineNum = uLine; pTmp -> uDataSize = uSize; pTmp -> pNext = pRememberRoot; pTmp -> pPrev = NULL; /* Add this remember structure to the linked list */ if (pRememberRoot) { pRememberRoot -> pPrev = pTmp; } pRememberRoot = pTmp; /* Keep the statistics */ lCurMemory += uSize; if (lCurMemory > lMaxMemory) { lMaxMemory = lCurMemory; } cNewCount++; /* Set the memory to the aribtrary wierd value */ for (pPtr = &pTmp -> aData[uSize]; pPtr > &pTmp -> aData[0];) { *(--pPtr) = ALLOC_VAL; } /* Return a pointer to the real data */ return (&(pTmp -> aData[0])); } /* * _myfree( char *pPtr, char *sFile, uns uLine ) * Deallocate some memory. */ _myfree (pPtr, sFile, uLine) char *pPtr; char *sFile; uns uLine; { struct remember *pRec; char *pTmp; _sanity (sFile, uLine); /* Check if we have a non-null pointer */ if (pPtr == NULL) { fprintf (stderr, "Freeing NULL pointer at line %d, \"%s\"\n", uLine, sFile); fflush (stderr); return; } /* Calculate the address of the remember structure */ pRec = (struct remember *) (pPtr - (sizeof (struct irem))); /* Check to make sure that we have a real remember structure */ /* Note: this test could fail for four reasons: */ /* (1) The memory was already free'ed */ /* (2) The memory was never new'ed */ /* (3) There was an underrun */ /* (4) A stray pointer hit this location */ /* */ if (pRec -> lSpecialValue != MAGICKEY) { fprintf (stderr, "Freeing unallocated data at line %d, \"%s\"\n", uLine, sFile); fflush (stderr); return; } /* Remove this structure from the linked list */ if (pRec -> pPrev) { pRec -> pPrev -> pNext = pRec -> pNext; } else { pRememberRoot = pRec -> pNext; } if (pRec -> pNext) { pRec -> pNext -> pPrev = pRec -> pPrev; } /* Mark this data as free'ed */ for (pTmp = &pRec -> aData[pRec -> uDataSize]; pTmp > &pRec -> aData[0];){ *(--pTmp) = FREE_VAL; } pRec -> lSpecialValue = ~MAGICKEY; /* Handle the statistics */ lCurMemory -= pRec -> uDataSize; cNewCount--; /* Actually free the memory */ free ((char *) pRec); } /* * TERMINATE() * Report on all the memory pieces that have not been * free'ed as well as the statistics. */ TERMINATE () { struct remember *pPtr; /* Report the difference between number of calls to */ /* NEW and the number of calls to FREE. >0 means more */ /* NEWs than FREEs. <0, etc. */ if (cNewCount) { fprintf (stderr, "cNewCount: %d\n", cNewCount); fflush (stderr); } /* Report on all the memory that was allocated with NEW */ /* but not free'ed with FREE. */ if (pRememberRoot != NULL) { fprintf (stderr, "Memory that was not free'ed:\n"); fflush (stderr); } pPtr = pRememberRoot; while (pPtr) { fprintf (stderr, "\t%5d bytes at 0x%06x, allocated at line %3d in \"%s\"\n", pPtr -> uDataSize, &(pPtr -> aData[0]), pPtr -> uLineNum, pPtr -> sFileName ); fflush (stderr); pPtr = pPtr -> pNext; } /* Report the memory usage statistics */ fprintf (stderr, "Maximum memory usage: %ld bytes (%ldk)\n", lMaxMemory, (lMaxMemory + 1023L) / 1024L); fflush (stderr); } static void _checkchunk (pRec, sFile, uLine) register struct remember *pRec; char *sFile; uns uLine; { register uns uSize; register char *magicp; /* Check for a possible underrun */ if (pRec -> lSpecialValue != MAGICKEY) { fprintf (stderr, "Memory allocated at \"%s:%d\" was underrun,", pRec -> sFileName, pRec -> uLineNum); fprintf (stderr, " discovered at \"%s:%d\"\n", sFile, uLine); fflush (stderr); } /* Check for a possible overrun */ uSize = pRec -> uDataSize; magicp = &(pRec -> aData[uSize]); if (*magicp++ != MAGICEND0 || *magicp++ != MAGICEND1 || *magicp++ != MAGICEND2 || *magicp++ != MAGICEND3) { fprintf (stderr, "Memory allocated at \"%s:%d\" was overrun,", pRec -> sFileName, pRec -> uLineNum); fprintf (stderr, " discovered at \"%s:%d\"\n", sFile, uLine); fflush (stderr); } } static void _sanity (sFile, uLine) char *sFile; uns uLine; { register struct remember *pTmp; for (pTmp = pRememberRoot; pTmp != NULL; pTmp = pTmp -> pNext) { _checkchunk (pTmp, sFile, uLine); } }