diff options
Diffstat (limited to 'mysys/safemalloc.c')
-rw-r--r-- | mysys/safemalloc.c | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/mysys/safemalloc.c b/mysys/safemalloc.c new file mode 100644 index 00000000000..d715f69e38a --- /dev/null +++ b/mysys/safemalloc.c @@ -0,0 +1,518 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA */ + +/* + * [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 gptr 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 *) ((char*) 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); + pthread_mutex_unlock(&THR_LOCK_malloc); + 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; + } + 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 */ |