Solution to "oops, corrupted memory again!"
Bjorn Benson
bjorn at dataioDataio.UUCP
Tue Apr 29 13:04:49 AEST 1986
[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())
--------------------------HEADER-FILE---------------------------
/*
* Memory sub-system, written by Bjorn Benson
*/
extern char *_new();
#define NEW(SZ) _new( SZ, __FILE__, __LINE__ )
#define FREE(PTR) _free( PTR, __FILE__, __LINE__ )
-------------------------C-SOURCE-FILE-------------------------
/*
* Memory sub-system, written by Bjorn Benson
*/
#include <stdio.h>
typedef unsigned uns; /* Shorthand */
struct REMEMBER_INTERNAL {
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 is above file */
uns _uDataSize; /* Size requested */
uns _lSpecialValue; /* Underrun marker value */
};
struct REMEMBER {
struct REMEMBER_INTERNAL 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
static long lCurMemory = 0; /* Current memory usage */
static long lMaxMemory = 0; /* Maximum memory usage */
/* 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 uns cNewCount = 0; /* Number of times NEW() was called */
static struct REMEMBER *pRememberRoot = NULL;
/* Root of the linked list of REMEMBERs */
#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. */
/*
* _new( uns uSize, char *sFile, uns uLine ) : char*
* Allocate some memory.
*/
char *_new( uSize, sFile, uLine )
uns uSize,uLine;
char *sFile;
{
extern char *malloc();
struct REMEMBER *pTmp;
char *pPtr;
/* Allocate the physical memory */
pTmp = (struct REMEMBER *) malloc(
sizeof(struct REMEMBER_INTERNAL) /* 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 );
exit(1);
}
/* 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]) );
}
/*
* _free( char *pPtr, char *sFile, uns uLine )
* Deallocate some memory.
*/
_free( pPtr, sFile, uLine )
char *pPtr,*sFile;
uns uLine;
{
struct REMEMBER *pRec;
char *pTmp;
uns uSize;
/* Check if we have a non-null pointer */
if( pPtr == NULL )
{
fprintf(stderr,"Freeing NULL pointer at line %d, \"%s\"\n",
uLine, sFile );
return;
}
/* Calculate the address of the REMEMBER structure */
pRec = (struct REMEMBER *)( pPtr -
(sizeof(struct REMEMBER_INTERNAL)) );
/* 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 );
return;
}
/* Check for an overrun */
uSize = pRec->uDataSize;
if( pRec->aData[uSize+0] != MAGICEND0
|| pRec->aData[uSize+1] != MAGICEND1
|| pRec->aData[uSize+2] != MAGICEND2
|| pRec->aData[uSize+3] != MAGICEND3 )
{
fprintf(stderr,"Memory being free'ed at line %d, \"%s\" \
was overrun\n", uLine, sFile );
fprintf(stderr,"\t(allocated at line %d, \"%s\")\n",
pRec->uLineNum, pRec->sFileName );
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 );
/* 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");
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
);
pPtr = pPtr->pNext;
}
/* Report the memory usage statistics */
fprintf(stderr,"Maximum memory usage: %ld bytes (%ldk)\n",
lMaxMemory, (lMaxMemory + 1023L)/1024L );
}
More information about the Comp.sources.unix
mailing list