diff options
Diffstat (limited to 'ext/mysql/libmysql/safemalloc.c')
| -rw-r--r-- | ext/mysql/libmysql/safemalloc.c | 503 | 
1 files changed, 503 insertions, 0 deletions
diff --git a/ext/mysql/libmysql/safemalloc.c b/ext/mysql/libmysql/safemalloc.c new file mode 100644 index 0000000000..5a44745bed --- /dev/null +++ b/ext/mysql/libmysql/safemalloc.c @@ -0,0 +1,503 @@ +/* Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB +   This file is public domain and comes with NO WARRANTY of any kind */ + +/* + * [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 4 functions in my package: + *	char *NEW( uSize )	Allocate memory of uSize bytes + *				(equivalent to malloc()) + *	char *REA( pPtr, uSize) Allocate memory of uSize bytes, move data and + *				free pPtr. + *				(equivalent to realloc()) + *	FREE( pPtr )		Free memory allocated by NEW + *				(equivalent to free()) + *	TERMINATE(file)		End system, report errors and stats on file + * 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()) + */ + +/* + * Memory sub-system, written by Bjorn Benson +   Fixed to use my_sys scheme by Michael Widenius + */ + +#ifndef SAFEMALLOC +#define SAFEMALLOC			/* Get protos from my_sys */ +#endif + +#include "mysys_priv.h" +#include <m_string.h> +#include "my_static.h" +#include "mysys_err.h" + +#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 functions prototypes */ + +static int check_ptr(const char *where, byte *ptr, const char *sFile, +		     uint uLine); +static int _checkchunk(struct remember *pRec, const char *sFile, uint uLine); + +/* + *	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. + */ + +#define ALLOC_VAL	(uchar) 0xA5	/* NEW'ed memory is filled with this */ +				/* value so that references to it will	 */ +				/* end up being very strange.		 */ +#define FREE_VAL	(uchar) 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.		  */ + + +/* + * gptr _mymalloc( uint uSize, my_string sFile, uint uLine, MyFlags ) + *	Allocate some memory. + */ + +gptr _mymalloc (uint uSize, const char *sFile, uint uLine, myf MyFlags) +{ +    struct remember *pTmp; +    DBUG_ENTER("_mymalloc"); +    DBUG_PRINT("enter",("Size: %u",uSize)); + +    if (!sf_malloc_quick) +      (void) _sanity (sFile, uLine); + +    /* Allocate the physical memory */ +    pTmp = (struct remember *) malloc ( +		sizeof (struct irem)			/* remember data  */ +		+ sf_malloc_prehunc +		+ uSize					/* size requested */ +		+ 4					/* overrun mark   */ +		+ sf_malloc_endhunc +		); + +    /* Check if there isn't anymore memory avaiable */ +    if (pTmp == NULL) +    { +      if (MyFlags & MY_FAE) +	error_handler_hook=fatal_error_handler_hook; +      if (MyFlags & (MY_FAE+MY_WME)) +      { +	char buff[SC_MAXWIDTH]; +	my_errno=errno; +	sprintf(buff,"Out of memory at line %d, '%s'", uLine, sFile); +	my_message(EE_OUTOFMEMORY,buff,MYF(ME_BELL+ME_WAITTANG)); +	sprintf(buff,"needed %d byte (%ldk), memory in use: %ld bytes (%ldk)", +		uSize, (uSize + 1023L) / 1024L, +		lMaxMemory, (lMaxMemory + 1023L) / 1024L); +	my_message(EE_OUTOFMEMORY,buff,MYF(ME_BELL+ME_WAITTANG)); +      } +      DBUG_PRINT("error",("Out of memory, in use: %ld at line %d, '%s'", +			  lMaxMemory,uLine, sFile)); +      if (MyFlags & MY_FAE) +	exit(1); +      DBUG_RETURN ((gptr) NULL); +    } + +    /* Fill up the structure */ +    *((long*) ((char*) &pTmp -> lSpecialValue+sf_malloc_prehunc)) = MAGICKEY; +    pTmp -> aData[uSize + sf_malloc_prehunc+0] = MAGICEND0; +    pTmp -> aData[uSize + sf_malloc_prehunc+1] = MAGICEND1; +    pTmp -> aData[uSize + sf_malloc_prehunc+2] = MAGICEND2; +    pTmp -> aData[uSize + sf_malloc_prehunc+3] = MAGICEND3; +    pTmp -> sFileName = (my_string) sFile; +    pTmp -> uLineNum = uLine; +    pTmp -> uDataSize = uSize; +    pTmp -> pPrev = NULL; + +    /* Add this remember structure to the linked list */ +    pthread_mutex_lock(&THR_LOCK_malloc); +    if ((pTmp->pNext=pRememberRoot)) +    { +      pRememberRoot -> pPrev = pTmp; +    } +    pRememberRoot = pTmp; + +    /* Keep the statistics */ +    lCurMemory += uSize; +    if (lCurMemory > lMaxMemory) { +	lMaxMemory = lCurMemory; +    } +    cNewCount++; +    pthread_mutex_unlock(&THR_LOCK_malloc); + +    /* Set the memory to the aribtrary wierd value */ +#ifdef HAVE_purify +    if (MyFlags & MY_ZEROFILL) +#endif +      bfill(&pTmp -> aData[sf_malloc_prehunc],uSize, +	    (char) (MyFlags & MY_ZEROFILL ? 0 : ALLOC_VAL)); +    /* Return a pointer to the real data */ +    DBUG_PRINT("exit",("ptr: %lx",&(pTmp -> aData[sf_malloc_prehunc]))); +    if (sf_min_adress > &(pTmp -> aData[sf_malloc_prehunc])) +      sf_min_adress = &(pTmp -> aData[sf_malloc_prehunc]); +    if (sf_max_adress < &(pTmp -> aData[sf_malloc_prehunc])) +      sf_max_adress = &(pTmp -> aData[sf_malloc_prehunc]); +    DBUG_RETURN ((gptr) &(pTmp -> aData[sf_malloc_prehunc])); +} + +/* + *  Allocate some new memory and move old memoryblock there. + *  Free then old memoryblock + */ + +gptr _myrealloc (register my_string pPtr, register uint uSize, +		 const char *sFile, uint uLine, myf MyFlags) +{ +  struct remember *pRec; +  gptr ptr; +  DBUG_ENTER("_myrealloc"); + +  if (!pPtr && (MyFlags & MY_ALLOW_ZERO_PTR)) +    DBUG_RETURN(_mymalloc(uSize,sFile,uLine,MyFlags)); + +  if (!sf_malloc_quick) +    (void) _sanity (sFile, uLine); + +  if (check_ptr("Reallocating",(byte*) pPtr,sFile,uLine)) +    DBUG_RETURN((gptr) NULL); + +  pRec = (struct remember *) (pPtr - sizeof (struct irem)-sf_malloc_prehunc); +  if (*((long*) ((char*) &pRec -> lSpecialValue+sf_malloc_prehunc)) +      != MAGICKEY) +  { +    fprintf (stderr, "Reallocating unallocated data at line %d, '%s'\n", +	     uLine, sFile); +    DBUG_PRINT("safe",("Reallocating unallocated data at line %d, '%s'", +		       uLine, sFile)); +    (void) fflush(stderr); +    DBUG_RETURN((gptr) NULL); +  } + +  if ((ptr=_mymalloc(uSize,sFile,uLine,MyFlags)))	/* Allocate new area */ +  { +    uSize=min(uSize,pRec-> uDataSize);		/* Move as much as possibly */ +    memcpy((byte*) ptr,pPtr,(size_t) uSize);	/* Copy old data */ +    _myfree(pPtr,sFile,uLine,0);		/* Free not needed area */ +  } +  else +  { +    if (MyFlags & MY_HOLD_ON_ERROR) +      DBUG_RETURN(pPtr); +    if (MyFlags & MY_FREE_ON_ERROR) +      _myfree(pPtr,sFile,uLine,0); +  } +  DBUG_RETURN(ptr); +} /* _myrealloc */ + + +/* + * void _myfree( my_string pPtr, my_string sFile, uint uLine, myf myflags) + *	Deallocate some memory. + */ + +void _myfree (gptr pPtr, const char *sFile, uint uLine, myf myflags) +{ +  struct remember *pRec; +  DBUG_ENTER("_myfree"); +  DBUG_PRINT("enter",("ptr: %lx",pPtr)); + +  if (!sf_malloc_quick) +    (void) _sanity (sFile, uLine); + +  if ((!pPtr && (myflags & MY_ALLOW_ZERO_PTR)) || +      check_ptr("Freeing",(byte*) pPtr,sFile,uLine)) +    DBUG_VOID_RETURN; + +  /* Calculate the address of the remember structure */ +  pRec = (struct remember *) ((byte*) pPtr-sizeof(struct irem)- +			      sf_malloc_prehunc); + +  /* 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 (*((long*) ((char*) &pRec -> lSpecialValue+sf_malloc_prehunc)) +      != MAGICKEY) +  { +    fprintf (stderr, "Freeing unallocated data at line %d, '%s'\n", +	     uLine, sFile); +    DBUG_PRINT("safe",("Unallocated data at line %d, '%s'",uLine,sFile)); +    (void) fflush(stderr); +    DBUG_VOID_RETURN; +  } + +  /* Remove this structure from the linked list */ +  pthread_mutex_lock(&THR_LOCK_malloc); +  if (pRec -> pPrev) { +    pRec -> pPrev -> pNext = pRec -> pNext; +  } else { +    pRememberRoot = pRec -> pNext; +  } +  if (pRec -> pNext) { +    pRec -> pNext -> pPrev = pRec -> pPrev; +  } +  /* Handle the statistics */ +  lCurMemory -= pRec -> uDataSize; +  cNewCount--; +  pthread_mutex_unlock(&THR_LOCK_malloc); + +#ifndef HAVE_purify +  /* Mark this data as free'ed */ +  bfill(&pRec->aData[sf_malloc_prehunc],pRec->uDataSize,(pchar) FREE_VAL); +#endif +  *((long*) ((char*) &pRec -> lSpecialValue+sf_malloc_prehunc)) = ~MAGICKEY; + +  /* Actually free the memory */ +  free ((my_string ) pRec); +  DBUG_VOID_RETURN; +} + +	/* Check if we have a wrong  pointer */ + +static int check_ptr(const char *where, byte *ptr, const char *sFile, +		     uint uLine) +{ +  if (!ptr) +  { +    fprintf (stderr, "%s NULL pointer at line %d, '%s'\n", +	     where,uLine, sFile); +    DBUG_PRINT("safe",("Null pointer at line %d '%s'", uLine, sFile)); +    (void) fflush(stderr); +    return 1; +  } +#ifndef _MSC_VER +  if ((long) ptr & (MY_ALIGN(1,sizeof(char *))-1)) +  { +    fprintf (stderr, "%s wrong aligned pointer at line %d, '%s'\n", +	     where,uLine, sFile); +    DBUG_PRINT("safe",("Wrong aligned pointer at line %d, '%s'", +		       uLine,sFile)); +    (void) fflush(stderr); +    return 1; +  } +#endif +  if (ptr < sf_min_adress || ptr > sf_max_adress) +  { +    fprintf (stderr, "%s pointer out of range at line %d, '%s'\n", +	     where,uLine, sFile); +    DBUG_PRINT("safe",("Pointer out of range at line %d '%s'", +		       uLine,sFile)); +    (void) fflush(stderr); +    return 1; +  } +  return 0; +} + + +/* + * TERMINATE(FILE *file) + *	Report on all the memory pieces that have not been + *	free'ed as well as the statistics. + */ + +void TERMINATE (FILE *file) +{ +  struct remember *pPtr; +  DBUG_ENTER("TERMINATE"); +  pthread_mutex_lock(&THR_LOCK_malloc); + +  /* 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) +  { +    if (file) +    { +      fprintf (file, "cNewCount: %d\n", cNewCount); +      (void) fflush(file); +    } +    DBUG_PRINT("safe",("cNewCount: %d",cNewCount)); +  } + +  /* Report on all the memory that was allocated with NEW	 */ +  /* but not free'ed with FREE.				 */ + +  if ((pPtr=pRememberRoot)) +  { +    if (file) +    { +      fprintf(file, "Memory that was not free'ed (%ld bytes):\n",lCurMemory); +      (void) fflush(file); +    } +    DBUG_PRINT("safe",("Memory that was not free'ed (%ld bytes):",lCurMemory)); +    while (pPtr) +    { +      if (file) +      { +	fprintf (file, +		 "\t%6u bytes at 0x%09lx, allocated at line %4u in '%s'\n", +		 pPtr -> uDataSize, +		 (ulong) &(pPtr -> aData[sf_malloc_prehunc]), +		 pPtr -> uLineNum, pPtr -> sFileName); +	(void) fflush(file); +      } +      DBUG_PRINT("safe", +		 ("%6u bytes at 0x%09lx, allocated at line %4d in '%s'", +		  pPtr -> uDataSize, &(pPtr -> aData[sf_malloc_prehunc]), +		  pPtr -> uLineNum, pPtr -> sFileName)); +      pPtr = pPtr -> pNext; +    } +  } +  /* Report the memory usage statistics */ +  if (file) +  { +    fprintf (file, "Maximum memory usage: %ld bytes (%ldk)\n", +	     lMaxMemory, (lMaxMemory + 1023L) / 1024L); +    (void) fflush(file); +  } +  DBUG_PRINT("safe",("Maximum memory usage: %ld bytes (%ldk)", +		     lMaxMemory, (lMaxMemory + 1023L) / 1024L)); +  pthread_mutex_unlock(&THR_LOCK_malloc); +  DBUG_VOID_RETURN; +} + + +	/* Returns 0 if chunk is ok */ + +static int _checkchunk (register struct remember *pRec, const char *sFile, +			uint uLine) +{ +  reg1 uint uSize; +  reg2 my_string magicp; +  reg3 int flag=0; + +  /* Check for a possible underrun */ +  if (*((long*) ((char*) &pRec -> lSpecialValue+sf_malloc_prehunc)) +      != MAGICKEY) +  { +    fprintf (stderr, "Memory allocated at %s:%d was underrun,", +	     pRec -> sFileName, pRec -> uLineNum); +    fprintf (stderr, " discovered at %s:%d\n", sFile, uLine); +    (void) fflush(stderr); +    DBUG_PRINT("safe",("Underrun at %lx, allocated at %s:%d", +		       &(pRec -> aData[sf_malloc_prehunc]), +		       pRec -> sFileName, +		       pRec -> uLineNum)); +    flag=1; +  } + +  /* Check for a possible overrun */ +  uSize = pRec -> uDataSize; +  magicp = &(pRec -> aData[uSize+sf_malloc_prehunc]); +  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); +    (void) fflush(stderr); +    DBUG_PRINT("safe",("Overrun at %lx, allocated at %s:%d", +		       &(pRec -> aData[sf_malloc_prehunc]), +		       pRec -> sFileName, +		       pRec -> uLineNum)); +    flag=1; +  } +  return(flag); +} + + +	/* Returns how many wrong chunks */ + +int _sanity (const char *sFile, uint uLine) +{ +  reg1 struct remember *pTmp; +  reg2 int flag=0; +  uint count=0; + +  pthread_mutex_lock(&THR_LOCK_malloc); +  count=cNewCount; +  for (pTmp = pRememberRoot; pTmp != NULL && count-- ; pTmp = pTmp -> pNext) +    flag+=_checkchunk (pTmp, sFile, uLine); +  if (count || pTmp) +  { +    const char *format="Safemalloc link list destroyed, discovered at '%s:%d'"; +    fprintf (stderr, format, sFile, uLine); fputc('\n',stderr); +    (void) fflush(stderr); +    DBUG_PRINT("safe",(format, sFile, uLine)); +    flag=1; +  } +  pthread_mutex_unlock(&THR_LOCK_malloc); +  return flag; +} /* _sanity */ + + +	/* malloc and copy */ + +gptr _my_memdup(const byte *from, uint length, const char *sFile, uint uLine, +		myf MyFlags) +{ +  gptr ptr; +  if ((ptr=_mymalloc(length,sFile,uLine,MyFlags)) != 0) +    memcpy((byte*) ptr, (byte*) from,(size_t) length); +  return(ptr); +} /*_my_memdup */ + + +my_string _my_strdup(const char *from, const char *sFile, uint uLine, +		     myf MyFlags) +{ +  gptr ptr; +  uint length=(uint) strlen(from)+1; +  if ((ptr=_mymalloc(length,sFile,uLine,MyFlags)) != 0) +    memcpy((byte*) ptr, (byte*) from,(size_t) length); +  return((my_string) ptr); +} /* _my_strdup */  | 
