From: danielk1977 Date: Fri, 9 Dec 2005 14:25:08 +0000 (+0000) Subject: Many small changes to ensure memory is not leaked after malloc() fails. (CVS 2808) X-Git-Tag: version-3.6.10~3352 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2e588c7525a77b3a6cc53c67ea833f75f7e4052b;p=thirdparty%2Fsqlite.git Many small changes to ensure memory is not leaked after malloc() fails. (CVS 2808) FossilOrigin-Name: 601c335463aaabc2e9918e4b9298cff6161be5c4 --- diff --git a/manifest b/manifest index 84ff3b8da0..292d56defc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\smore\sstress\stesting\sto\sautovacuum\sin\san\s(unsuccessful)\sattempt\sto\nreproduce\sa\sreported\sproblem.\s\sEven\sthough\sthese\stests\sdid\snot\suncover\nanything\samiss,\sextra\stests\snever\shurt...\s(CVS\s2807) -D 2005-12-09T02:35:54 +C Many\ssmall\schanges\sto\sensure\smemory\sis\snot\sleaked\safter\smalloc()\sfails.\s(CVS\s2808) +D 2005-12-09T14:25:08 F Makefile.in e3c6b3a38d734d41574c04f2fc90d18de2b87102 F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -32,7 +32,7 @@ F sqlite3.def c413e514217736884254739a105c8c942fdf0c2f F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a F src/alter.c 7ed4b794c2e3a8ad8c1effe50202eaef42cedc23 F src/analyze.c ea42005eed52c382fcc7ef66969e7f1858597633 -F src/attach.c 39e678033d0be197dea4e9d5eeac2b2df51d6c9f +F src/attach.c ee70131f128d31a9c6dcb8824e8471c91b18601a F src/auth.c 31e2304bef67f44d635655f44234387ea7d21454 F src/btree.c aa88194f460becf8fff6196996d6e38f1b37286e F src/btree.h 1ed561263ca0e335bc3e81d761c9d5ff8c22f61e @@ -55,7 +55,7 @@ F src/os.h d5ae3f4c1c7731437b6cddec279b7c06f761c44e F src/os_common.h d74a11728ad2444b6b695b94c28c06881f049e49 F src/os_test.c 49833426101f99aee4bb5f6a44b7c4b2029fda1c F src/os_test.h 903c93554c23d88f34f667f1979e4a1cee792af3 -F src/os_unix.c 76189e4cb5e1ad451293ce1aeb348c3b829e7e13 +F src/os_unix.c 01648f7fa16cddb1f8a11ef2a582348801b185a6 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e F src/os_win.c d962ac2dd0e482847e42b846d46cd044f97d1c32 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b @@ -63,32 +63,32 @@ F src/pager.c 893cb2106261a4f77d84c1fa0d10a083e889b23b F src/pager.h e7b41ce8e7b5f629d456708b7ad9a8c8ede37140 F src/parse.y 87080d89439925de19f16189310d3dbc7f9ab3f6 F src/pragma.c a6d3a7c76b4008b8197913de4bd2079e00b145db -F src/prepare.c e93967011379051316728d316755f533a9bb438c +F src/prepare.c 7e21ab5e2304e76f5f679d0df19dca3b0e3ff4f2 F src/printf.c 3ea3a17d25d7ac498efc18007c70371a42c968f8 F src/random.c ff5e9a8cad790e2a51cd4d2e7737dc8540e09d1d F src/select.c 0e4d3627fec4a445b45f6cb471f68aab9c97a8b3 -F src/shell.c 3596c1e559b82663057940d19ba533ad421c7dd3 +F src/shell.c 4872acee1d2a826c73c914961e469e563204b7f9 F src/sqlite.h.in 8e648e1f386e4509f2f96c09ded7c07b0df0c9a2 -F src/sqliteInt.h bbc310a83a32476aa960055a166af4a0ef503a79 +F src/sqliteInt.h 4247cf9ae4280ae33e5bbc7b45358940ff34b9ce F src/table.c 486dcfce532685b53b5a2b5da8bba0ded6fb2316 F src/tclsqlite.c 328060916c24d328cfab1622c9a0e7ad57c2da8c -F src/test1.c 0b4bf8ab9afb37f34a83c46f81de54adf519b23d +F src/test1.c 350129da26943dd62067e75ad71ba85a1a648230 F src/test2.c 36390cdfc70c08e5ee0b466d0654a117f398bbff F src/test3.c f4e6a16a602091696619a1171bda25c0e3df49f7 F src/test4.c a8fd681e139e1c61f22a77d07fc3a99cb28fff3f F src/test5.c 64f08b2a50ef371a1bd68ff206829e7b1b9997f5 F src/test6.c cb811391ec0b7c75f29e545d4820a9cf19f3637e F src/tokenize.c a189d7466524076220f41a21baed05e1639a82f4 -F src/trigger.c 388c13a2f07584decdb88413a63017391f9a7f7a +F src/trigger.c dfc1f8ee8e71c6482fb269695778c8b51f821c98 F src/update.c ec8e540617b116725b5a55c8d6b4db8bc67fdd7d F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c -F src/util.c 88ebafb5708f04002e58a373386e3c8c31bfd858 -F src/vacuum.c 4d64ce98a4cb130002cbf8aba018b2567773e6b1 -F src/vdbe.c 7f4a2affee140b7b57952ab2c081a9c22b950638 +F src/util.c c77727365ce60b0e0087550ba88653ad0fcf24ea +F src/vacuum.c 3cd457f91b05338269f2ea21c4c58dce93f2eef2 +F src/vdbe.c 4f78f1fe3b5e7675ee54a2cc18f14dea4ff5fd94 F src/vdbe.h 8729a4ee16ff9aeab2af9667df3cf300ff978e13 F src/vdbeInt.h 0055c37eccbf3a189fd893a90f8eb6a5fa60c871 -F src/vdbeapi.c b80b3a1e8bf7866d96ca76d91bba979155bace57 -F src/vdbeaux.c 90143aaffbe232a700fd05e4b2e7428d512b605b +F src/vdbeapi.c b270b680cbc5d20b5a1abfdb08339667985df94e +F src/vdbeaux.c b0a4a4b19b42f18d86c0026a9cc548478dc6a7a7 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5 F src/vdbemem.c 1c70555d615c6001c4490dedb6a62cb2884b92ff F src/where.c 269569f380ddc018518f67765fe2f0d3c8760e28 @@ -180,8 +180,8 @@ F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9 F test/main.test b12f01d49a5c805a33fa6c0ef168691f63056e79 F test/malloc.test a5ed721cf7d1b12602ede4f98c11b65ab1582cc0 F test/malloc2.test e6e321db96d6c94cb18bf82ad7215070c41e624e -F test/malloc3.test 0d60a9d19196581e9279c2d040bebd8162ab495b -F test/malloc4.test 593ae91bf69ab5abc68f7652e679e5d207e23166 +F test/malloc3.test 56372aafe58cc24a31bad4b24ff3f77cf1cc30ab +F test/malloc4.test 2e29d155eb4b4808019ef47eeedfcbe9e09e0f05 F test/manydb.test d81debbf5871242e3b5df1d3bb5e14c50431b6f8 F test/memdb.test 1860e060be810bf0775bc57408a5b7c4954bcaea F test/memleak.test df2b2b96e77f8ba159a332299535b1e5f18e49ac @@ -208,7 +208,7 @@ F test/rollback.test 94cd981ee3a627d9f6466f69dcf1f7dbfe695d7a F test/rowid.test 040a3bef06f970c45f5fcd14b2355f7f4d62f0cf F test/safety.test 907b64fee719554a3622853812af3886fddbbb4f F test/schema.test 8a2ae440fb15f5798a68059e8746402f3137be46 -F test/select1.test 480233d4f5a81d7d59a55e40d05084d97e57ecdf +F test/select1.test 20275797efe032a56e97687aa06ba65d7a0b9b2b F test/select2.test f3c2678c3a9f3cf08ec4988a3845bda64be6d9e3 F test/select3.test 8fece41cd8f2955131b3f973a7123bec60b6e65e F test/select4.test c239f516aa31f42f2ef7c6d7cd01105f08f934ca @@ -223,7 +223,7 @@ F test/table.test ec0e6c2186bb8f6824f470caa118524dfd8fe057 F test/tableapi.test 6a66d58b37d46dc0f2b3c7d4bd2617d209399bd1 F test/tclsqlite.test 2da3e4b3a79b13c1511c9d0cd995e08f8362e782 F test/temptable.test 7927261befdbc7b0a7ffebb85ecc70a74fa7b15b -F test/tester.tcl 1dbcac19ed71f68f24b7f5c6c134568d551092ba +F test/tester.tcl 6effe753bed26313eaab11a382436a99e602fa54 F test/thread1.test 776c9e459b75ba905193b351926ac4019b049f35 F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b F test/threadtest2.c 97a830d53c24c42290501fdfba4a6e5bdd34748b @@ -327,7 +327,7 @@ F www/tclsqlite.tcl ddcf912ea48695603c8ed7efb29f0812ef8d1b49 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P a7c9e8989c752f6b1148e7cc7bf59bbd8b402e87 -R 0c1000cc2ea9df5aafb3433db2962046 -U drh -Z 44f91e86d2b44a0b3e1c8417c9a4606c +P d8a8933ff30b83c0483be214403d92c4dfa9a4af +R 208b8157372f7c8db3da02b392e698f6 +U danielk1977 +Z 1ece2b1e923caae0853519e926a399e9 diff --git a/manifest.uuid b/manifest.uuid index 56042eff3f..8b1e95e028 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d8a8933ff30b83c0483be214403d92c4dfa9a4af \ No newline at end of file +601c335463aaabc2e9918e4b9298cff6161be5c4 \ No newline at end of file diff --git a/src/attach.c b/src/attach.c index 0088417502..8003d7cc99 100644 --- a/src/attach.c +++ b/src/attach.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to implement the ATTACH and DETACH commands. ** -** $Id: attach.c,v 1.36 2005/12/06 17:19:11 danielk1977 Exp $ +** $Id: attach.c,v 1.37 2005/12/09 14:25:08 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -261,12 +261,17 @@ static void codeAttach( sqlite3* db = pParse->db; #ifndef SQLITE_OMIT_AUTHORIZATION - char *zAuthArg = sqlite3NameFromToken(&pAuthArg->span); - if( !zAuthArg ){ - goto attach_end; - } - if( sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0)!=SQLITE_OK ){ - goto attach_end; + assert( sqlite3Tsd()->mallocFailed || pAuthArg ); + if( pAuthArg ){ + char *zAuthArg = sqlite3NameFromToken(&pAuthArg->span); + if( !zAuthArg ){ + goto attach_end; + } + rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0); + sqliteFree(zAuthArg); + if(rc!=SQLITE_OK ){ + goto attach_end; + } } #endif /* SQLITE_OMIT_AUTHORIZATION */ @@ -304,7 +309,6 @@ attach_end: sqlite3ExprDelete(pFilename); sqlite3ExprDelete(pDbname); sqlite3ExprDelete(pKey); - sqliteFree(zAuthArg); } /* diff --git a/src/os_unix.c b/src/os_unix.c index 3beaf3b910..c07a80cb91 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -633,6 +633,7 @@ static int unixOpenReadWrite( static int unixOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){ int rc; unixFile f; + int fd; assert( 0==*pId ); if( access(zFilename, 0)==0 ){ @@ -1485,6 +1486,8 @@ static int allocateUnixFile(unixFile *pInit, OsFile **pId){ pNew = sqliteMalloc( sizeof(unixFile) ); if( pNew==0 ){ close(pInit->h); + releaseLockInfo(pInit->pLock); + releaseOpenCnt(pInit->pOpen); *pId = 0; return SQLITE_NOMEM; }else{ diff --git a/src/prepare.c b/src/prepare.c index 4a9b2b1cef..9a1d4f3382 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -13,7 +13,7 @@ ** interface, and routines that contribute to loading the database schema ** from disk. ** -** $Id: prepare.c,v 1.5 2005/12/06 12:52:59 danielk1977 Exp $ +** $Id: prepare.c,v 1.6 2005/12/09 14:25:08 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -503,7 +503,7 @@ prepare_out: sqlite3Error(db, rc, 0); } - sqlite3ClearMallocFailed(); + sqlite3MallocClearFailed(); return rc; } diff --git a/src/shell.c b/src/shell.c index 3296f44bfe..b06f913091 100644 --- a/src/shell.c +++ b/src/shell.c @@ -12,7 +12,7 @@ ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. ** -** $Id: shell.c,v 1.128 2005/09/11 02:03:04 drh Exp $ +** $Id: shell.c,v 1.129 2005/12/09 14:25:08 danielk1977 Exp $ */ #include #include @@ -81,7 +81,7 @@ static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */ /* ** Determines if a string is a number of not. */ -static int isNumber(const unsigned char *z, int *realnum){ +static int isNumber(const char *z, int *realnum){ if( *z=='-' || *z=='+' ) z++; if( !isdigit(*z) ){ return 0; @@ -690,8 +690,9 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){ zSelect = appendText(zSelect, " || ' VALUES(' || ", 0); rc = sqlite3_step(pTableInfo); while( rc==SQLITE_ROW ){ + const char *zText = (const char *)sqlite3_column_text(pTableInfo, 1); zSelect = appendText(zSelect, "quote(", 0); - zSelect = appendText(zSelect, sqlite3_column_text(pTableInfo, 1), '"'); + zSelect = appendText(zSelect, zText, '"'); rc = sqlite3_step(pTableInfo); if( rc==SQLITE_ROW ){ zSelect = appendText(zSelect, ") || ', ' || ", 0); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 6c9ae45115..f9c853a034 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.430 2005/12/06 17:19:11 danielk1977 Exp $ +** @(#) $Id: sqliteInt.h,v 1.431 2005/12/09 14:25:08 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -233,47 +233,46 @@ struct BusyHandler { */ #define Addr(X) ((uptr)X) -/* -** If memory allocation problems are found, recompile with -** -** -DSQLITE_DEBUG=1 -** -** to enable some sanity checking on malloc() and free(). To -** check for memory leaks, recompile with -** -** -DSQLITE_DEBUG=2 -** -** and a line of text will be written to standard error for -** each malloc() and free(). This output can be analyzed -** by an AWK script to determine if there are any leaks. -*/ #ifdef SQLITE_MEMDEBUG -# define sqliteMalloc(X) sqlite3Malloc_(X,1,__FILE__,__LINE__) -# define sqliteMallocRaw(X) sqlite3Malloc_(X,0,__FILE__,__LINE__) -# define sqliteFree(X) sqlite3Free_(X,__FILE__,__LINE__) -# define sqliteRealloc(X,Y) sqlite3Realloc_(X,Y,__FILE__,__LINE__) -# define sqliteStrDup(X) sqlite3StrDup_(X,__FILE__,__LINE__) -# define sqliteStrNDup(X,Y) sqlite3StrNDup_(X,Y,__FILE__,__LINE__) -#else -# define sqliteFree sqlite3FreeX -# define sqliteMalloc sqlite3Malloc -# define sqliteMallocRaw sqlite3MallocRaw -# define sqliteRealloc sqlite3Realloc -# define sqliteStrDup sqlite3StrDup -# define sqliteStrNDup sqlite3StrNDup -#endif - /* ** The following global variables are used for testing and debugging ** only. They only work if SQLITE_DEBUG is defined. */ -#ifdef SQLITE_MEMDEBUG extern int sqlite3_nMalloc; /* Number of sqliteMalloc() calls */ extern int sqlite3_nFree; /* Number of sqliteFree() calls */ extern int sqlite3_iMallocFail; /* Fail sqliteMalloc() after this many calls */ extern int sqlite3_iMallocReset; /* Set iMallocFail to this when it reaches 0 */ +#define ENTER_MALLOC (\ + sqlite3Tsd()->zFile = __FILE__, sqlite3Tsd()->iLine = __LINE__ \ +) +#else +#define ENTER_MALLOC 0 #endif +#define sqliteFree(x) sqlite3FreeX(x) +#define sqliteMalloc(x) (ENTER_MALLOC, sqlite3Malloc(x)) +#define sqliteMallocRaw(x) (ENTER_MALLOC, sqlite3MallocRaw(x)) +#define sqliteRealloc(x,y) (ENTER_MALLOC, sqlite3Realloc(x,y)) +#define sqliteStrDup(x) (ENTER_MALLOC, sqlite3StrDup(x)) +#define sqliteStrNDup(x,y) (ENTER_MALLOC, sqlite3StrNDup(x,y)) + +/* +** An instance of this structure is allocated for each thread that uses SQLite. +*/ +typedef struct SqliteTsd SqliteTsd; +struct SqliteTsd { + int mallocFailed; /* True after a malloc() has failed */ +#ifndef NDEBUG + int mallocAllowed; /* assert() in sqlite3Malloc() if not set */ +#endif +#ifdef SQLITE_MEMDEBUG + int isFail; /* True if all malloc() calls should fail */ + const char *zFile; /* Filename to associate debugging info with */ + int iLine; /* Line number to associate debugging info with */ + void *pFirst; /* Pointer to linked list of allocations */ +#endif +}; + /* ** Name of the master database table. The master database table ** is a special table that holds the names and attributes of all @@ -1379,14 +1378,6 @@ typedef struct { char **pzErrMsg; /* Error message stored here */ } InitData; -/* -** An instance of this structure is allocated for each thread that uses SQLite. -*/ -typedef struct SqliteTsd SqliteTsd; -struct SqliteTsd { - int mallocFailed; /* True after a malloc() has failed */ -}; - /* * This global flag is set for performance testing of triggers. When it is set * SQLite will perform the overhead of building new and old trigger references @@ -1417,26 +1408,18 @@ int sqlite3IsNumber(const char*, int*, u8); int sqlite3Compare(const char *, const char *); int sqlite3SortCompare(const char *, const char *); void sqlite3RealToSortable(double r, char *); -#ifdef SQLITE_MEMDEBUG - void *sqlite3Malloc_(int,int,char*,int); - void sqlite3Free_(void*,char*,int); - void *sqlite3Realloc_(void*,int,char*,int); - char *sqlite3StrDup_(const char*,char*,int); - char *sqlite3StrNDup_(const char*, int,char*,int); - void sqlite3CheckMemory(void*,int); -#else - void *sqlite3Malloc(int); - void *sqlite3MallocRaw(int); - void sqlite3Free(void*); - void *sqlite3Realloc(void*,int); - char *sqlite3StrDup(const char*); - char *sqlite3StrNDup(const char*, int); + +void *sqlite3Malloc(int); +void *sqlite3MallocRaw(int); +void sqlite3Free(void*); +void *sqlite3Realloc(void*,int); +char *sqlite3StrDup(const char*); +char *sqlite3StrNDup(const char*, int); # define sqlite3CheckMemory(a,b) -# define sqlite3MallocX sqlite3Malloc -#endif void sqlite3ReallocOrFree(void**,int); void sqlite3FreeX(void*); void *sqlite3MallocX(int); + char *sqlite3MPrintf(const char*, ...); char *sqlite3VMPrintf(const char*, va_list); void sqlite3DebugPrintf(const char*, ...); @@ -1677,9 +1660,17 @@ void sqlite3DefaultRowEst(Index*); void sqlite3RegisterLikeFunctions(sqlite3*, int); int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*); SqliteTsd *sqlite3Tsd(); -void sqlite3ClearMallocFailed(); void sqlite3AttachFunctions(sqlite3 *); +void sqlite3MallocClearFailed(); +#ifdef NDEBUG + #define sqlite3MallocDisallow() + #define sqlite3MallocAllow() +#else + void sqlite3MallocDisallow(); + void sqlite3MallocAllow(); +#endif + #ifdef SQLITE_SSE #include "sseInt.h" #endif diff --git a/src/test1.c b/src/test1.c index d29346410f..d4e15ac2b6 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.171 2005/12/06 12:53:00 danielk1977 Exp $ +** $Id: test1.c,v 1.172 2005/12/09 14:25:08 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -832,6 +832,15 @@ static int sqlite_malloc_stat( Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } +static int sqlite_malloc_outstanding( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + char **argv /* Text of each argument */ +){ + extern int sqlite3OutstandingMallocs(Tcl_Interp *interp); + return sqlite3OutstandingMallocs(interp); +} #endif /* @@ -3082,6 +3091,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ #ifdef SQLITE_MEMDEBUG { "sqlite_malloc_fail", (Tcl_CmdProc*)sqlite_malloc_fail }, { "sqlite_malloc_stat", (Tcl_CmdProc*)sqlite_malloc_stat }, + { "sqlite_malloc_outstanding", (Tcl_CmdProc*)sqlite_malloc_outstanding}, #endif { "sqlite_bind", (Tcl_CmdProc*)test_bind }, { "breakpoint", (Tcl_CmdProc*)test_breakpoint }, @@ -3176,6 +3186,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_sync_count, sqlite3_fullsync_count; extern int sqlite3_opentemp_count; extern int sqlite3_memUsed; + extern int sqlite3_malloc_id; extern int sqlite3_memMax; extern int sqlite3_like_count; #if OS_WIN @@ -3210,6 +3221,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ (char*)&sqlite3_current_time, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_os_trace", (char*)&sqlite3_os_trace, TCL_LINK_INT); + Tcl_LinkVar(interp, "sqlite_malloc_id", + (char*)&sqlite3_malloc_id, TCL_LINK_STRING); #if OS_WIN Tcl_LinkVar(interp, "sqlite_os_type", (char*)&sqlite3_os_type, TCL_LINK_INT); diff --git a/src/trigger.c b/src/trigger.c index cc5fc080e7..fc4f172ba9 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -199,7 +199,7 @@ void sqlite3FinishTrigger( pTrig = pParse->pNewTrigger; pParse->pNewTrigger = 0; - if( pParse->nErr || pTrig==0 ) goto triggerfinish_cleanup; + if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup; pTrig->step_list = pStepList; while( pStepList ){ pStepList->pTrig = pTrig; @@ -312,7 +312,10 @@ static void sqlitePersistTriggerStep(TriggerStep *p){ */ TriggerStep *sqlite3TriggerSelectStep(Select *pSelect){ TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep)); - if( pTriggerStep==0 ) return 0; + if( pTriggerStep==0 ) { + sqlite3SelectDelete(pSelect); + return 0; + } pTriggerStep->op = TK_SELECT; pTriggerStep->pSelect = pSelect; diff --git a/src/util.c b/src/util.c index 88924fbebb..ac8677a79b 100644 --- a/src/util.c +++ b/src/util.c @@ -14,42 +14,175 @@ ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.149 2005/12/06 12:53:01 danielk1977 Exp $ +** $Id: util.c,v 1.150 2005/12/09 14:25:09 danielk1977 Exp $ */ #include "sqliteInt.h" #include #include -#if SQLITE_MEMDEBUG>2 && defined(__GLIBC__) -#include -void print_stack_trace(){ - void *bt[30]; - int i; - int n = backtrace(bt, 30); +/* +** MALLOC WRAPPER ARCHITECTURE +** +** The sqlite code accesses dynamic memory allocation/deallocation by invoking +** the following four APIs (which may be implemented as macros). +** +** sqlite3Malloc() +** sqlite3MallocRaw() +** sqlite3Realloc() +** sqlite3ReallocOrFree() +** sqlite3Free() +** +** The function sqlite3FreeX performs the same task as sqlite3Free and is +** guaranteed to be a real function. +** +** The above APIs are implemented in terms of the functions provided at the Os +** level (not in this file). The Os level interface is never accessed directly +** by code outside of this file. +** +** sqlite3OsMalloc() +** sqlite3OsRealloc() +** sqlite3OsFree() +** sqlite3OsAllocationSize() +** +** Functions sqlite3MallocRaw() and sqlite3Realloc() may invoke +** sqlite3_release_memory() if a call to sqlite3OsMalloc() or +** sqlite3OsRealloc() fails. Function sqlite3Malloc() usually invokes +** sqlite3MallocRaw(). +** +** MALLOC TEST WRAPPER ARCHITECTURE +** +** The test wrapper provides extra test facilities to ensure the library +** does not leak memory and handles the failure of the underlying (Os level) +** allocation system correctly. It is only present if the library is +** compiled with the SQLITE_MEMDEBUG macro set. +** +** * Guardposts to detect overwrites. +** * Ability to cause a specific Malloc() or Realloc() to fail. +** * Audit outstanding memory allocations (i.e check for leaks). +*/ - fprintf(stderr, "STACK: "); - for(i=0; i0); + assert(sizeof(int)<=8); + if( p ){ + *(int *)p = n; + } + return (void *)(p + 8); +} +void *sqlite3OsRealloc(void *p, int n){ + char *p2 = ((char *)p - 8); + assert(n>0); + p2 = realloc(p2, n+8); + if( p2 ){ + *(int *)p2 = n; } - fprintf(stderr, "\n"); + return (void *)((char *)p2 + 8); } +void sqlite3OsFree(void *p){ + assert(p); + free((void *)((char *)p - 8)); +} +int sqlite3OsAllocationSize(void *p){ + return *(int *)((char *)p - 8); +} + +/* +** TODO! +*/ +#define sqlite3_release_memory(x) 0 + +#ifdef SQLITE_MEMDEBUG +/*-------------------------------------------------------------------------- +** Begin code for memory allocation system test layer. +** +** Memory debugging is turned on by defining the SQLITE_MEMDEBUG macro. +*/ + +/* Figure out whether or not to store backtrace() information for each malloc. +** The backtrace() function is only used if SQLITE_MEMDEBUG is set to 2 or +** greater and glibc is in use. If we don't want to use backtrace(), then just +** define it as an empty macro and set the amount of space reserved to 0. +*/ +#if defined(__GLIBC__) && SQLITE_MEMDEBUG>1 + extern int backtrace(void **, int); + #define TESTALLOC_STACKSIZE 128 + #define TESTALLOC_STACKFRAMES ((TESTALLOC_STACKSIZE-8)/sizeof(void*)) #else -#define print_stack_trace() + #define backtrace(x, y) 0 + #define TESTALLOC_STACKSIZE 0 + #define TESTALLOC_STACKFRAMES 0 #endif -#if 0 /* -** If malloc() ever fails, this global variable gets set to 1. -** This causes the library to abort and never again function. +** Number of 32-bit guard words. This should probably be a multiple of +** 2 since on 64-bit machines we want the value returned by sqliteMalloc() +** to be 8-byte aligned. */ -int sqlite3Tsd()->mallocFailed = 0; -#endif +#define TESTALLOC_NGUARD 2 /* -** If SQLITE_MEMDEBUG is defined, then use versions of malloc() and -** free() that track memory usage and check for buffer overruns. +** Size reserved for storing file-name along with each malloc()ed blob. */ -#ifdef SQLITE_MEMDEBUG +#define TESTALLOC_FILESIZE 64 + +/* +** Size reserved for storing the user string. +*/ +#define TESTALLOC_USERSIZE 64 +const char *sqlite3_malloc_id = 0; + +/* +** Blocks used by the test layer have the following format: +** +** +** +** +** +** +** <32-bit line number> +** +** +*/ + +#define TESTALLOC_OFFSET_GUARD1(p) (sizeof(void *) * 2) +#define TESTALLOC_OFFSET_DATA(p) ( \ + TESTALLOC_OFFSET_GUARD1(p) + sizeof(u32) * TESTALLOC_NGUARD \ +) +#define TESTALLOC_OFFSET_GUARD2(p) ( \ + TESTALLOC_OFFSET_DATA(p) + sqlite3OsAllocationSize(p) - TESTALLOC_OVERHEAD \ +) +#define TESTALLOC_OFFSET_LINENUMBER(p) ( \ + TESTALLOC_OFFSET_GUARD2(p) + sizeof(u32) * TESTALLOC_NGUARD \ +) +#define TESTALLOC_OFFSET_FILENAME(p) ( \ + TESTALLOC_OFFSET_LINENUMBER(p) + sizeof(u32) \ +) +#define TESTALLOC_OFFSET_USER(p) ( \ + TESTALLOC_OFFSET_FILENAME(p) + TESTALLOC_FILESIZE \ +) +#define TESTALLOC_OFFSET_STACK(p) ( \ + TESTALLOC_OFFSET_USER(p) + TESTALLOC_USERSIZE + 8 - \ + (TESTALLOC_OFFSET_USER(p) % 8) \ +) + +#define TESTALLOC_OVERHEAD ( \ + sizeof(void *)*2 + /* pPrev and pNext pointers */ \ + TESTALLOC_NGUARD*sizeof(u32)*2 + /* Guard words */ \ + sizeof(u32) + TESTALLOC_FILESIZE + /* File and line number */ \ + TESTALLOC_USERSIZE + /* User string */ \ + TESTALLOC_STACKSIZE /* backtrace() stack */ \ +) /* ** For keeping track of the number of mallocs and frees. This @@ -60,35 +193,25 @@ int sqlite3Tsd()->mallocFailed = 0; */ int sqlite3_nMalloc; /* Number of sqliteMalloc() calls */ int sqlite3_nFree; /* Number of sqliteFree() calls */ -int sqlite3_memUsed; /* Total memory obtained from malloc */ -int sqlite3_memMax; /* Mem usage high-water mark */ +int sqlite3_memUsed; /* TODO Total memory obtained from malloc */ +int sqlite3_memMax; /* TODO Mem usage high-water mark */ int sqlite3_iMallocFail; /* Fail sqliteMalloc() after this many calls */ int sqlite3_iMallocReset = -1; /* When iMallocFail reaches 0, set to this */ -#if SQLITE_MEMDEBUG>1 -static int memcnt = 0; -#endif - -/* -** Number of 32-bit guard words. This should probably be a multiple of -** 2 since on 64-bit machines we want the value returned by sqliteMalloc() -** to be 8-byte aligned. -*/ -#define N_GUARD 2 /* ** Check for a simulated memory allocation failure. Return true if ** the failure should be simulated. Return false to proceed as normal. */ -static int simulatedMallocFailure(int n, char *zFile, int line){ +static int failMalloc(){ + SqliteTsd *pTsd = sqlite3Tsd(); + if( pTsd->isFail ){ + return 1; + } if( sqlite3_iMallocFail>=0 ){ sqlite3_iMallocFail--; if( sqlite3_iMallocFail==0 ){ - sqlite3Tsd()->mallocFailed++; -#if SQLITE_MEMDEBUG>1 - fprintf(stderr,"**** failed to allocate %d bytes at %s:%d\n", - n, zFile,line); -#endif sqlite3_iMallocFail = sqlite3_iMallocReset; + pTsd->isFail = 1; return 1; } } @@ -96,303 +219,368 @@ static int simulatedMallocFailure(int n, char *zFile, int line){ } /* -** Allocate new memory and set it to zero. Return NULL if -** no memory is available. +** The argument is a pointer returned by sqlite3OsMalloc() or Realloc(). +** assert() that the first and last (TESTALLOC_NGUARD*4) bytes are set to the +** values set by the applyGuards() function. */ -void *sqlite3Malloc_(int n, int bZero, char *zFile, int line){ - void *p; - int *pi; - int i, k; - - /* Any malloc() calls between a malloc() failure and clearing the - ** mallocFailed flag (done before returning control to the user) - ** automatically fail. Although this restriction may have to be relaxed in - ** the future, for now it makes the system easier to test. - ** - ** TODO: This will eventually be done as part of the same wrapper that may - ** call sqlite3_release_memory(). Above the sqlite3OsMalloc() level. - */ - if( sqlite3Tsd()->mallocFailed ){ - return 0; - } +static void checkGuards(u32 *p) +{ + int i; + char *zAlloc = (char *)p; + char *z; - if( n==0 ){ - return 0; + /* First set of guard words */ + z = &zAlloc[TESTALLOC_OFFSET_GUARD1(p)]; + for(i=0; i0 ) sqlite3Tsd()->mallocFailed++; - return 0; + + /* Second set of guard words */ + z = &zAlloc[TESTALLOC_OFFSET_GUARD2(p)]; + for(i=0; i1 - print_stack_trace(); - fprintf(stderr,"%06d malloc %d bytes at 0x%x from %s:%d\n", - ++memcnt, n, (int)p, zFile,line); -#endif - return p; } /* -** This version of malloc is always a real function, never a macro +** The argument is a pointer returned by sqlite3OsMalloc() or Realloc(). The +** first and last (TESTALLOC_NGUARD*4) bytes are set to known values for use as +** guard-posts. */ -void *sqlite3MallocX(int n){ - return sqlite3Malloc_(n, 0, __FILE__, __LINE__); +static void applyGuards(u32 *p) +{ + int i; + char *z; + char *zAlloc = (char *)p; + + /* First set of guard words */ + z = &zAlloc[TESTALLOC_OFFSET_GUARD1(p)]; + for(i=0; iiLine, sizeof(u32)); + + /* File name */ + z = &zAlloc[TESTALLOC_OFFSET_FILENAME(p)]; + strncpy(z, sqlite3Tsd()->zFile, TESTALLOC_FILESIZE); + z[TESTALLOC_FILESIZE - 1] = '\0'; + + /* User string */ + z = &zAlloc[TESTALLOC_OFFSET_USER(p)]; + z[0] = 0; + if( sqlite3_malloc_id ){ + strncpy(z, sqlite3_malloc_id, TESTALLOC_USERSIZE); + z[TESTALLOC_USERSIZE-1] = 0; + } + + /* backtrace() stack */ + z = &zAlloc[TESTALLOC_OFFSET_STACK(p)]; + backtrace((void **)z, TESTALLOC_STACKFRAMES); + + /* Sanity check to make sure checkGuards() is working */ + checkGuards(p); +} + +static void *getOsPointer(void *p) +{ + char *z = (char *)p; + return (void *)(&z[-1 * TESTALLOC_OFFSET_DATA(p)]); } /* -** Check to see if the given pointer was obtained from sqliteMalloc() -** and is able to hold at least N bytes. Raise an exception if this -** is not the case. -** -** This routine is used for testing purposes only. +** The argument points to an Os level allocation. Link it into the threads list +** of allocations. */ -void sqlite3CheckMemory(void *p, int N){ - int *pi = p; - int n, i, k; - pi -= N_GUARD+1; - for(i=0; i=0 && NpFirst; + if( pTsd->pFirst ){ + ((void **)pTsd->pFirst)[0] = p; } + pTsd->pFirst = p; } /* -** Free memory previously obtained from sqliteMalloc() +** The argument points to an Os level allocation. Unlinke it from the threads +** list of allocations. */ -void sqlite3Free_(void *p, char *zFile, int line){ - if( p ){ - int *pi, i, k, n; - pi = p; - pi -= N_GUARD+1; - sqlite3_nFree++; - for(i=0; ipFirst ){ + assert(!pp[0]); + assert(!pp[1] || ((void **)(pp[1]))[0]==p); + pTsd->pFirst = pp[1]; + if( pTsd->pFirst ){ + ((void **)pTsd->pFirst)[0] = 0; } - n = pi[N_GUARD]; - sqlite3_memUsed -= n; - k = (n+sizeof(int)-1)/sizeof(int); - for(i=0; i1 - fprintf(stderr,"%06d free %d bytes at 0x%x from %s:%d\n", - ++memcnt, n, (int)p, zFile,line); -#endif - free(pi); } } /* -** Resize a prior allocation. If p==0, then this routine -** works just like sqliteMalloc(). If n==0, then this routine -** works just like sqliteFree(). +** Pointer p is a pointer to an OS level allocation that has just been +** realloc()ed. Set the list pointers that point to this entry to it's new +** location. */ -void *sqlite3Realloc_(void *oldP, int n, char *zFile, int line){ - int *oldPi, *pi, i, k, oldN, oldK; - void *p; - if( oldP==0 ){ - return sqlite3Malloc_(n,1,zFile,line); - } - if( n==0 ){ - sqlite3Free_(oldP,zFile,line); - return 0; - } - if( simulatedMallocFailure(n, zFile, line) ){ - return 0; +static void relinkAlloc(void *p) +{ + void **pp = (void **)p; + if( pp[0] ){ + ((void **)(pp[0]))[1] = p; + }else{ + SqliteTsd *pTsd = sqlite3Tsd(); + pTsd->pFirst = p; } - oldPi = oldP; - oldPi -= N_GUARD+1; - if( oldPi[0]!=0xdead1122 ){ - fprintf(stderr,"Low-end memory corruption in realloc at 0x%x\n", (int)oldP); - return 0; + if( pp[1] ){ + ((void **)(pp[1]))[0] = p; } - oldN = oldPi[N_GUARD]; - sqlite3_memUsed -= oldN; - oldK = (oldN+sizeof(int)-1)/sizeof(int); - for(i=0; i +int sqlite3OutstandingMallocs(Tcl_Interp *interp){ + void *p; + SqliteTsd *pTsd = sqlite3Tsd(); + Tcl_Obj *pRes = Tcl_NewObj(); + Tcl_IncrRefCount(pRes); + + for(p=pTsd->pFirst; p; p=((void **)p)[1]){ + Tcl_Obj *pEntry = Tcl_NewObj(); + Tcl_Obj *pStack = Tcl_NewObj(); + char *z; + u32 iLine; + int nBytes = sqlite3OsAllocationSize(p) - TESTALLOC_OVERHEAD; + char *zAlloc = (char *)p; + int i; + + Tcl_ListObjAppendElement(0, pEntry, Tcl_NewIntObj(nBytes)); + + z = &zAlloc[TESTALLOC_OFFSET_FILENAME(p)]; + Tcl_ListObjAppendElement(0, pEntry, Tcl_NewStringObj(z, -1)); + + z = &zAlloc[TESTALLOC_OFFSET_LINENUMBER(p)]; + memcpy(&iLine, z, sizeof(u32)); + Tcl_ListObjAppendElement(0, pEntry, Tcl_NewIntObj(iLine)); + + z = &zAlloc[TESTALLOC_OFFSET_USER(p)]; + Tcl_ListObjAppendElement(0, pEntry, Tcl_NewStringObj(z, -1)); + + z = &zAlloc[TESTALLOC_OFFSET_STACK(p)]; + for(i=0; i0 ) sqlite3Tsd()->mallocFailed++; - return 0; - } - for(i=0; ioldN ? oldN : n); - if( n>oldN ){ - memset(&((char*)p)[oldN], 0x55, n-oldN); - } - memset(oldPi, 0xab, (oldK+N_GUARD+2)*sizeof(int)); - free(oldPi); -#if SQLITE_MEMDEBUG>1 - print_stack_trace(); - fprintf(stderr,"%06d realloc %d to %d bytes at 0x%x to 0x%x at %s:%d\n", - ++memcnt, oldN, n, (int)oldP, (int)p, zFile, line); -#endif - return p; + + Tcl_ResetResult(interp); + Tcl_SetObjResult(interp, pRes); + Tcl_DecrRefCount(pRes); + return TCL_OK; } +#endif /* -** Make a copy of a string in memory obtained from sqliteMalloc() +** This is the test layer's wrapper around sqlite3OsMalloc(). */ -char *sqlite3StrDup_(const char *z, char *zFile, int line){ - char *zNew; - if( z==0 ) return 0; - zNew = sqlite3Malloc_(strlen(z)+1, 0, zFile, line); - if( zNew ) strcpy(zNew, z); - return zNew; -} -char *sqlite3StrNDup_(const char *z, int n, char *zFile, int line){ - char *zNew; - if( z==0 ) return 0; - zNew = sqlite3Malloc_(n+1, 0, zFile, line); - if( zNew ){ - memcpy(zNew, z, n); - zNew[n] = 0; +static void * OSMALLOC(int n){ + if( !failMalloc() ){ + u32 *p; + p = (u32 *)sqlite3OsMalloc(n + TESTALLOC_OVERHEAD); + assert(p); + sqlite3_nMalloc++; + applyGuards(p); + linkAlloc(p); + return (void *)(&p[TESTALLOC_NGUARD + 2*sizeof(void *)/sizeof(u32)]); } - return zNew; + return 0; } /* -** A version of sqliteFree that is always a function, not a macro. +** This is the test layer's wrapper around sqlite3OsFree(). The argument is a +** pointer to the space allocated for the application to use. */ -void sqlite3FreeX(void *p){ - sqliteFree(p); +void OSFREE(void *pFree){ + u32 *p = (u32 *)getOsPointer(pFree); /* p points to Os level allocation */ + checkGuards(p); + unlinkAlloc(p); + sqlite3OsFree(p); + sqlite3_nFree++; } -#endif /* SQLITE_MEMDEBUG */ /* -** The following versions of malloc() and free() are for use in a -** normal build. +** This is the test layer's wrapper around sqlite3OsRealloc(). */ -#if !defined(SQLITE_MEMDEBUG) +void * OSREALLOC(void *pRealloc, int n){ + if( !failMalloc() ){ + u32 *p = (u32 *)getOsPointer(pRealloc); + checkGuards(p); + p = sqlite3OsRealloc(p, n + TESTALLOC_OVERHEAD); + applyGuards(p); + relinkAlloc(p); + return (void *)(&p[TESTALLOC_NGUARD + 2*sizeof(void *)/sizeof(u32)]); + } + return 0; +} + +void OSMALLOC_FAILED(){ + sqlite3Tsd()->isFail = 0; +} +#else +#define OSMALLOC(x) sqlite3OsMalloc(x) +#define OSREALLOC(x,y) sqlite3OsRealloc(x,y) +#define OSFREE(x) sqlite3OsFree(x) +#define OSMALLOC_FAILED() +#endif /* -** Allocate new memory and set it to zero. Return NULL if -** no memory is available. See also sqliteMallocRaw(). +** End code for memory allocation system test layer. +**--------------------------------------------------------------------------*/ + +/* +** Allocate and return N bytes of uninitialised memory by calling +** sqlite3OsMalloc(). If the Malloc() call fails, attempt to free memory +** by calling sqlite3_release_memory(). */ -void *sqlite3Malloc(int n){ - void *p; - if( n==0 ) return 0; - if( (p = malloc(n))==0 ){ - if( n>0 ) sqlite3Tsd()->mallocFailed++; - }else{ - memset(p, 0, n); +void *sqlite3MallocRaw(int n){ + SqliteTsd *pTsd = sqlite3Tsd(); + void *p = 0; + + if( n>0 && !pTsd->mallocFailed ){ + while( !(p = OSMALLOC(n)) && sqlite3_release_memory(n) ); + if( !p ){ + sqlite3Tsd()->mallocFailed = 1; + OSMALLOC_FAILED(); + } } return p; } /* -** Allocate new memory but do not set it to zero. Return NULL if -** no memory is available. See also sqliteMalloc(). +** Resize the allocation at p to n bytes by calling sqlite3OsRealloc(). The +** pointer to the new allocation is returned. If the Realloc() call fails, +** attempt to free memory by calling sqlite3_release_memory(). */ -void *sqlite3MallocRaw(int n){ - void *p; - if( n==0 ) return 0; - if( (p = malloc(n))==0 ){ - if( n>0 ) sqlite3Tsd()->mallocFailed++; +void *sqlite3Realloc(void *p, int n){ + SqliteTsd *pTsd = sqlite3Tsd(); + if( pTsd->mallocFailed ){ + return 0; + } + + if( !p ){ + return sqlite3Malloc(n); + }else{ + void *np = 0; + while( !(np = OSREALLOC(p, n)) && sqlite3_release_memory(n) ); + if( !np ){ + pTsd->mallocFailed = 1; + OSMALLOC_FAILED(); + } + return np; } - return p; } /* -** Free memory previously obtained from sqliteMalloc() +** Free the memory pointed to by p. p must be either a NULL pointer or a +** value returned by a previous call to sqlite3Malloc() or sqlite3Realloc(). */ void sqlite3FreeX(void *p){ if( p ){ - free(p); + OSFREE(p); } } /* -** Resize a prior allocation. If p==0, then this routine -** works just like sqliteMalloc(). If n==0, then this routine -** works just like sqliteFree(). +** A version of sqliteMalloc() that is always a function, not a macro. +** Currently, this is used only to alloc only used drawback. */ -void *sqlite3Realloc(void *p, int n){ - void *p2; - if( p==0 ){ - return sqliteMalloc(n); - } - if( n==0 ){ - sqliteFree(p); - return 0; +void *sqlite3MallocX(int n){ + return sqliteMalloc(n); +} + +/* +** sqlite3Malloc +** sqlite3ReallocOrFree +** +** These two are implemented as wrappers around sqlite3MallocRaw(), +** sqlite3Realloc() and sqlite3Free(). +*/ +void *sqlite3Malloc(int n){ + void *p = sqlite3MallocRaw(n); + if( p ){ + memset(p, 0, n); } - p2 = realloc(p, n); - if( p2==0 ){ - if( n>0 ) sqlite3Tsd()->mallocFailed++; + return p; +} +void sqlite3ReallocOrFree(void **pp, int n){ + void *p = sqlite3Realloc(*pp, n); + if( !p ){ + sqlite3FreeX(*pp); } - return p2; + *pp = p; } /* -** Make a copy of a string in memory obtained from sqliteMalloc() +** Make a copy of a string in memory obtained from sqliteMalloc(). These +** functions call sqlite3MallocRaw() directly instead of sqliteMalloc(). This +** is because when memory debugging is turned on, these two functions are +** called via macros that record the current file and line number in the +** SqliteTsd structure. */ char *sqlite3StrDup(const char *z){ char *zNew; if( z==0 ) return 0; - zNew = sqliteMallocRaw(strlen(z)+1); + zNew = sqlite3MallocRaw(strlen(z)+1); if( zNew ) strcpy(zNew, z); return zNew; } char *sqlite3StrNDup(const char *z, int n){ char *zNew; if( z==0 ) return 0; - zNew = sqliteMallocRaw(n+1); + zNew = sqlite3MallocRaw(n+1); if( zNew ){ memcpy(zNew, z, n); zNew[n] = 0; } return zNew; } -#endif /* !defined(SQLITE_MEMDEBUG) */ - -/* -** Reallocate a buffer to a different size. This is similar to -** sqliteRealloc() except that if the allocation fails the buffer -** is freed. -*/ -void sqlite3ReallocOrFree(void **ppBuf, int newSize){ - void *pNew = sqliteRealloc(*ppBuf, newSize); - if( pNew==0 ){ - sqliteFree(*ppBuf); - } - *ppBuf = pNew; -} /* ** Create a string from the 2nd and subsequent arguments (up to the @@ -426,11 +614,6 @@ void sqlite3SetString(char **pz, ...){ zResult += strlen(zResult); } va_end(ap); -#ifdef SQLITE_MEMDEBUG -#if SQLITE_MEMDEBUG>1 - fprintf(stderr,"string at 0x%x is %s\n", (int)*pz, *pz); -#endif -#endif } /* @@ -1026,14 +1209,44 @@ void *sqlite3TextToPtr(const char *z){ /* ** Return a pointer to the SqliteTsd associated with the calling thread. */ -static SqliteTsd tsd = { 0 }; +static SqliteTsd tsd = { + 0 /* mallocFailed flag */ +#ifndef NDEBUG + , 1 /* mallocAllowed flag */ +#endif +#ifndef SQLITE_MEMDEBUG + , 0 + , 0 + , 0 + , 0 +#endif +}; SqliteTsd *sqlite3Tsd(){ return &tsd; } -void sqlite3ClearMallocFailed(){ +void sqlite3MallocClearFailed(){ sqlite3Tsd()->mallocFailed = 0; } +#ifndef NDEBUG +/* +** This function sets a flag in the thread-specific-data structure that will +** cause an assert to fail if sqliteMalloc() or sqliteRealloc() is called. +*/ +void sqlite3MallocDisallow(){ + assert(sqlite3Tsd()->mallocAllowed); + sqlite3Tsd()->mallocAllowed = 0; +} + +/* +** This function clears the flag set in the thread-specific-data structure set +** by sqlite3MallocDisallow(). +*/ +void sqlite3MallocAllow(){ + assert(!sqlite3Tsd()->mallocAllowed); + sqlite3Tsd()->mallocAllowed = 1; +} +#endif diff --git a/src/vacuum.c b/src/vacuum.c index b3533acfe5..8fe0550b3d 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -14,7 +14,7 @@ ** Most of the code in this file may be omitted by defining the ** SQLITE_OMIT_VACUUM macro. ** -** $Id: vacuum.c,v 1.51 2005/12/06 17:48:32 danielk1977 Exp $ +** $Id: vacuum.c,v 1.52 2005/12/09 14:25:09 danielk1977 Exp $ */ #include "sqliteInt.h" #include "vdbeInt.h" @@ -311,12 +311,17 @@ end_of_vacuum: db->autoCommit = 1; if( pDetach ){ + int mf = sqlite3Tsd()->mallocFailed; + sqlite3Tsd()->mallocFailed = 0; + sqlite3MallocDisallow(); ((Vdbe *)pDetach)->expired = 0; sqlite3_step(pDetach); rc2 = sqlite3_finalize(pDetach); if( rc==SQLITE_OK ){ rc = rc2; } + sqlite3MallocAllow(); + sqlite3Tsd()->mallocFailed = mf; } /* If one of the execSql() calls above returned SQLITE_NOMEM, then the diff --git a/src/vdbe.c b/src/vdbe.c index 7a178a2a4e..b1de8d8d2f 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.502 2005/12/06 12:53:01 danielk1977 Exp $ +** $Id: vdbe.c,v 1.503 2005/12/09 14:25:09 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -390,11 +390,15 @@ int sqlite3VdbeExec( if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE; assert( db->magic==SQLITE_MAGIC_BUSY ); + pTos = p->pTos; + if( p->rc==SQLITE_NOMEM ){ + /* This happens if a malloc() inside a call to sqlite3_column_text() or + ** sqlite3_column_text16() failed. */ + goto no_mem; + } assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY ); p->rc = SQLITE_OK; assert( p->explain==0 ); - pTos = p->pTos; - if( sqlite3Tsd()->mallocFailed ) goto no_mem; if( p->popStack ){ popStack(&pTos, p->popStack); p->popStack = 0; @@ -3898,13 +3902,13 @@ case OP_ParseSchema: { /* no-push */ db->init.busy = 1; assert(0==sqlite3Tsd()->mallocFailed); rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); + sqliteFree(zSql); db->init.busy = 0; sqlite3SafetyOn(db); if( rc==SQLITE_NOMEM ){ sqlite3Tsd()->mallocFailed = 1; goto no_mem; } - sqliteFree(zSql); break; } diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 5d5e9cbb92..b0a1c612a5 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -241,7 +241,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){ #endif sqlite3Error(p->db, rc, p->zErrMsg ? "%s" : 0, p->zErrMsg); - sqlite3ClearMallocFailed(); + sqlite3MallocClearFailed(); return rc; } @@ -378,30 +378,78 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ return &pVm->pTos[(1-vals)+i]; } +/* +** This function is called after invoking an sqlite3_value_XXX function on a +** column value (i.e. a value returned by evaluating an SQL expression in the +** select list of a SELECT statement) that may cause a malloc() failure. If +** malloc() has failed, the threads mallocFailed flag is cleared and the result +** code of statement pStmt set to SQLITE_NOMEM. +** +** Specificly, this is called from within: +** +** sqlite3_column_int() +** sqlite3_column_int64() +** sqlite3_column_text() +** sqlite3_column_text16() +** sqlite3_column_real() +** sqlite3_column_bytes() +** sqlite3_column_bytes16() +** +** But not for sqlite3_column_blob(), which never calls malloc(). +*/ +static void columnMallocFailure(sqlite3_stmt *pStmt) +{ + /* If malloc() failed during an encoding conversion within an + ** sqlite3_column_XXX API, then set the return code of the statement to + ** SQLITE_NOMEM. The next call to _step() (if any) will return SQLITE_ERROR + ** and _finalize() will return NOMEM. + */ + if( sqlite3Tsd()->mallocFailed ){ + ((Vdbe *)pStmt)->rc = SQLITE_NOMEM; + sqlite3MallocClearFailed(); + } +} + /**************************** sqlite3_column_ ******************************* ** The following routines are used to access elements of the current row ** in the result set. */ const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_blob( columnMem(pStmt,i) ); + const void *val; + sqlite3MallocDisallow(); + val = sqlite3_value_blob( columnMem(pStmt,i) ); + sqlite3MallocAllow(); + return val; } int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_bytes( columnMem(pStmt,i) ); + int val = sqlite3_value_bytes( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; } int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_bytes16( columnMem(pStmt,i) ); + int val = sqlite3_value_bytes16( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; } double sqlite3_column_double(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_double( columnMem(pStmt,i) ); + double val = sqlite3_value_double( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; } int sqlite3_column_int(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_int( columnMem(pStmt,i) ); + int val = sqlite3_value_int( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; } sqlite_int64 sqlite3_column_int64(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_int64( columnMem(pStmt,i) ); + sqlite_int64 val = sqlite3_value_int64( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; } const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_text( columnMem(pStmt,i) ); + const unsigned char *val = sqlite3_value_text( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; } #if 0 sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){ @@ -410,7 +458,9 @@ sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){ #endif #ifndef SQLITE_OMIT_UTF16 const void *sqlite3_column_text16(sqlite3_stmt *pStmt, int i){ - return sqlite3_value_text16( columnMem(pStmt,i) ); + const void *val = sqlite3_value_text16( columnMem(pStmt,i) ); + columnMallocFailure(pStmt); + return val; } #endif /* SQLITE_OMIT_UTF16 */ int sqlite3_column_type(sqlite3_stmt *pStmt, int i){ @@ -452,11 +502,10 @@ static const void *columnName( /* A malloc may have failed inside of the xFunc() call. If this is the case, ** clear the mallocFailed flag and return NULL. */ - sqlite3ClearMallocFailed(); + sqlite3MallocClearFailed(); return ret; } - /* ** Return the name of the Nth column of the result set returned by SQL ** statement pStmt. diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 3949087b75..4c9cd0b93f 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -415,7 +415,7 @@ static void freeP3(int p3type, void *p3){ void sqlite3VdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){ Op *pOp; assert( p->magic==VDBE_MAGIC_INIT ); - if( p==0 || p->aOp==0 ){ + if( p==0 || p->aOp==0 || sqlite3Tsd()->mallocFailed ){ if (n != P3_KEYINFO) { freeP3(n, (void*)*(char**)&zP3); } @@ -470,7 +470,7 @@ void sqlite3VdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){ void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){ va_list ap; assert( p->nOp>0 ); - assert( p->aOp==0 || p->aOp[p->nOp-1].p3==0 ); + assert( p->aOp==0 || p->aOp[p->nOp-1].p3==0 || sqlite3Tsd()->mallocFailed ); va_start(ap, zFormat); sqlite3VdbeChangeP3(p, -1, sqlite3VMPrintf(zFormat, ap), P3_DYNAMIC); va_end(ap); diff --git a/test/malloc3.test b/test/malloc3.test index f1259cda17..c1f13652b2 100644 --- a/test/malloc3.test +++ b/test/malloc3.test @@ -13,7 +13,7 @@ # correctly. The emphasis of these tests are the _prepare(), _step() and # _finalize() calls. # -# $Id: malloc3.test,v 1.3 2005/12/07 06:27:45 danielk1977 Exp $ +# $Id: malloc3.test,v 1.4 2005/12/09 14:25:12 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -580,14 +580,19 @@ proc run_test {arglist {pcstart 0} {iFailStart 1}} { default { error "Unknown switch: $k" } } +# if {$iFail > ($iFailStart+1)} return } } # Turn of the Tcl interface's prepared statement caching facility. db cache size 0 -#run_test $::run_test_script 59 97 -run_test $::run_test_script +run_test $::run_test_script +# run_test [lrange $::run_test_script 0 3] 0 63 sqlite_malloc_fail 0 +db close + +pp_check_for_leaks + finish_test diff --git a/test/malloc4.test b/test/malloc4.test index eee0afc68e..c769ff1712 100644 --- a/test/malloc4.test +++ b/test/malloc4.test @@ -12,11 +12,15 @@ # This file contains tests to ensure that the library handles malloc() failures # correctly. The emphasis in this file is on sqlite3_column_XXX() APIs. # -# $Id: malloc4.test,v 1.1 2005/12/07 06:27:45 danielk1977 Exp $ +# $Id: malloc4.test,v 1.2 2005/12/09 14:25:12 danielk1977 Exp $ #--------------------------------------------------------------------------- # NOTES ON EXPECTED BEHAVIOUR # +# [193] When a memory allocation failure occurs during sqlite3_column_name(), +# sqlite3_column_name16(), sqlite3_column_decltype(), or +# sqlite3_column_decltype16() the function shall return NULL. +# #--------------------------------------------------------------------------- set testdir [file dirname $argv0] @@ -29,7 +33,6 @@ if {[info command sqlite_malloc_stat]==""} { return } - proc do_stmt_test {id sql} { set ::sql $sql set go 1 @@ -48,6 +51,18 @@ proc do_stmt_test {id sql} { # Test malloc failure in the _name(), _name16(), decltype() and # decltype16() APIs. Calls that occur after the malloc() failure should # return NULL. No error is raised though. + # + # ${testid}.2.1 - Call _name() + # ${testid}.2.2 - Call _name16() + # ${testid}.2.3 - Call _name() + # ${testid}.2.4 - Check that the return values of the above three calls are + # consistent with each other and with the simulated + # malloc() failures. + # + # Because the code that implements the _decltype() and _decltype16() APIs + # is the same as the _name() and _name16() implementations, we don't worry + # about explicitly testing them. + # do_test ${testid}.2.1 { set mf1 [expr [lindex [sqlite_malloc_stat] 2] <= 0] set ::name8 [sqlite3_column_name $::STMT 0] @@ -78,27 +93,83 @@ proc do_stmt_test {id sql} { } } {1} - if {$::mallocFailed == 0} { + # Step the statement so that we can call _text() and _text16(). Before + # running sqlite3_step(), make sure that malloc() is not about to fail. + # Memory allocation failures that occur within sqlite3_step() are tested + # elsewhere. + set mf [lindex [sqlite_malloc_stat] 2] + sqlite_malloc_fail 0 + do_test ${testid}.3 { + sqlite3_step $::STMT + } {SQLITE_ROW} + sqlite_malloc_fail $mf + + # Test for malloc() failures within _text() and _text16(). + # + do_test ${testid}.4.1 { + set ::text8 [sqlite3_column_text $::STMT 0] + set mf [expr [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed] + expr {$mf==0 || $::text8 == ""} + } {1} + do_test ${testid}.4.2 { + set ::text16 [sqlite3_column_text16 $::STMT 0] + set ::text16 [encoding convertfrom unicode $::text16] + set ::text16 [string range $::text16 0 end-1] + set mf [expr [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed] + expr {$mf==0 || $::text16 == ""} + } {1} + do_test ${testid}.4.3 { + set ::text8_2 [sqlite3_column_text $::STMT 0] + set mf [expr [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed] + expr {$mf==0 || $::text8_2 == "" || ($::text16 == "" && $::text8 != "")} + } {1} + + # Test for malloc() failures within _int(), _int64() and _real(). The only + # way this can occur is if the string has to be translated from UTF-16 to + # UTF-8 before being converted to a numeric value. + do_test ${testid}.4.4.1 { + set mf [lindex [sqlite_malloc_stat] 2] sqlite_malloc_fail 0 - set go 0 - } + sqlite3_column_text16 $::STMT 0 + sqlite_malloc_fail $mf + sqlite3_column_int $::STMT 0 + } {0} + do_test ${testid}.4.5 { + set mf [lindex [sqlite_malloc_stat] 2] + sqlite_malloc_fail 0 + sqlite3_column_text16 $::STMT 0 + sqlite_malloc_fail $mf + sqlite3_column_int64 $::STMT 0 + } {0} + + do_test ${testid}.4.6 { + set mf [lindex [sqlite_malloc_stat] 2] + sqlite_malloc_fail 0 + sqlite3_column_text16 $::STMT 0 + sqlite_malloc_fail $mf + sqlite3_column_double $::STMT 0 + } {0.0} -if 0 { + set mallocFailedAfterStep [expr \ + [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed + ] + + sqlite_malloc_fail 0 # Test that if a malloc() failed the next call to sqlite3_step() returns - # SQLITE_ERROR. If malloc() did not fail, it should return SQLITE_ROW. + # SQLITE_ERROR. If malloc() did not fail, it should return SQLITE_DONE. # - # Before running sqlite3_step(), make sure that malloc() is not about to - # fail. Memory allocation failures that occur within sqlite3_step() are - # tested elsewhere. - do_test ${testid}.3 { - set rc [sqlite3_step $::STMT] - list [expr $rc=="SQLITE_ERROR"] [expr $rc=="SQLITE_ROW"] - } [list $mallocFailed [expr !$mallocFailed]] -} + do_test ${testid}.5 { + sqlite3_step $::STMT + } [expr {$mallocFailedAfterStep ? "SQLITE_ERROR" : "SQLITE_DONE"}] - do_test ${testid}.4 { + do_test ${testid}.6 { sqlite3_finalize $::STMT - } {SQLITE_OK} + } [expr {$mallocFailedAfterStep ? "SQLITE_NOMEM" : "SQLITE_OK"}] + + if {$::mallocFailed == 0 && $mallocFailedAfterStep == 0} { + sqlite_malloc_fail 0 + set go 0 + } } } @@ -106,7 +177,11 @@ execsql { CREATE TABLE tbl( the_first_reasonably_long_column_name that_also_has_quite_a_lengthy_type ); + INSERT INTO tbl VALUES( + 'An extra long string. Far too long to be stored in NBFS bytes.' + ); } + do_stmt_test 1 "SELECT * FROM tbl" sqlite_malloc_fail 0 diff --git a/test/select1.test b/test/select1.test index 8a2a4f0b3c..7445ed1d93 100644 --- a/test/select1.test +++ b/test/select1.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the SELECT statement. # -# $Id: select1.test,v 1.43 2005/09/08 10:37:01 drh Exp $ +# $Id: select1.test,v 1.44 2005/12/09 14:25:12 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -799,5 +799,4 @@ ifcapable {compound && subquery} { } {x 1 x 3} } ;# ifcapable compound - finish_test diff --git a/test/tester.tcl b/test/tester.tcl index ab35a64f94..8cc925cd19 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -11,7 +11,7 @@ # This file implements some common TCL routines used for regression # testing the SQLite library # -# $Id: tester.tcl,v 1.52 2005/11/26 00:25:04 drh Exp $ +# $Id: tester.tcl,v 1.53 2005/12/09 14:25:12 danielk1977 Exp $ # Make sure tclsqlite3 was compiled correctly. Abort now with an # error message if not. @@ -83,6 +83,7 @@ set maxErr 1000 # proc do_test {name cmd expected} { global argv nErr nTest skip_test maxErr + set ::sqlite_malloc_id $name if {$skip_test} { set skip_test 0 return @@ -139,7 +140,13 @@ proc finish_test {} { proc finalize_testing {} { global nTest nErr nProb sqlite_open_file_count if {$nErr==0} memleak_check + catch {db close} + catch {db2 close} + catch {db3 close} + +pp_check_for_leaks + puts "$nErr errors out of $nTest tests" puts "Failures on these tests: $::failList" if {$nProb>0} { @@ -423,6 +430,59 @@ proc copy_file {from to} { } } +# This command checks for outstanding calls to sqliteMalloc() from within +# the current thread. A list is returned with one entry for each outstanding +# malloc. Each list entry is itself a list of 5 items, as follows: +# +# { } +# +proc check_for_leaks {} { + set ret [list] + foreach alloc [sqlite_malloc_outstanding] { + foreach {nBytes file iLine userstring backtrace} $alloc {} + set stack [list] + set skip 0 + + # The first command in this block will probably fail on windows. This + # means there will be no stack dump available. + catch { + set stuff [eval "exec addr2line -e ./testfixture -f $backtrace"] + foreach {func line} $stuff { + if {$func != "??" || $line != "??:0"} { + regexp {.*/(.*)} $line dummy line + lappend stack "${func}() $line" + } else { + if {[lindex $stack end] != "..."} { + lappend stack "..." + } + } + } + } + + if {!$skip} { + lappend ret [list $nBytes $file $iLine $userstring $stack] + } + } + return $ret +} + +# Pretty print a report based on the return value of [check_for_leaks] to +# stdout. +proc pp_check_for_leaks {} { + set l [check_for_leaks] + set n 0 + foreach leak $l { + foreach {nBytes file iLine userstring stack} $leak {} + puts "$nBytes bytes leaked at $file:$iLine ($userstring)" + foreach frame $stack { + puts " $frame" + } + incr n $nBytes + } + puts "Memory leaked: $n bytes in [llength $l] allocations" + puts "" +} + # If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set # to non-zero, then set the global variable $AUTOVACUUM to 1. set AUTOVACUUM $sqlite_options(default_autovacuum)