#include <string.h>
#include <stdarg.h>
#include <ctype.h>
+#include <assert.h>
#include "sqlite3.h"
#define ISSPACE(X) isspace((unsigned char)(X))
#define ISDIGIT(X) isdigit((unsigned char)(X))
}
}
-
/* Return the current wall-clock time */
static sqlite3_int64 timeOfDay(void){
static sqlite3_vfs *clockVfs = 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 iTimeout = 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 byte-code program length in SQLite */
+static int vdbeOpLimit = 25000;
+
+/* Maximum size of the in-memory database */
+static sqlite3_int64 maxDbSize = 104857600;
+
+/*
+** 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 int 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<nIn && (c = zIn[i])!=']'; i++){
+ if( !isxdigit(c) ) return 0;
+ k = k*16 + hexToInt(c);
+ }
+ if( i==nIn ) return 0;
+ *pK = 2*k;
+ *pI += i;
+ return 1;
+}
+
+/*
+** Decode the text starting at zIn into a binary database file.
+** The maximum length of zIn is nIn bytes. Compute the binary database
+** file contain in space obtained from sqlite3_malloc().
+**
+** Return the number of bytes of zIn consumed. Or return -1 if there
+** is an error. One potential error is that the recipe specifies a
+** database file larger than MX_FILE_SZ bytes.
+**
+** Abort on an OOM.
+*/
+static int decodeDatabase(
+ const unsigned char *zIn, /* Input text to be decoded */
+ int nIn, /* Bytes of input text */
+ unsigned char **paDecode, /* OUT: decoded database file */
+ int *pnDecode /* OUT: Size of decoded database */
+){
+ unsigned char *a; /* Database under construction */
+ int mx = 0; /* Current size of the database */
+ sqlite3_uint64 nAlloc = 4096; /* Space allocated in a[] */
+ unsigned int i; /* Next byte of zIn[] to read */
+ unsigned int j; /* Temporary integer */
+ unsigned int k; /* half-byte cursor index for output */
+ unsigned int n; /* Number of bytes of input */
+ unsigned char b = 0;
+ if( nIn<4 ) return -1;
+ n = (unsigned int)nIn;
+ a = sqlite3_malloc( nAlloc );
+ if( a==0 ){
+ fprintf(stderr, "Out of memory!\n");
+ exit(1);
+ }
+ memset(a, 0, nAlloc);
+ for(i=k=0; i<n; i++){
+ char c = zIn[i];
+ if( isxdigit(c) ){
+ k++;
+ if( k & 1 ){
+ b = hexToInt(c)*16;
+ }else{
+ b += hexToInt(c);
+ j = k/2 - 1;
+ if( j>=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;
+ }
+ a = sqlite3_realloc( a, newSize );
+ if( a==0 ){
+ fprintf(stderr, "Out of memory!\n");
+ exit(1);
+ }
+ assert( newSize > nAlloc );
+ memset(a+nAlloc, 0, newSize - nAlloc);
+ nAlloc = newSize;
+ }
+ if( j>=(unsigned)mx ){
+ mx = (j + 4095)&~4095;
+ if( mx>MX_FILE_SZ ) mx = MX_FILE_SZ;
+ }
+ assert( j<nAlloc );
+ a[j] = b;
+ }
+ }else if( zIn[i]=='[' && i<n-3 && isOffset(zIn+i, nIn-i, &k, &i) ){
+ continue;
+ }else if( zIn[i]=='\n' && i<n-4 && memcmp(zIn+i,"\n--\n",4)==0 ){
+ i += 4;
+ break;
+ }
+ }
+ *pnDecode = mx;
+ *paDecode = a;
+ return i;
+}
+
+/*
+** Progress handler callback.
+**
+** The argument is the cutoff-time after which all processing should
+** stop. So return non-zero if the cut-off time is exceeded.
+*/
+static int progress_handler(void *pClientData) {
+ FuzzCtx *p = (FuzzCtx*)pClientData;
+ sqlite3_int64 iNow = timeOfDay();
+ int rc = iNow>=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 ){
+ 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;
+ }
+ }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]) ) zSql++;
+ if( zSql[0]==0 ) return SQLITE_OK;
+ if( eVerbosity>=3 ){
+ 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>=4 ){
+ int j;
+ for(j=0; j<sqlite3_column_count(pStmt); j++){
+ if( j ) printf(",");
+ switch( sqlite3_column_type(pStmt, j) ){
+ case SQLITE_NULL: {
+ printf("NULL");
+ break;
+ }
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT: {
+ printf("%s", sqlite3_column_text(pStmt, j));
+ break;
+ }
+ case SQLITE_BLOB: {
+ int n = sqlite3_column_bytes(pStmt, j);
+ int i;
+ const unsigned char *a;
+ a = (const unsigned char*)sqlite3_column_blob(pStmt, j);
+ printf("x'");
+ for(i=0; i<n; i++){
+ printf("%02x", a[i]);
+ }
+ printf("'");
+ break;
+ }
+ case SQLITE_TEXT: {
+ int n = sqlite3_column_bytes(pStmt, j);
+ int i;
+ const unsigned char *a;
+ a = (const unsigned char*)sqlite3_column_blob(pStmt, j);
+ printf("'");
+ for(i=0; i<n; i++){
+ if( a[i]=='\'' ){
+ printf("''");
+ }else{
+ putchar(a[i]);
+ }
+ }
+ printf("'");
+ break;
+ }
+ } /* End switch() */
+ } /* End for() */
+ printf("\n");
+ fflush(stdout);
+ } /* End if( eVerbosity>=4 ) */
+ } /* End while( SQLITE_ROW */
+ if( rc!=SQLITE_DONE && eVerbosity>=3 ){
+ printf("SQL-ERROR: (%d) %s\n", rc, sqlite3_errmsg(db));
+ fflush(stdout);
+ }
+ }else if( eVerbosity>=3 ){
+ 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 in mutator: %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 = nByte - iSql;
+ if( eVerbosity>=2 ){
+ 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 ) 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 iTimeout seconds have
+ ** elapsed since the start of the test.
+ */
+ cx.iLastCb = timeOfDay();
+ cx.iCutoffTime = cx.iLastCb + iTimeout; /* Now + iTimeout 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( 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);
+ }
+
+ /* 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<i ){
+ runDbSql(cx.db, zSql+j);
+ }
+ }
+testrun_finished:
+ sqlite3_free(zSql);
+ rc = sqlite3_close(cx.db);
+ if( rc!=SQLITE_OK ){
+ fprintf(stdout, "sqlite3_close() returns %d\n", rc);
+ }
+ if( eVerbosity ){
+ 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){
+ if( n>4 && memcmp(a,"\n--\n",4)==0 ) return 1;
+ while( n>0 && isspace(a[0]) ){ a++; n--; }
+ if( n>8 && memcmp(a,"53514c69",8)==0 ) return 1;
+ return 0;
+}
+
+
/* Methods for the VHandle object
*/
static int inmemClose(sqlite3_file *pFile){
if( strcmp(z,"quiet")==0 || strcmp(z,"q")==0 ){
quietFlag = 1;
verboseFlag = 0;
+ eVerbosity = 0;
}else
if( strcmp(z,"rebuild")==0 ){
rebuildFlag = 1;
if( strcmp(z,"verbose")==0 || strcmp(z,"v")==0 ){
quietFlag = 0;
verboseFlag++;
+ eVerbosity++;
if( verboseFlag>1 ) runFlags |= SQL_TRACE;
}else
+ if( strcmp(z,"version")==0 ){
+ int ii;
+ const char *z;
+ printf("SQLite %s %s\n", sqlite3_libversion(), sqlite3_sourceid());
+ for(ii=0; (z = sqlite3_compileoption_get(ii))!=0; ii++){
+ printf("%s\n", z);
+ }
+ return 0;
+ }else
{
fatalError("unknown option: %s", argv[i]);
}
*/
if( !verboseFlag && !quietFlag ) 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( 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;
+ }
+ }
+ runCombinedDbSqlInput(pSql->a, pSql->sz);
+ nTest++;
+ g.zTestName[0] = 0;
+ continue;
+ }
for(pDb=g.pFirstDb; pDb; pDb=pDb->pNext){
int openFlags;
const char *zVfs = "inmem";