/* ** 2015-05-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This is a utility program designed to aid running regressions tests on ** the SQLite library using data from external fuzzers. ** ** This program reads content from an SQLite database file with the following ** schema: ** ** CREATE TABLE db( ** dbid INTEGER PRIMARY KEY, -- database id ** dbcontent BLOB -- database disk file image ** ); ** CREATE TABLE xsql( ** sqlid INTEGER PRIMARY KEY, -- SQL script id ** sqltext TEXT -- Text of SQL statements to run ** ); ** CREATE TABLE IF NOT EXISTS readme( ** msg TEXT -- Human-readable description of this test collection ** ); ** ** For each database file in the DB table, the SQL text in the XSQL table ** is run against that database. All README.MSG values are printed prior ** to the start of the test (unless the --quiet option is used). If the ** DB table is empty, then all entries in XSQL are run against an empty ** in-memory database. ** ** This program is looking for crashes, assertion faults, and/or memory leaks. ** No attempt is made to verify the output. The assumption is that either all ** of the database files or all of the SQL statements are malformed inputs, ** generated by a fuzzer, that need to be checked to make sure they do not ** present a security risk. ** ** This program also includes some command-line options to help with ** creation and maintenance of the source content database. The command ** ** ./fuzzcheck database.db --load-sql FILE... ** ** Loads all FILE... arguments into the XSQL table. The --load-db option ** works the same but loads the files into the DB table. The -m option can ** be used to initialize the README table. The "database.db" file is created ** if it does not previously exist. Example: ** ** ./fuzzcheck new.db --load-sql *.sql ** ./fuzzcheck new.db --load-db *.db ** ./fuzzcheck new.db -m 'New test cases' ** ** The three commands above will create the "new.db" file and initialize all ** tables. Then do "./fuzzcheck new.db" to run the tests. ** ** DEBUGGING HINTS: ** ** If fuzzcheck does crash, it can be run in the debugger and the content ** of the global variable g.zTextName[] will identify the specific XSQL and ** DB values that were running when the crash occurred. ** ** DBSQLFUZZ: ** ** The dbsqlfuzz fuzzer includes both a database file and SQL to run against ** that database in its input. This utility can now process dbsqlfuzz ** input files. Load such files using the "--load-dbsql FILE ..." command-line ** option. ** ** Dbsqlfuzz inputs are ordinary text. The first part of the file is text ** that describes the content of the database (using a lot of hexadecimal), ** then there is a divider line followed by the SQL to run against the ** database. Because they are ordinary text, dbsqlfuzz inputs are stored ** in the XSQL table, as if they were ordinary SQL inputs. The isDbSql() ** function can look at a text string and determine whether or not it is ** a valid dbsqlfuzz input. */ #include #include #include #include #include #include #include "sqlite3.h" #define ISSPACE(X) isspace((unsigned char)(X)) #define ISDIGIT(X) isdigit((unsigned char)(X)) #ifdef __unix__ # include # include #endif #include #if !defined(_MSC_VER) # include #endif #if defined(_MSC_VER) typedef unsigned char uint8_t; #endif /* ** Files in the virtual file system. */ typedef struct VFile VFile; struct VFile { char *zFilename; /* Filename. NULL for delete-on-close. From malloc() */ int sz; /* Size of the file in bytes */ int nRef; /* Number of references to this file */ unsigned char *a; /* Content of the file. From malloc() */ }; typedef struct VHandle VHandle; struct VHandle { sqlite3_file base; /* Base class. Must be first */ VFile *pVFile; /* The underlying file */ }; /* ** The value of a database file template, or of an SQL script */ typedef struct Blob Blob; struct Blob { Blob *pNext; /* Next in a list */ int id; /* Id of this Blob */ int seq; /* Sequence number */ int sz; /* Size of this Blob in bytes */ unsigned char a[1]; /* Blob content. Extra space allocated as needed. */ }; /* ** Maximum number of files in the in-memory virtual filesystem. */ #define MX_FILE 10 /* ** Maximum allowed file size */ #define MX_FILE_SZ 10000000 /* ** All global variables are gathered into the "g" singleton. */ static struct GlobalVars { const char *zArgv0; /* Name of program */ const char *zDbFile; /* Name of database file */ VFile aFile[MX_FILE]; /* The virtual filesystem */ int nDb; /* Number of template databases */ Blob *pFirstDb; /* Content of first template database */ int nSql; /* Number of SQL scripts */ Blob *pFirstSql; /* First SQL script */ unsigned int uRandom; /* Seed for the SQLite PRNG */ char zTestName[100]; /* Name of current test */ } g; /* ** Print an error message and quit. */ static void fatalError(const char *zFormat, ...){ va_list ap; fprintf(stderr, "%s", g.zArgv0); if( g.zDbFile ) fprintf(stderr, " %s", g.zDbFile); if( g.zTestName[0] ) fprintf(stderr, " (%s)", g.zTestName); fprintf(stderr, ": "); va_start(ap, zFormat); vfprintf(stderr, zFormat, ap); va_end(ap); fprintf(stderr, "\n"); exit(1); } /* ** signal handler */ #ifdef __unix__ static void signalHandler(int signum){ const char *zSig; if( signum==SIGABRT ){ zSig = "abort"; }else if( signum==SIGALRM ){ zSig = "timeout"; }else if( signum==SIGSEGV ){ zSig = "segfault"; }else{ zSig = "signal"; } fatalError(zSig); } #endif /* ** Set the an alarm to go off after N seconds. Disable the alarm ** if N==0 */ static void setAlarm(int N){ #ifdef __unix__ alarm(N); #else (void)N; #endif } #ifndef SQLITE_OMIT_PROGRESS_CALLBACK /* ** This an SQL progress handler. After an SQL statement has run for ** many steps, we want to interrupt it. This guards against infinite ** loops from recursive common table expressions. ** ** *pVdbeLimitFlag is true if the --limit-vdbe command-line option is used. ** In that case, hitting the progress handler is a fatal error. */ static int progressHandler(void *pVdbeLimitFlag){ if( *(int*)pVdbeLimitFlag ) fatalError("too many VDBE cycles"); return 1; } #endif /* ** Reallocate memory. Show and error and quit if unable. */ static void *safe_realloc(void *pOld, int szNew){ void *pNew = realloc(pOld, szNew<=0 ? 1 : szNew); if( pNew==0 ) fatalError("unable to realloc for %d bytes", szNew); return pNew; } /* ** Initialize the virtual file system. */ static void formatVfs(void){ int i; for(i=0; i0 ){ fatalError("file %d still open. nRef=%d", i, g.aFile[i].nRef); } g.aFile[i].sz = -1; free(g.aFile[i].a); g.aFile[i].a = 0; g.aFile[i].nRef = 0; } } /* ** Find a VFile by name */ static VFile *findVFile(const char *zName){ int i; if( zName==0 ) return 0; for(i=0; i=0; i++){} if( i>=MX_FILE ) return 0; pNew = &g.aFile[i]; if( zName ){ int nName = (int)strlen(zName)+1; pNew->zFilename = safe_realloc(0, nName); memcpy(pNew->zFilename, zName, nName); }else{ pNew->zFilename = 0; } pNew->nRef = 0; pNew->sz = sz; pNew->a = safe_realloc(0, sz); if( sz>0 ) memcpy(pNew->a, pData, sz); return pNew; } /* ** Implementation of the "readfile(X)" SQL function. The entire content ** of the file named X is read and returned as a BLOB. NULL is returned ** if the file does not exist or is unreadable. */ static void readfileFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zName; FILE *in; long nIn; void *pBuf; zName = (const char*)sqlite3_value_text(argv[0]); if( zName==0 ) return; in = fopen(zName, "rb"); if( in==0 ) return; fseek(in, 0, SEEK_END); nIn = ftell(in); rewind(in); pBuf = sqlite3_malloc64( nIn ); if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ sqlite3_result_blob(context, pBuf, nIn, sqlite3_free); }else{ sqlite3_free(pBuf); } fclose(in); } /* ** Implementation of the "readtextfile(X)" SQL function. The text content ** of the file named X through the end of the file or to the first \000 ** character, whichever comes first, is read and returned as TEXT. NULL ** is returned if the file does not exist or is unreadable. */ static void readtextfileFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zName; FILE *in; long nIn; char *pBuf; zName = (const char*)sqlite3_value_text(argv[0]); if( zName==0 ) return; in = fopen(zName, "rb"); if( in==0 ) return; fseek(in, 0, SEEK_END); nIn = ftell(in); rewind(in); pBuf = sqlite3_malloc64( nIn+1 ); if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ pBuf[nIn] = 0; sqlite3_result_text(context, pBuf, -1, sqlite3_free); }else{ sqlite3_free(pBuf); } fclose(in); } /* ** Implementation of the "writefile(X,Y)" SQL function. The argument Y ** is written into file X. The number of bytes written is returned. Or ** NULL is returned if something goes wrong, such as being unable to open ** file X for writing. */ static void writefileFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ FILE *out; const char *z; sqlite3_int64 rc; const char *zFile; (void)argc; zFile = (const char*)sqlite3_value_text(argv[0]); if( zFile==0 ) return; out = fopen(zFile, "wb"); if( out==0 ) return; z = (const char*)sqlite3_value_blob(argv[1]); if( z==0 ){ rc = 0; }else{ rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out); } fclose(out); sqlite3_result_int64(context, rc); } /* ** Load a list of Blob objects from the database */ static void blobListLoadFromDb( sqlite3 *db, /* Read from this database */ const char *zSql, /* Query used to extract the blobs */ int onlyId, /* Only load where id is this value */ int *pN, /* OUT: Write number of blobs loaded here */ Blob **ppList /* OUT: Write the head of the blob list here */ ){ Blob head; Blob *p; sqlite3_stmt *pStmt; int n = 0; int rc; char *z2; if( onlyId>0 ){ z2 = sqlite3_mprintf("%s WHERE rowid=%d", zSql, onlyId); }else{ z2 = sqlite3_mprintf("%s", zSql); } rc = sqlite3_prepare_v2(db, z2, -1, &pStmt, 0); sqlite3_free(z2); if( rc ) fatalError("%s", sqlite3_errmsg(db)); head.pNext = 0; p = &head; while( SQLITE_ROW==sqlite3_step(pStmt) ){ int sz = sqlite3_column_bytes(pStmt, 1); Blob *pNew = safe_realloc(0, sizeof(*pNew)+sz ); pNew->id = sqlite3_column_int(pStmt, 0); pNew->sz = sz; pNew->seq = n++; pNew->pNext = 0; memcpy(pNew->a, sqlite3_column_blob(pStmt,1), sz); pNew->a[sz] = 0; p->pNext = pNew; p = pNew; } sqlite3_finalize(pStmt); *pN = n; *ppList = head.pNext; } /* ** Free a list of Blob objects */ static void blobListFree(Blob *p){ Blob *pNext; while( p ){ pNext = p->pNext; free(p); p = pNext; } } /* Return the current wall-clock time */ static sqlite3_int64 timeOfDay(void){ static sqlite3_vfs *clockVfs = 0; sqlite3_int64 t; if( clockVfs==0 ){ clockVfs = sqlite3_vfs_find(0); if( clockVfs==0 ) return 0; } if( clockVfs->iVersion>=1 && clockVfs->xCurrentTimeInt64!=0 ){ clockVfs->xCurrentTimeInt64(clockVfs, &t); }else{ double r; clockVfs->xCurrentTime(clockVfs, &r); t = (sqlite3_int64)(r*86400000.0); } return t; } /*************************************************************************** ** Code to process combined database+SQL scripts generated by the ** dbsqlfuzz fuzzer. */ /* An instance of the following object is passed by pointer as the ** client data to various callbacks. */ typedef struct FuzzCtx { sqlite3 *db; /* The database connection */ sqlite3_int64 iCutoffTime; /* Stop processing at this time. */ sqlite3_int64 iLastCb; /* Time recorded for previous progress callback */ sqlite3_int64 mxInterval; /* Longest interval between two progress calls */ unsigned nCb; /* Number of progress callbacks */ unsigned mxCb; /* Maximum number of progress callbacks allowed */ unsigned execCnt; /* Number of calls to the sqlite3_exec callback */ int timeoutHit; /* True when reaching a timeout */ } FuzzCtx; /* Verbosity level for the dbsqlfuzz test runner */ static int eVerbosity = 0; /* True to activate PRAGMA vdbe_debug=on */ static int bVdbeDebug = 0; /* Timeout for each fuzzing attempt, in milliseconds */ static int giTimeout = 10000; /* Defaults to 10 seconds */ /* Maximum number of progress handler callbacks */ static unsigned int mxProgressCb = 2000; /* Maximum string length in SQLite */ static int lengthLimit = 1000000; /* Maximum expression depth */ static int depthLimit = 500; /* Limit on the amount of heap memory that can be used */ static sqlite3_int64 heapLimit = 100000000; /* Maximum byte-code program length in SQLite */ static int vdbeOpLimit = 25000; /* Maximum size of the in-memory database */ static sqlite3_int64 maxDbSize = 104857600; /* OOM simulation parameters */ static unsigned int oomCounter = 0; /* Simulate OOM when equals 1 */ static unsigned int oomRepeat = 0; /* Number of OOMs in a row */ static void*(*defaultMalloc)(int) = 0; /* The low-level malloc routine */ /* This routine is called when a simulated OOM occurs. It is broken ** out as a separate routine to make it easy to set a breakpoint on ** the OOM */ void oomFault(void){ if( eVerbosity ){ printf("Simulated OOM fault\n"); } if( oomRepeat>0 ){ oomRepeat--; }else{ oomCounter--; } } /* This routine is a replacement malloc() that is used to simulate ** Out-Of-Memory (OOM) errors for testing purposes. */ static void *oomMalloc(int nByte){ if( oomCounter ){ if( oomCounter==1 ){ oomFault(); return 0; }else{ oomCounter--; } } return defaultMalloc(nByte); } /* Register the OOM simulator. This must occur before any memory ** allocations */ static void registerOomSimulator(void){ sqlite3_mem_methods mem; sqlite3_shutdown(); sqlite3_config(SQLITE_CONFIG_GETMALLOC, &mem); defaultMalloc = mem.xMalloc; mem.xMalloc = oomMalloc; sqlite3_config(SQLITE_CONFIG_MALLOC, &mem); } /* Turn off any pending OOM simulation */ static void disableOom(void){ oomCounter = 0; oomRepeat = 0; } /* ** Translate a single byte of Hex into an integer. ** This routine only works if h really is a valid hexadecimal ** character: 0..9a..fA..F */ static unsigned char hexToInt(unsigned int h){ #ifdef SQLITE_EBCDIC h += 9*(1&~(h>>4)); /* EBCDIC */ #else h += 9*(1&(h>>6)); /* ASCII */ #endif return h & 0xf; } /* ** The first character of buffer zIn[0..nIn-1] is a '['. This routine ** checked to see if the buffer holds "[NNNN]" or "[+NNNN]" and if it ** does it makes corresponding changes to the *pK value and *pI value ** and returns true. If the input buffer does not match the patterns, ** no changes are made to either *pK or *pI and this routine returns false. */ static int isOffset( const unsigned char *zIn, /* Text input */ int nIn, /* Bytes of input */ unsigned int *pK, /* half-byte cursor to adjust */ unsigned int *pI /* Input index to adjust */ ){ int i; unsigned int k = 0; unsigned char c; for(i=1; i=nAlloc ){ sqlite3_uint64 newSize; if( nAlloc==MX_FILE_SZ || j>=MX_FILE_SZ ){ if( eVerbosity ){ fprintf(stderr, "Input database too big: max %d bytes\n", MX_FILE_SZ); } sqlite3_free(a); return -1; } newSize = nAlloc*2; if( newSize<=j ){ newSize = (j+4096)&~4095; } if( newSize>MX_FILE_SZ ){ if( j>=MX_FILE_SZ ){ sqlite3_free(a); return -1; } newSize = MX_FILE_SZ; } aNew = sqlite3_realloc64( a, newSize ); if( aNew==0 ){ sqlite3_free(a); return -1; } a = aNew; assert( newSize > nAlloc ); memset(a+nAlloc, 0, (size_t)(newSize - nAlloc)); nAlloc = newSize; } if( j>=(unsigned)mx ){ mx = (j + 4095)&~4095; if( mx>MX_FILE_SZ ) mx = MX_FILE_SZ; } assert( j=p->iCutoffTime; sqlite3_int64 iDiff = iNow - p->iLastCb; if( iDiff > p->mxInterval ) p->mxInterval = iDiff; p->nCb++; if( rc==0 && p->mxCb>0 && p->mxCb<=p->nCb ) rc = 1; if( rc && !p->timeoutHit && eVerbosity>=2 ){ printf("Timeout on progress callback %d\n", p->nCb); fflush(stdout); p->timeoutHit = 1; } return rc; } /* ** Disallow debugging pragmas such as "PRAGMA vdbe_debug" and ** "PRAGMA parser_trace" since they can dramatically increase the ** amount of output without actually testing anything useful. ** ** Also block ATTACH and DETACH */ static int block_troublesome_sql( void *Notused, int eCode, const char *zArg1, const char *zArg2, const char *zArg3, const char *zArg4 ){ (void)Notused; (void)zArg2; (void)zArg3; (void)zArg4; if( eCode==SQLITE_PRAGMA ){ if( sqlite3_strnicmp("vdbe_", zArg1, 5)==0 || sqlite3_stricmp("parser_trace", zArg1)==0 || sqlite3_stricmp("temp_store_directory", zArg1)==0 ){ return SQLITE_DENY; } if( sqlite3_stricmp("oom",zArg1)==0 && zArg2!=0 && zArg2[0]!=0 ){ oomCounter = atoi(zArg2); } }else if( (eCode==SQLITE_ATTACH || eCode==SQLITE_DETACH) && zArg1 && zArg1[0] ){ return SQLITE_DENY; } return SQLITE_OK; } /* ** Run the SQL text */ static int runDbSql(sqlite3 *db, const char *zSql){ int rc; sqlite3_stmt *pStmt; while( isspace(zSql[0]&0x7f) ) zSql++; if( zSql[0]==0 ) return SQLITE_OK; if( eVerbosity>=4 ){ printf("RUNNING-SQL: [%s]\n", zSql); fflush(stdout); } rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); if( rc==SQLITE_OK ){ while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){ if( eVerbosity>=5 ){ int j; for(j=0; j=5 ) */ } /* End while( SQLITE_ROW */ if( rc!=SQLITE_DONE && eVerbosity>=4 ){ printf("SQL-ERROR: (%d) %s\n", rc, sqlite3_errmsg(db)); fflush(stdout); } }else if( eVerbosity>=4 ){ printf("SQL-ERROR (%d): %s\n", rc, sqlite3_errmsg(db)); fflush(stdout); } /* End if( SQLITE_OK ) */ return sqlite3_finalize(pStmt); } /* Invoke this routine to run a single test case */ int runCombinedDbSqlInput(const uint8_t *aData, size_t nByte){ int rc; /* SQLite API return value */ int iSql; /* Index in aData[] of start of SQL */ unsigned char *aDb = 0; /* Decoded database content */ int nDb = 0; /* Size of the decoded database */ int i; /* Loop counter */ int j; /* Start of current SQL statement */ char *zSql = 0; /* SQL text to run */ int nSql; /* Bytes of SQL text */ FuzzCtx cx; /* Fuzzing context */ if( nByte<10 ) return 0; if( sqlite3_initialize() ) return 0; if( sqlite3_memory_used()!=0 ){ int nAlloc = 0; int nNotUsed = 0; sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &nAlloc, &nNotUsed, 0); fprintf(stderr,"memory leak prior to test start:" " %lld bytes in %d allocations\n", sqlite3_memory_used(), nAlloc); exit(1); } memset(&cx, 0, sizeof(cx)); iSql = decodeDatabase((unsigned char*)aData, (int)nByte, &aDb, &nDb); if( iSql<0 ) return 0; nSql = (int)(nByte - iSql); if( eVerbosity>=3 ){ printf( "****** %d-byte input, %d-byte database, %d-byte script " "******\n", (int)nByte, nDb, nSql); fflush(stdout); } rc = sqlite3_open(0, &cx.db); if( rc ){ sqlite3_free(aDb); return 1; } if( bVdbeDebug ){ sqlite3_exec(cx.db, "PRAGMA vdbe_debug=ON", 0, 0, 0); } /* Invoke the progress handler frequently to check to see if we ** are taking too long. The progress handler will return true ** (which will block further processing) if more than giTimeout seconds have ** elapsed since the start of the test. */ cx.iLastCb = timeOfDay(); cx.iCutoffTime = cx.iLastCb + giTimeout; /* Now + giTimeout seconds */ cx.mxCb = mxProgressCb; #ifndef SQLITE_OMIT_PROGRESS_CALLBACK sqlite3_progress_handler(cx.db, 10, progress_handler, (void*)&cx); #endif /* Set a limit on the maximum size of a prepared statement, and the ** maximum length of a string or blob */ if( vdbeOpLimit>0 ){ sqlite3_limit(cx.db, SQLITE_LIMIT_VDBE_OP, vdbeOpLimit); } if( lengthLimit>0 ){ sqlite3_limit(cx.db, SQLITE_LIMIT_LENGTH, lengthLimit); } if( depthLimit>0 ){ sqlite3_limit(cx.db, SQLITE_LIMIT_EXPR_DEPTH, depthLimit); } sqlite3_limit(cx.db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 100); sqlite3_hard_heap_limit64(heapLimit); if( nDb>=20 && aDb[18]==2 && aDb[19]==2 ){ aDb[18] = aDb[19] = 1; } rc = sqlite3_deserialize(cx.db, "main", aDb, nDb, nDb, SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE); if( rc ){ fprintf(stderr, "sqlite3_deserialize() failed with %d\n", rc); goto testrun_finished; } if( maxDbSize>0 ){ sqlite3_int64 x = maxDbSize; sqlite3_file_control(cx.db, "main", SQLITE_FCNTL_SIZE_LIMIT, &x); } /* For high debugging levels, turn on debug mode */ if( eVerbosity>=5 ){ sqlite3_exec(cx.db, "PRAGMA vdbe_debug=ON;", 0, 0, 0); } /* Block debug pragmas and ATTACH/DETACH. But wait until after ** deserialize to do this because deserialize depends on ATTACH */ sqlite3_set_authorizer(cx.db, block_troublesome_sql, 0); /* Consistent PRNG seed */ sqlite3_randomness(0,0); zSql = sqlite3_malloc( nSql + 1 ); if( zSql==0 ){ fprintf(stderr, "Out of memory!\n"); }else{ memcpy(zSql, aData+iSql, nSql); zSql[nSql] = 0; for(i=j=0; zSql[i]; i++){ if( zSql[i]==';' ){ char cSaved = zSql[i+1]; zSql[i+1] = 0; if( sqlite3_complete(zSql+j) ){ rc = runDbSql(cx.db, zSql+j); j = i+1; } zSql[i+1] = cSaved; if( rc==SQLITE_INTERRUPT || progress_handler(&cx) ){ goto testrun_finished; } } } if( j=2 ){ fprintf(stdout, "Peak memory usages: %f MB\n", sqlite3_memory_highwater(1) / 1000000.0); } if( sqlite3_memory_used()!=0 ){ int nAlloc = 0; int nNotUsed = 0; sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &nAlloc, &nNotUsed, 0); fprintf(stderr,"Memory leak: %lld bytes in %d allocations\n", sqlite3_memory_used(), nAlloc); exit(1); } return 0; } /* ** END of the dbsqlfuzz code ***************************************************************************/ /* Look at a SQL text and try to determine if it begins with a database ** description, such as would be found in a dbsqlfuzz test case. Return ** true if this does appear to be a dbsqlfuzz test case and false otherwise. */ static int isDbSql(unsigned char *a, int n){ unsigned char buf[12]; int i; if( n>4 && memcmp(a,"\n--\n",4)==0 ) return 1; while( n>0 && isspace(a[0]) ){ a++; n--; } for(i=0; n>0 && i<8; n--, a++){ if( isxdigit(a[0]) ) buf[i++] = a[0]; } if( i==8 && memcmp(buf,"53514c69",8)==0 ) return 1; return 0; } /* Implementation of the isdbsql(TEXT) SQL function. */ static void isDbSqlFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ int n = sqlite3_value_bytes(argv[0]); unsigned char *a = (unsigned char*)sqlite3_value_blob(argv[0]); sqlite3_result_int(context, a!=0 && n>0 && isDbSql(a,n)); } /* Methods for the VHandle object */ static int inmemClose(sqlite3_file *pFile){ VHandle *p = (VHandle*)pFile; VFile *pVFile = p->pVFile; pVFile->nRef--; if( pVFile->nRef==0 && pVFile->zFilename==0 ){ pVFile->sz = -1; free(pVFile->a); pVFile->a = 0; } return SQLITE_OK; } static int inmemRead( sqlite3_file *pFile, /* Read from this open file */ void *pData, /* Store content in this buffer */ int iAmt, /* Bytes of content */ sqlite3_int64 iOfst /* Start reading here */ ){ VHandle *pHandle = (VHandle*)pFile; VFile *pVFile = pHandle->pVFile; if( iOfst<0 || iOfst>=pVFile->sz ){ memset(pData, 0, iAmt); return SQLITE_IOERR_SHORT_READ; } if( iOfst+iAmt>pVFile->sz ){ memset(pData, 0, iAmt); iAmt = (int)(pVFile->sz - iOfst); memcpy(pData, pVFile->a + iOfst, iAmt); return SQLITE_IOERR_SHORT_READ; } memcpy(pData, pVFile->a + iOfst, iAmt); return SQLITE_OK; } static int inmemWrite( sqlite3_file *pFile, /* Write to this file */ const void *pData, /* Content to write */ int iAmt, /* bytes to write */ sqlite3_int64 iOfst /* Start writing here */ ){ VHandle *pHandle = (VHandle*)pFile; VFile *pVFile = pHandle->pVFile; if( iOfst+iAmt > pVFile->sz ){ if( iOfst+iAmt >= MX_FILE_SZ ){ return SQLITE_FULL; } pVFile->a = safe_realloc(pVFile->a, (int)(iOfst+iAmt)); if( iOfst > pVFile->sz ){ memset(pVFile->a + pVFile->sz, 0, (int)(iOfst - pVFile->sz)); } pVFile->sz = (int)(iOfst + iAmt); } memcpy(pVFile->a + iOfst, pData, iAmt); return SQLITE_OK; } static int inmemTruncate(sqlite3_file *pFile, sqlite3_int64 iSize){ VHandle *pHandle = (VHandle*)pFile; VFile *pVFile = pHandle->pVFile; if( pVFile->sz>iSize && iSize>=0 ) pVFile->sz = (int)iSize; return SQLITE_OK; } static int inmemSync(sqlite3_file *pFile, int flags){ return SQLITE_OK; } static int inmemFileSize(sqlite3_file *pFile, sqlite3_int64 *pSize){ *pSize = ((VHandle*)pFile)->pVFile->sz; return SQLITE_OK; } static int inmemLock(sqlite3_file *pFile, int type){ return SQLITE_OK; } static int inmemUnlock(sqlite3_file *pFile, int type){ return SQLITE_OK; } static int inmemCheckReservedLock(sqlite3_file *pFile, int *pOut){ *pOut = 0; return SQLITE_OK; } static int inmemFileControl(sqlite3_file *pFile, int op, void *pArg){ return SQLITE_NOTFOUND; } static int inmemSectorSize(sqlite3_file *pFile){ return 512; } static int inmemDeviceCharacteristics(sqlite3_file *pFile){ return SQLITE_IOCAP_SAFE_APPEND | SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | SQLITE_IOCAP_POWERSAFE_OVERWRITE; } /* Method table for VHandle */ static sqlite3_io_methods VHandleMethods = { /* iVersion */ 1, /* xClose */ inmemClose, /* xRead */ inmemRead, /* xWrite */ inmemWrite, /* xTruncate */ inmemTruncate, /* xSync */ inmemSync, /* xFileSize */ inmemFileSize, /* xLock */ inmemLock, /* xUnlock */ inmemUnlock, /* xCheck... */ inmemCheckReservedLock, /* xFileCtrl */ inmemFileControl, /* xSectorSz */ inmemSectorSize, /* xDevchar */ inmemDeviceCharacteristics, /* xShmMap */ 0, /* xShmLock */ 0, /* xShmBarrier */ 0, /* xShmUnmap */ 0, /* xFetch */ 0, /* xUnfetch */ 0 }; /* ** Open a new file in the inmem VFS. All files are anonymous and are ** delete-on-close. */ static int inmemOpen( sqlite3_vfs *pVfs, const char *zFilename, sqlite3_file *pFile, int openFlags, int *pOutFlags ){ VFile *pVFile = createVFile(zFilename, 0, (unsigned char*)""); VHandle *pHandle = (VHandle*)pFile; if( pVFile==0 ){ return SQLITE_FULL; } pHandle->pVFile = pVFile; pVFile->nRef++; pFile->pMethods = &VHandleMethods; if( pOutFlags ) *pOutFlags = openFlags; return SQLITE_OK; } /* ** Delete a file by name */ static int inmemDelete( sqlite3_vfs *pVfs, const char *zFilename, int syncdir ){ VFile *pVFile = findVFile(zFilename); if( pVFile==0 ) return SQLITE_OK; if( pVFile->nRef==0 ){ free(pVFile->zFilename); pVFile->zFilename = 0; pVFile->sz = -1; free(pVFile->a); pVFile->a = 0; return SQLITE_OK; } return SQLITE_IOERR_DELETE; } /* Check for the existance of a file */ static int inmemAccess( sqlite3_vfs *pVfs, const char *zFilename, int flags, int *pResOut ){ VFile *pVFile = findVFile(zFilename); *pResOut = pVFile!=0; return SQLITE_OK; } /* Get the canonical pathname for a file */ static int inmemFullPathname( sqlite3_vfs *pVfs, const char *zFilename, int nOut, char *zOut ){ sqlite3_snprintf(nOut, zOut, "%s", zFilename); return SQLITE_OK; } /* Always use the same random see, for repeatability. */ static int inmemRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ memset(zBuf, 0, nBuf); memcpy(zBuf, &g.uRandom, nBufxSleep; inmemVfs.xCurrentTimeInt64 = pDefault->xCurrentTimeInt64; sqlite3_vfs_register(&inmemVfs, makeDefault); }; /* ** Allowed values for the runFlags parameter to runSql() */ #define SQL_TRACE 0x0001 /* Print each SQL statement as it is prepared */ #define SQL_OUTPUT 0x0002 /* Show the SQL output */ /* ** Run multiple commands of SQL. Similar to sqlite3_exec(), but does not ** stop if an error is encountered. */ static void runSql(sqlite3 *db, const char *zSql, unsigned runFlags){ const char *zMore; sqlite3_stmt *pStmt; while( zSql && zSql[0] ){ zMore = 0; pStmt = 0; sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zMore); if( zMore==zSql ) break; if( runFlags & SQL_TRACE ){ const char *z = zSql; int n; while( z0 && ISSPACE(z[n-1]) ) n--; if( n==0 ) break; if( pStmt==0 ){ printf("TRACE: %.*s (error: %s)\n", n, z, sqlite3_errmsg(db)); }else{ printf("TRACE: %.*s\n", n, z); } } zSql = zMore; if( pStmt ){ if( (runFlags & SQL_OUTPUT)==0 ){ while( SQLITE_ROW==sqlite3_step(pStmt) ){} }else{ int nCol = -1; while( SQLITE_ROW==sqlite3_step(pStmt) ){ int i; if( nCol<0 ){ nCol = sqlite3_column_count(pStmt); }else if( nCol>0 ){ printf("--------------------------------------------\n"); } for(i=0; i='0' && c<='9' ) return c - '0'; if( c>='a' && c<='f' ) return c - 'a' + 10; if( c>='A' && c<='F' ) return c - 'A' + 10; return -1; } /* ** Interpret zArg as an integer value, possibly with suffixes. */ static int integerValue(const char *zArg){ sqlite3_int64 v = 0; static const struct { char *zSuffix; int iMult; } aMult[] = { { "KiB", 1024 }, { "MiB", 1024*1024 }, { "GiB", 1024*1024*1024 }, { "KB", 1000 }, { "MB", 1000000 }, { "GB", 1000000000 }, { "K", 1000 }, { "M", 1000000 }, { "G", 1000000000 }, }; int i; int isNeg = 0; if( zArg[0]=='-' ){ isNeg = 1; zArg++; }else if( zArg[0]=='+' ){ zArg++; } if( zArg[0]=='0' && zArg[1]=='x' ){ int x; zArg += 2; while( (x = hexDigitValue(zArg[0]))>=0 ){ v = (v<<4) + x; zArg++; } }else{ while( ISDIGIT(zArg[0]) ){ v = v*10 + zArg[0] - '0'; zArg++; } } for(i=0; i0x7fffffff ) fatalError("parameter too large - max 2147483648"); return (int)(isNeg? -v : v); } /* ** Return the number of "v" characters in a string. Return 0 if there ** are any characters in the string other than "v". */ static int numberOfVChar(const char *z){ int N = 0; while( z[0] && z[0]=='v' ){ z++; N++; } return z[0]==0 ? N : 0; } /* ** Print sketchy documentation for this utility program */ static void showHelp(void){ printf("Usage: %s [options] SOURCE-DB ?ARGS...?\n", g.zArgv0); printf( "Read databases and SQL scripts from SOURCE-DB and execute each script against\n" "each database, checking for crashes and memory leaks.\n" "Options:\n" " --cell-size-check Set the PRAGMA cell_size_check=ON\n" " --dbid N Use only the database where dbid=N\n" " --export-db DIR Write databases to files(s) in DIR. Works with --dbid\n" " --export-sql DIR Write SQL to file(s) in DIR. Also works with --sqlid\n" " --help Show this help text\n" " --info Show information about SOURCE-DB w/o running tests\n" " --limit-depth N Limit expression depth to N. Default: 500\n" " --limit-heap N Limit heap memory to N. Default: 100M\n" " --limit-mem N Limit memory used by test SQLite instance to N bytes\n" " --limit-vdbe Panic if any test runs for more than 100,000 cycles\n" " --load-sql ARGS... Load SQL scripts fron files into SOURCE-DB\n" " --load-db ARGS... Load template databases from files into SOURCE_DB\n" " --load-dbsql ARGS.. Load dbsqlfuzz outputs into the xsql table\n" " -m TEXT Add a description to the database\n" " --native-vfs Use the native VFS for initially empty database files\n" " --native-malloc Turn off MEMSYS3/5 and Lookaside\n" " --oss-fuzz Enable OSS-FUZZ testing\n" " --prng-seed N Seed value for the PRGN inside of SQLite\n" " -q|--quiet Reduced output\n" " --rebuild Rebuild and vacuum the database file\n" " --result-trace Show the results of each SQL command\n" " --skip N Skip the first N test cases\n" " --spinner Use a spinner to show progress\n" " --sqlid N Use only SQL where sqlid=N\n" " --timeout N Abort if any single test needs more than N seconds\n" " -v|--verbose Increased output. Repeat for more output.\n" " --vdbe-debug Activate VDBE debugging.\n" ); } int main(int argc, char **argv){ sqlite3_int64 iBegin; /* Start time of this program */ int quietFlag = 0; /* True if --quiet or -q */ int verboseFlag = 0; /* True if --verbose or -v */ char *zInsSql = 0; /* SQL statement for --load-db or --load-sql */ int iFirstInsArg = 0; /* First argv[] for --load-db or --load-sql */ sqlite3 *db = 0; /* The open database connection */ sqlite3_stmt *pStmt; /* A prepared statement */ int rc; /* Result code from SQLite interface calls */ Blob *pSql; /* For looping over SQL scripts */ Blob *pDb; /* For looping over template databases */ int i; /* Loop index for the argv[] loop */ int dbSqlOnly = 0; /* Only use scripts that are dbsqlfuzz */ int onlySqlid = -1; /* --sqlid */ int onlyDbid = -1; /* --dbid */ int nativeFlag = 0; /* --native-vfs */ int rebuildFlag = 0; /* --rebuild */ int vdbeLimitFlag = 0; /* --limit-vdbe */ int infoFlag = 0; /* --info */ int nSkip = 0; /* --skip */ int bSpinner = 0; /* True for --spinner */ int timeoutTest = 0; /* undocumented --timeout-test flag */ int runFlags = 0; /* Flags sent to runSql() */ char *zMsg = 0; /* Add this message */ int nSrcDb = 0; /* Number of source databases */ char **azSrcDb = 0; /* Array of source database names */ int iSrcDb; /* Loop over all source databases */ int nTest = 0; /* Total number of tests performed */ char *zDbName = ""; /* Appreviated name of a source database */ const char *zFailCode = 0; /* Value of the TEST_FAILURE env variable */ int cellSzCkFlag = 0; /* --cell-size-check */ int sqlFuzz = 0; /* True for SQL fuzz. False for DB fuzz */ int iTimeout = 120; /* Default 120-second timeout */ int nMem = 0; /* Memory limit override */ int nMemThisDb = 0; /* Memory limit set by the CONFIG table */ char *zExpDb = 0; /* Write Databases to files in this directory */ char *zExpSql = 0; /* Write SQL to files in this directory */ void *pHeap = 0; /* Heap for use by SQLite */ int ossFuzz = 0; /* enable OSS-FUZZ testing */ int ossFuzzThisDb = 0; /* ossFuzz value for this particular database */ int nativeMalloc = 0; /* Turn off MEMSYS3/5 and lookaside if true */ sqlite3_vfs *pDfltVfs; /* The default VFS */ int openFlags4Data; /* Flags for sqlite3_open_v2() */ int nV; /* How much to increase verbosity with -vvvv */ registerOomSimulator(); sqlite3_initialize(); iBegin = timeOfDay(); #ifdef __unix__ signal(SIGALRM, signalHandler); signal(SIGSEGV, signalHandler); signal(SIGABRT, signalHandler); #endif g.zArgv0 = argv[0]; openFlags4Data = SQLITE_OPEN_READONLY; zFailCode = getenv("TEST_FAILURE"); pDfltVfs = sqlite3_vfs_find(0); inmemVfsRegister(1); for(i=1; i=argc-1 ) fatalError("missing arguments on %s", argv[i]); onlyDbid = integerValue(argv[++i]); }else if( strcmp(z,"export-db")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); zExpDb = argv[++i]; }else if( strcmp(z,"export-sql")==0 || strcmp(z,"export-dbsql")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); zExpSql = argv[++i]; }else if( strcmp(z,"help")==0 ){ showHelp(); return 0; }else if( strcmp(z,"info")==0 ){ infoFlag = 1; }else if( strcmp(z,"limit-depth")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); depthLimit = integerValue(argv[++i]); }else if( strcmp(z,"limit-heap")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); heapLimit = integerValue(argv[++i]); }else if( strcmp(z,"limit-mem")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); nMem = integerValue(argv[++i]); }else if( strcmp(z,"limit-vdbe")==0 ){ vdbeLimitFlag = 1; }else if( strcmp(z,"load-sql")==0 ){ zInsSql = "INSERT INTO xsql(sqltext)" "VALUES(CAST(readtextfile(?1) AS text))"; iFirstInsArg = i+1; openFlags4Data = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; break; }else if( strcmp(z,"load-db")==0 ){ zInsSql = "INSERT INTO db(dbcontent) VALUES(readfile(?1))"; iFirstInsArg = i+1; openFlags4Data = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; break; }else if( strcmp(z,"load-dbsql")==0 ){ zInsSql = "INSERT INTO xsql(sqltext)" "VALUES(CAST(readtextfile(?1) AS text))"; iFirstInsArg = i+1; openFlags4Data = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; dbSqlOnly = 1; break; }else if( strcmp(z,"m")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); zMsg = argv[++i]; openFlags4Data = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; }else if( strcmp(z,"native-malloc")==0 ){ nativeMalloc = 1; }else if( strcmp(z,"native-vfs")==0 ){ nativeFlag = 1; }else if( strcmp(z,"oss-fuzz")==0 ){ ossFuzz = 1; }else if( strcmp(z,"prng-seed")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); g.uRandom = atoi(argv[++i]); }else if( strcmp(z,"quiet")==0 || strcmp(z,"q")==0 ){ quietFlag = 1; verboseFlag = 0; eVerbosity = 0; }else if( strcmp(z,"rebuild")==0 ){ rebuildFlag = 1; openFlags4Data = SQLITE_OPEN_READWRITE; }else if( strcmp(z,"result-trace")==0 ){ runFlags |= SQL_OUTPUT; }else if( strcmp(z,"skip")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); nSkip = atoi(argv[++i]); }else if( strcmp(z,"spinner")==0 ){ bSpinner = 1; }else if( strcmp(z,"sqlid")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); onlySqlid = integerValue(argv[++i]); }else if( strcmp(z,"timeout")==0 ){ if( i>=argc-1 ) fatalError("missing arguments on %s", argv[i]); iTimeout = integerValue(argv[++i]); }else if( strcmp(z,"timeout-test")==0 ){ timeoutTest = 1; #ifndef __unix__ fatalError("timeout is not available on non-unix systems"); #endif }else if( strcmp(z,"vdbe-debug")==0 ){ bVdbeDebug = 1; }else if( strcmp(z,"verbose")==0 ){ quietFlag = 0; verboseFlag++; eVerbosity++; if( verboseFlag>1 ) runFlags |= SQL_TRACE; }else if( (nV = numberOfVChar(z))>=1 ){ quietFlag = 0; verboseFlag += nV; eVerbosity += nV; if( verboseFlag>1 ) runFlags |= SQL_TRACE; }else if( strcmp(z,"version")==0 ){ int ii; const char *zz; printf("SQLite %s %s\n", sqlite3_libversion(), sqlite3_sourceid()); for(ii=0; (zz = sqlite3_compileoption_get(ii))!=0; ii++){ printf("%s\n", zz); } return 0; }else { fatalError("unknown option: %s", argv[i]); } }else{ nSrcDb++; azSrcDb = safe_realloc(azSrcDb, nSrcDb*sizeof(azSrcDb[0])); azSrcDb[nSrcDb-1] = argv[i]; } } if( nSrcDb==0 ) fatalError("no source database specified"); if( nSrcDb>1 ){ if( zMsg ){ fatalError("cannot change the description of more than one database"); } if( zInsSql ){ fatalError("cannot import into more than one database"); } } /* Process each source database separately */ for(iSrcDb=0; iSrcDbzName); if( rc ){ fatalError("cannot open source database %s - %s", azSrcDb[iSrcDb], sqlite3_errmsg(db)); } /* Print the description, if there is one */ if( infoFlag ){ int n; zDbName = azSrcDb[iSrcDb]; i = (int)strlen(zDbName) - 1; while( i>0 && zDbName[i-1]!='/' && zDbName[i-1]!='\\' ){ i--; } zDbName += i; sqlite3_prepare_v2(db, "SELECT msg FROM readme", -1, &pStmt, 0); if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ printf("%s: %s", zDbName, sqlite3_column_text(pStmt,0)); }else{ printf("%s: (empty \"readme\")", zDbName); } sqlite3_finalize(pStmt); sqlite3_prepare_v2(db, "SELECT count(*) FROM db", -1, &pStmt, 0); if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW && (n = sqlite3_column_int(pStmt,0))>0 ){ printf(" - %d DBs", n); } sqlite3_finalize(pStmt); sqlite3_prepare_v2(db, "SELECT count(*) FROM xsql", -1, &pStmt, 0); if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW && (n = sqlite3_column_int(pStmt,0))>0 ){ printf(" - %d scripts", n); } sqlite3_finalize(pStmt); printf("\n"); sqlite3_close(db); continue; } rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS db(\n" " dbid INTEGER PRIMARY KEY, -- database id\n" " dbcontent BLOB -- database disk file image\n" ");\n" "CREATE TABLE IF NOT EXISTS xsql(\n" " sqlid INTEGER PRIMARY KEY, -- SQL script id\n" " sqltext TEXT -- Text of SQL statements to run\n" ");" "CREATE TABLE IF NOT EXISTS readme(\n" " msg TEXT -- Human-readable description of this file\n" ");", 0, 0, 0); if( rc ) fatalError("cannot create schema: %s", sqlite3_errmsg(db)); if( zMsg ){ char *zSql; zSql = sqlite3_mprintf( "DELETE FROM readme; INSERT INTO readme(msg) VALUES(%Q)", zMsg); rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); if( rc ) fatalError("cannot change description: %s", sqlite3_errmsg(db)); } ossFuzzThisDb = ossFuzz; /* If the CONFIG(name,value) table exists, read db-specific settings ** from that table */ if( sqlite3_table_column_metadata(db,0,"config",0,0,0,0,0,0)==SQLITE_OK ){ rc = sqlite3_prepare_v2(db, "SELECT name, value FROM config", -1, &pStmt, 0); if( rc ) fatalError("cannot prepare query of CONFIG table: %s", sqlite3_errmsg(db)); while( SQLITE_ROW==sqlite3_step(pStmt) ){ const char *zName = (const char *)sqlite3_column_text(pStmt,0); if( zName==0 ) continue; if( strcmp(zName, "oss-fuzz")==0 ){ ossFuzzThisDb = sqlite3_column_int(pStmt,1); if( verboseFlag ) printf("Config: oss-fuzz=%d\n", ossFuzzThisDb); } if( strcmp(zName, "limit-mem")==0 ){ nMemThisDb = sqlite3_column_int(pStmt,1); if( verboseFlag ) printf("Config: limit-mem=%d\n", nMemThisDb); } } sqlite3_finalize(pStmt); } if( zInsSql ){ sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0, readfileFunc, 0, 0); sqlite3_create_function(db, "readtextfile", 1, SQLITE_UTF8, 0, readtextfileFunc, 0, 0); sqlite3_create_function(db, "isdbsql", 1, SQLITE_UTF8, 0, isDbSqlFunc, 0, 0); rc = sqlite3_prepare_v2(db, zInsSql, -1, &pStmt, 0); if( rc ) fatalError("cannot prepare statement [%s]: %s", zInsSql, sqlite3_errmsg(db)); rc = sqlite3_exec(db, "BEGIN", 0, 0, 0); if( rc ) fatalError("cannot start a transaction"); for(i=iFirstInsArg; iid = 1; g.pFirstDb->seq = 0; g.nDb = 1; sqlFuzz = 1; } /* Print the description, if there is one */ if( !quietFlag ){ zDbName = azSrcDb[iSrcDb]; i = (int)strlen(zDbName) - 1; while( i>0 && zDbName[i-1]!='/' && zDbName[i-1]!='\\' ){ i--; } zDbName += i; sqlite3_prepare_v2(db, "SELECT msg FROM readme", -1, &pStmt, 0); if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ printf("%s: %s\n", zDbName, sqlite3_column_text(pStmt,0)); } sqlite3_finalize(pStmt); } /* Rebuild the database, if requested */ if( rebuildFlag ){ if( !quietFlag ){ printf("%s: rebuilding... ", zDbName); fflush(stdout); } rebuild_database(db, 0); if( !quietFlag ) printf("done\n"); } /* Close the source database. Verify that no SQLite memory allocations are ** outstanding. */ sqlite3_close(db); if( sqlite3_memory_used()>0 ){ fatalError("SQLite has memory in use before the start of testing"); } /* Limit available memory, if requested */ sqlite3_shutdown(); if( nMemThisDb>0 && nMem==0 ){ if( !nativeMalloc ){ pHeap = realloc(pHeap, nMemThisDb); if( pHeap==0 ){ fatalError("failed to allocate %d bytes of heap memory", nMem); } sqlite3_config(SQLITE_CONFIG_HEAP, pHeap, nMemThisDb, 128); }else{ sqlite3_hard_heap_limit64((sqlite3_int64)nMemThisDb); } }else{ sqlite3_hard_heap_limit64(0); } /* Disable lookaside with the --native-malloc option */ if( nativeMalloc ){ sqlite3_config(SQLITE_CONFIG_LOOKASIDE, 0, 0); } /* Reset the in-memory virtual filesystem */ formatVfs(); /* Run a test using each SQL script against each database. */ if( !verboseFlag && !quietFlag && !bSpinner ) printf("%s:", zDbName); for(pSql=g.pFirstSql; pSql; pSql=pSql->pNext){ if( isDbSql(pSql->a, pSql->sz) ){ sqlite3_snprintf(sizeof(g.zTestName), g.zTestName, "sqlid=%d",pSql->id); if( bSpinner ){ int nTotal =g.nSql; int idx = pSql->seq; printf("\r%s: %d/%d ", zDbName, idx, nTotal); fflush(stdout); }else if( verboseFlag ){ printf("%s\n", g.zTestName); fflush(stdout); }else if( !quietFlag ){ static int prevAmt = -1; int idx = pSql->seq; int amt = idx*10/(g.nSql); if( amt!=prevAmt ){ printf(" %d%%", amt*10); fflush(stdout); prevAmt = amt; } } if( nSkip>0 ){ nSkip--; }else{ runCombinedDbSqlInput(pSql->a, pSql->sz); } nTest++; g.zTestName[0] = 0; disableOom(); continue; } for(pDb=g.pFirstDb; pDb; pDb=pDb->pNext){ int openFlags; const char *zVfs = "inmem"; sqlite3_snprintf(sizeof(g.zTestName), g.zTestName, "sqlid=%d,dbid=%d", pSql->id, pDb->id); if( bSpinner ){ int nTotal = g.nDb*g.nSql; int idx = pSql->seq*g.nDb + pDb->id - 1; printf("\r%s: %d/%d ", zDbName, idx, nTotal); fflush(stdout); }else if( verboseFlag ){ printf("%s\n", g.zTestName); fflush(stdout); }else if( !quietFlag ){ static int prevAmt = -1; int idx = pSql->seq*g.nDb + pDb->id - 1; int amt = idx*10/(g.nDb*g.nSql); if( amt!=prevAmt ){ printf(" %d%%", amt*10); fflush(stdout); prevAmt = amt; } } if( nSkip>0 ){ nSkip--; continue; } createVFile("main.db", pDb->sz, pDb->a); sqlite3_randomness(0,0); if( ossFuzzThisDb ){ #ifndef SQLITE_OSS_FUZZ fatalError("--oss-fuzz not supported: recompile" " with -DSQLITE_OSS_FUZZ"); #else extern int LLVMFuzzerTestOneInput(const uint8_t*, size_t); LLVMFuzzerTestOneInput((const uint8_t*)pSql->a, (size_t)pSql->sz); #endif }else{ openFlags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE; if( nativeFlag && pDb->sz==0 ){ openFlags |= SQLITE_OPEN_MEMORY; zVfs = 0; } rc = sqlite3_open_v2("main.db", &db, openFlags, zVfs); if( rc ) fatalError("cannot open inmem database"); sqlite3_limit(db, SQLITE_LIMIT_LENGTH, 100000000); sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 50); if( cellSzCkFlag ) runSql(db, "PRAGMA cell_size_check=ON", runFlags); setAlarm(iTimeout); #ifndef SQLITE_OMIT_PROGRESS_CALLBACK if( sqlFuzz || vdbeLimitFlag ){ sqlite3_progress_handler(db, 100000, progressHandler, &vdbeLimitFlag); } #endif #ifdef SQLITE_TESTCTRL_PRNG_SEED sqlite3_test_control(SQLITE_TESTCTRL_PRNG_SEED, 1, db); #endif if( bVdbeDebug ){ sqlite3_exec(db, "PRAGMA vdbe_debug=ON", 0, 0, 0); } do{ runSql(db, (char*)pSql->a, runFlags); }while( timeoutTest ); setAlarm(0); sqlite3_exec(db, "PRAGMA temp_store_directory=''", 0, 0, 0); sqlite3_close(db); } if( sqlite3_memory_used()>0 ){ fatalError("memory leak: %lld bytes outstanding", sqlite3_memory_used()); } reformatVfs(); nTest++; g.zTestName[0] = 0; /* Simulate an error if the TEST_FAILURE environment variable is "5". ** This is used to verify that automated test script really do spot ** errors that occur in this test program. */ if( zFailCode ){ if( zFailCode[0]=='5' && zFailCode[1]==0 ){ fatalError("simulated failure"); }else if( zFailCode[0]!=0 ){ /* If TEST_FAILURE is something other than 5, just exit the test ** early */ printf("\nExit early due to TEST_FAILURE being set\n"); iSrcDb = nSrcDb-1; goto sourcedb_cleanup; } } } } if( bSpinner ){ printf("\n"); }else if( !quietFlag && !verboseFlag ){ printf(" 100%% - %d tests\n", g.nDb*g.nSql); } /* Clean up at the end of processing a single source database */ sourcedb_cleanup: blobListFree(g.pFirstSql); blobListFree(g.pFirstDb); reformatVfs(); } /* End loop over all source databases */ if( !quietFlag ){ sqlite3_int64 iElapse = timeOfDay() - iBegin; printf("fuzzcheck: 0 errors out of %d tests in %d.%03d seconds\n" "SQLite %s %s\n", nTest, (int)(iElapse/1000), (int)(iElapse%1000), sqlite3_libversion(), sqlite3_sourceid()); } free(azSrcDb); free(pHeap); return 0; }