summaryrefslogtreecommitdiff
path: root/mysys/safemalloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'mysys/safemalloc.c')
-rw-r--r--mysys/safemalloc.c518
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 */