aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2016-12-17 20:27:22 +0000
committerdrh <drh@noemail.net>2016-12-17 20:27:22 +0000
commit6918e2f9b22d475fa31d1f29cad418a438f05f42 (patch)
treea864edbd678955059d8ab07ad7ba70832a74e9c4
parent11da002c0209be4cd0e07cefdef0e869e174f2e0 (diff)
downloadsqlite-6918e2f9b22d475fa31d1f29cad418a438f05f42.tar.gz
sqlite-6918e2f9b22d475fa31d1f29cad418a438f05f42.zip
Enhance fuzzershell.c to read and execute SQL commands in the autoexec table
of the database under test. Add the dbfuzz.c test program combining selected features of fuzzershell.c and fuzzcheck.c. FossilOrigin-Name: ef6e071a62cd79a0edbbef9f41ca9482540e5cb8
-rw-r--r--Makefile.in4
-rw-r--r--main.mk6
-rw-r--r--manifest19
-rw-r--r--manifest.uuid2
-rw-r--r--test/dbfuzz.c710
-rw-r--r--tool/fuzzershell.c72
6 files changed, 803 insertions, 10 deletions
diff --git a/Makefile.in b/Makefile.in
index 76233c4a7..b961070c2 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -567,6 +567,7 @@ SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ
FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c
+DBFUZZ_OPT =
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.
@@ -621,6 +622,9 @@ fuzzcheck$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h
ossshell$(TEXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/ossshell.c sqlite3.c sqlite3.h $(TLIBS)
+dbfuzz$(TEXE): $(TOP)/test/dbfuzz.c sqlite3.c sqlite3.h
+ $(LTLINK) -o $@ $(DBFUZZ_OPT) $(TOP)/test/dbfuzz.c sqlite3.c $(TLIBS)
+
mptester$(TEXE): sqlite3.lo $(TOP)/mptest/mptest.c
$(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.lo \
$(TLIBS) -rpath "$(libdir)"
diff --git a/main.mk b/main.mk
index 91487b72b..e07b964d6 100644
--- a/main.mk
+++ b/main.mk
@@ -477,6 +477,7 @@ SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS
SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
+DBFUZZ_OPT =
# This is the default Makefile target. The objects listed here
# are what get build when you type just "make" with no arguments.
@@ -513,6 +514,11 @@ fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h
$(FUZZERSHELL_OPT) $(TOP)/tool/fuzzershell.c sqlite3.c \
$(TLIBS) $(THREADLIB)
+dbfuzz$(EXE): $(TOP)/test/dbfuzz.c sqlite3.c sqlite3.h
+ $(TCCX) -o dbfuzz$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
+ $(DBFUZZ_OPT) $(TOP)/test/dbfuzz.c sqlite3.c \
+ $(TLIBS) $(THREADLIB)
+
fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(TOP)/test/ossfuzz.c
$(TCCX) -o fuzzcheck$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_ENABLE_MEMSYS5 $(FUZZCHECK_OPT) -DSQLITE_OSS_FUZZ \
diff --git a/manifest b/manifest
index 3f3cdc35e..1dc3a4ab5 100644
--- a/manifest
+++ b/manifest
@@ -1,6 +1,6 @@
-C Fix\sa\sproblem\sin\sthe\sshell\stools\sreadfile()\scommand\scausing\sblobs\sto\sbe\s\ntruncated\sat\sthe\sfirst\sembedded\s0x00\sbyte\sin\srelease\sbuilds,\sor\san\sassert()\sto\nfail\sin\sa\sdebug\sbuild.
-D 2016-12-17T08:18:05.241
-F Makefile.in c194b58fe00c370a48ac6ae6945e92a7781db1c8
+C Enhance\sfuzzershell.c\sto\sread\sand\sexecute\sSQL\scommands\sin\sthe\sautoexec\stable\nof\sthe\sdatabase\sunder\stest.\s\sAdd\sthe\sdbfuzz.c\stest\sprogram\scombining\sselected\nfeatures\sof\sfuzzershell.c\sand\sfuzzcheck.c.
+D 2016-12-17T20:27:22.394
+F Makefile.in 41bd4cad981487345c4a84081074bcdb876e4b2e
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc b8ca53350ae545e3562403d5da2a69cec79308da
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
@@ -312,7 +312,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk bd1149fdb5543e432a8530a0ea7642613a655bc1
+F main.mk 2cf5f0362c5687fd8e912c3a327b49a2e8ba0f9b
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -628,6 +628,7 @@ F test/ctime.test ff6c38e822459d6ca743c34901caf57740b08b54
F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856
F test/cursorhint2.test fa41f0d997e67db921d08c31e73111b32811201a
F test/date.test a6a5a48b90907bca9fbcc79a30be5a715c1ab2fc
+F test/dbfuzz.c 2bf7858028c0c437ccffc5795f791c495800cd9b
F test/dbstatus.test 73149851b3aff14fc6db478e58f9083a66422cf5
F test/dbstatus2.test e93ab03bfae6d62d4d935f20de928c19ca0ed0ab
F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d
@@ -1461,7 +1462,7 @@ F tool/dbhash.c a06228aa21ebc4e6ea8daa486601d938499238a5
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
F tool/fast_vacuum.c 5ba0d6f5963a0a63bdc42840f678bad75b2ebce1
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
-F tool/fuzzershell.c d5c870a07452caa5c70a8e49d6c64e403558686d
+F tool/fuzzershell.c dbf6c26eef936ec78cb0707570de3a4308b2507e
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
@@ -1538,7 +1539,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 94689e3bdac2eabbcf1a51d741c2604ed4bd8a40
-R b4ced5d015604f5a5058a9f967dfbb21
-U dan
-Z aab7365a5b426408e7c16bc3f1fbf9d3
+P 8dedd6ad44bd1d103dced9d1350188cb2327128d
+R 467cdb2635ae3db86cb6ad0c3fe29c15
+U drh
+Z 6af4ccd37cf6da1faf8a726dcd8a746b
diff --git a/manifest.uuid b/manifest.uuid
index 04a638bfe..258fb2afd 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-8dedd6ad44bd1d103dced9d1350188cb2327128d \ No newline at end of file
+ef6e071a62cd79a0edbbef9f41ca9482540e5cb8 \ No newline at end of file
diff --git a/test/dbfuzz.c b/test/dbfuzz.c
new file mode 100644
index 000000000..0484b15eb
--- /dev/null
+++ b/test/dbfuzz.c
@@ -0,0 +1,710 @@
+/*
+** 2016-12-17
+**
+** 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 program is designed for fuzz-testing SQLite database files.
+**
+** This program reads fuzzed database files from the disk files named
+** on the command-line. Each database is loaded into an in-memory
+** filesystem so that the original database file is unmolested.
+**
+** The fuzzed database is then opened, and series of SQL statements
+** are run against the database to ensure that SQLite can safely handle
+** the fuzzed database.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#define ISSPACE(X) isspace((unsigned char)(X))
+#define ISDIGIT(X) isdigit((unsigned char)(X))
+#include "sqlite3.h"
+#ifdef __unix__
+# include <signal.h>
+# include <unistd.h>
+#endif
+
+/*
+** Print sketchy documentation for this utility program
+*/
+static void showHelp(const char *zArgv0){
+ printf("Usage: %s [options] DATABASE ...\n", zArgv0);
+ printf(
+"Read databases into an in-memory filesystem. Run test SQL as specified\n"
+"by command-line arguments or from\n"
+"\n"
+" SELECT group_concat(sql) FROM autoexec;\n"
+"\n"
+"Options:\n"
+" --help Show this help text\n"
+" -q|--quiet Reduced output\n"
+" --limit-mem N Limit memory used by test SQLite instances to N bytes\n"
+" --limit-vdbe Panic if any test runs for more than 100,000 cycles\n"
+" --no-lookaside Disable the lookaside memory allocator\n"
+" --timeout N Timeout after N seconds.\n"
+" --trace Show the results of each SQL command\n"
+" -v|--verbose Increased output. Repeat for more output.\n"
+ );
+ exit(0);
+}
+
+/*
+** Print an error message and quit.
+*/
+static void fatalError(const char *zFormat, ...){
+ va_list ap;
+ va_start(ap, zFormat);
+ vfprintf(stderr, zFormat, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/*
+** Files in the virtual file system.
+*/
+typedef struct VFile VFile;
+typedef struct VHandle VHandle;
+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() */
+};
+struct VHandle {
+ sqlite3_file base; /* Base class. Must be first */
+ VFile *pVFile; /* The underlying file */
+};
+
+/*
+** Maximum number of files in the in-memory virtual filesystem.
+*/
+#define MX_FILE 10
+
+/*
+** Maximum allowed file size
+*/
+#define MX_FILE_SZ 1000000
+
+/*
+** All global variables are gathered into the "g" singleton.
+*/
+static struct GlobalVars {
+ VFile aFile[MX_FILE]; /* The virtual filesystem */
+} g;
+
+
+/*
+** Initialize the virtual file system.
+*/
+static void formatVfs(void){
+ int i;
+ for(i=0; i<MX_FILE; i++){
+ g.aFile[i].sz = -1;
+ g.aFile[i].zFilename = 0;
+ g.aFile[i].a = 0;
+ g.aFile[i].nRef = 0;
+ }
+}
+
+
+/*
+** Erase all information in the virtual file system.
+*/
+static void reformatVfs(void){
+ int i;
+ for(i=0; i<MX_FILE; i++){
+ if( g.aFile[i].sz<0 ) continue;
+ if( g.aFile[i].zFilename ){
+ free(g.aFile[i].zFilename);
+ g.aFile[i].zFilename = 0;
+ }
+ if( g.aFile[i].nRef>0 ){
+ 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<MX_FILE; i++){
+ if( g.aFile[i].zFilename==0 ) continue;
+ if( strcmp(g.aFile[i].zFilename, zName)==0 ) return &g.aFile[i];
+ }
+ return 0;
+}
+
+/*
+** Find a VFile called zName. Initialize it to the content of
+** disk file zDiskFile.
+**
+** Return NULL if the filesystem is full.
+*/
+static VFile *createVFile(const char *zName, const char *zDiskFile){
+ VFile *pNew = findVFile(zName);
+ int i;
+ FILE *in = 0;
+ long sz = 0;
+
+ if( pNew ) return pNew;
+ for(i=0; i<MX_FILE && g.aFile[i].sz>=0; i++){}
+ if( i>=MX_FILE ) return 0;
+ if( zDiskFile ){
+ in = fopen(zDiskFile, "rb");
+ if( in==0 ) fatalError("no such file: \"%s\"", zDiskFile);
+ fseek(in, 0, SEEK_END);
+ sz = ftell(in);
+ rewind(in);
+ }
+ pNew = &g.aFile[i];
+ if( zName ){
+ int nName = (int)strlen(zName)+1;
+ pNew->zFilename = malloc(nName);
+ if( pNew->zFilename==0 ){
+ if( in ) fclose(in);
+ return 0;
+ }
+ memcpy(pNew->zFilename, zName, nName);
+ }else{
+ pNew->zFilename = 0;
+ }
+ pNew->nRef = 0;
+ pNew->sz = sz;
+ pNew->a = malloc(sz);
+ if( sz>0 ){
+ if( pNew->a==0 || fread(pNew->a, sz, 1, in)<1 ){
+ free(pNew->zFilename);
+ free(pNew->a);
+ pNew->a = 0;
+ pNew->zFilename = 0;
+ pNew->sz = -1;
+ pNew = 0;
+ }
+ }
+ if( in ) fclose(in);
+ return pNew;
+}
+
+/* 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, 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 ){
+ unsigned char *aNew;
+ if( iOfst+iAmt >= MX_FILE_SZ ){
+ return SQLITE_FULL;
+ }
+ aNew = realloc(pVFile->a, (int)(iOfst+iAmt));
+ if( aNew==0 ){
+ return SQLITE_FULL;
+ }
+ pVFile->a = aNew;
+ 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);
+ 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;
+}
+
+/*
+** Register the VFS that reads from the g.aFile[] set of files.
+*/
+static void inmemVfsRegister(void){
+ static sqlite3_vfs inmemVfs;
+ sqlite3_vfs *pDefault = sqlite3_vfs_find(0);
+ inmemVfs.iVersion = 3;
+ inmemVfs.szOsFile = sizeof(VHandle);
+ inmemVfs.mxPathname = 200;
+ inmemVfs.zName = "inmem";
+ inmemVfs.xOpen = inmemOpen;
+ inmemVfs.xDelete = inmemDelete;
+ inmemVfs.xAccess = inmemAccess;
+ inmemVfs.xFullPathname = inmemFullPathname;
+ inmemVfs.xRandomness = pDefault->xRandomness;
+ inmemVfs.xSleep = pDefault->xSleep;
+ inmemVfs.xCurrentTimeInt64 = pDefault->xCurrentTimeInt64;
+ sqlite3_vfs_register(&inmemVfs, 0);
+};
+
+/*
+** Timeout handler
+*/
+#ifdef __unix__
+static void timeoutHandler(int NotUsed){
+ (void)NotUsed;
+ fatalError("timeout\n");
+}
+#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
+}
+/***************************************************************************
+** String accumulator object
+*/
+typedef struct Str Str;
+struct Str {
+ char *z; /* The string. Memory from malloc() */
+ sqlite3_uint64 n; /* Bytes of input used */
+ sqlite3_uint64 nAlloc; /* Bytes allocated to z[] */
+ int oomErr; /* OOM error has been seen */
+};
+
+/* Initialize a Str object */
+static void StrInit(Str *p){
+ memset(p, 0, sizeof(*p));
+}
+
+/* Append text to the end of a Str object */
+static void StrAppend(Str *p, const char *z){
+ sqlite3_uint64 n = strlen(z);
+ if( p->n + n >= p->nAlloc ){
+ char *zNew;
+ sqlite3_uint64 nNew;
+ if( p->oomErr ) return;
+ nNew = p->nAlloc*2 + 100 + n;
+ zNew = sqlite3_realloc(p->z, nNew);
+ if( zNew==0 ){
+ sqlite3_free(p->z);
+ memset(p, 0, sizeof(*p));
+ p->oomErr = 1;
+ return;
+ }
+ p->z = zNew;
+ p->nAlloc = nNew;
+ }
+ memcpy(p->z + p->n, z, n);
+ p->n += n;
+ p->z[p->n] = 0;
+}
+
+/* Return the current string content */
+static char *StrStr(Str *p){
+ return p->z;
+}
+
+/* Free the string */
+static void StrFree(Str *p){
+ sqlite3_free(p->z);
+ StrInit(p);
+}
+
+/*
+** Return the value of a hexadecimal digit. Return -1 if the input
+** is not a hex digit.
+*/
+static int hexDigitValue(char c){
+ if( c>='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; i<sizeof(aMult)/sizeof(aMult[0]); i++){
+ if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
+ v *= aMult[i].iMult;
+ break;
+ }
+ }
+ if( v>0x7fffffff ) fatalError("parameter too large - max 2147483648");
+ return (int)(isNeg? -v : v);
+}
+
+/*
+** This callback is invoked by sqlite3_exec() to return query results.
+*/
+static int execCallback(void *NotUsed, int argc, char **argv, char **colv){
+ int i;
+ static unsigned cnt = 0;
+ printf("ROW #%u:\n", ++cnt);
+ if( argv ){
+ for(i=0; i<argc; i++){
+ printf(" %s=", colv[i]);
+ if( argv[i] ){
+ printf("[%s]\n", argv[i]);
+ }else{
+ printf("NULL\n");
+ }
+ }
+ }
+ fflush(stdout);
+ return 0;
+}
+static int execNoop(void *NotUsed, int argc, char **argv, char **colv){
+ return 0;
+}
+
+/*
+** This callback is invoked by sqlite3_log().
+*/
+static void sqlLog(void *pNotUsed, int iErrCode, const char *zMsg){
+ printf("LOG: (%d) %s\n", iErrCode, zMsg);
+ fflush(stdout);
+}
+
+#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
+
+
+
+int main(int argc, char **argv){
+ int i; /* Loop counter */
+ int nDb; /* Number of databases to fuzz */
+ const char **azDb = 0; /* Names of the databases (limit: 20) */
+ int verboseFlag = 0; /* True for extra output */
+ int traceFlag = 0; /* True to trace results */
+ int noLookaside = 0; /* Disable lookaside if true */
+ int vdbeLimitFlag = 0; /* Stop after 100,000 VDBE ops */
+ int nHeap = 0; /* True for fixed heap size */
+ int iTimeout = 0; /* Timeout delay in seconds */
+ int rc; /* Result code from SQLite3 API calls */
+ sqlite3 *db; /* The database connection */
+ sqlite3_stmt *pStmt; /* A single SQL statement */
+ Str sql; /* SQL to run */
+
+ for(i=1; i<argc; i++){
+ const char *z = argv[i];
+ if( z[0]!='-' ){
+ azDb = realloc(azDb, sizeof(azDb[0])*(nDb+1));
+ if( azDb==0 ) fatalError("out of memory");
+ azDb[nDb++] = z;
+ continue;
+ }
+ z++;
+ if( z[0]=='-' ) z++;
+ if( strcmp(z, "help")==0 ){
+ showHelp(argv[0]);
+ }else if( strcmp(z, "limit-mem")==0 ){
+ if( i==argc-1 ) fatalError("missing argument to %s", argv[i]);
+ nHeap = integerValue(argv[++i]);
+ }else if( strcmp(z, "no-lookaside")==0 ){
+ noLookaside = 1;
+ }else if( strcmp(z, "timeout")==0 ){
+ if( i==argc-1 ) fatalError("missing argument to %s", argv[i]);
+ iTimeout = integerValue(argv[++i]);
+ }else if( strcmp(z, "trace")==0 ){
+ traceFlag = 1;
+ }else if( strcmp(z, "limit-vdbe")==0 ){
+ vdbeLimitFlag = 1;
+ }else if( strcmp(z, "v")==0 || strcmp(z, "verbose")==0 ){
+ verboseFlag = 1;
+ }else{
+ fatalError("unknown command-line option: \"%s\"\n", argv[i]);
+ }
+ }
+ if( nDb==0 ){
+ showHelp(argv[0]);
+ }
+ if( verboseFlag ){
+ sqlite3_config(SQLITE_CONFIG_LOG, sqlLog);
+ }
+ if( nHeap>0 ){
+ void *pHeap = malloc( nHeap );
+ if( pHeap==0 ) fatalError("cannot allocate %d-byte heap\n", nHeap);
+ rc = sqlite3_config(SQLITE_CONFIG_HEAP, pHeap, nHeap, 32);
+ if( rc ) fatalError("heap configuration failed: %d\n", rc);
+ }
+ if( noLookaside ){
+ sqlite3_config(SQLITE_CONFIG_LOOKASIDE, 0, 0);
+ }
+ inmemVfsRegister();
+ formatVfs();
+ StrInit(&sql);
+#ifdef __unix__
+ signal(SIGALRM, timeoutHandler);
+#endif
+ for(i=0; i<nDb; i++){
+ StrFree(&sql);
+
+ if( verboseFlag && nDb>1 ){
+ printf("DATABASE-FILE: %s\n", azDb[i]);
+ fflush(stdout);
+ }
+ if( iTimeout ) setAlarm(iTimeout);
+ createVFile("test.db", azDb[i]);
+ rc = sqlite3_open_v2("test.db", &db, SQLITE_OPEN_READWRITE, "inmem");
+ if( rc ){
+ printf("cannot open test.db for \"%s\"\n", azDb[i]);
+ reformatVfs();
+ continue;
+ }
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ if( vdbeLimitFlag ){
+ sqlite3_progress_handler(db, 100000, progressHandler, &vdbeLimitFlag);
+ }
+#endif
+ rc = sqlite3_prepare_v2(db, "SELECT sql FROM autoexec", -1, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ StrAppend(&sql, (const char*)sqlite3_column_text(pStmt, 0));
+ StrAppend(&sql, "\n");
+ }
+ }
+ sqlite3_finalize(pStmt);
+ StrAppend(&sql, "PRAGMA integrity_check;\n");
+ if( traceFlag ){
+ char *zErrMsg = 0;
+ rc = sqlite3_exec(db, StrStr(&sql), execCallback, 0, &zErrMsg);
+ if( zErrMsg ){
+ printf("ERRMSG: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }
+ }else {
+ rc = sqlite3_exec(db, StrStr(&sql), execNoop, 0, 0);
+ }
+ sqlite3_close(db);
+ reformatVfs();
+ StrFree(&sql);
+ if( sqlite3_memory_used()>0 ){
+ free(azDb);
+ reformatVfs();
+ fatalError("memory leak of %lld bytes", sqlite3_memory_used());
+ }
+ }
+ StrFree(&sql);
+ reformatVfs();
+ return 0;
+}
diff --git a/tool/fuzzershell.c b/tool/fuzzershell.c
index 710e07024..c75beb4d8 100644
--- a/tool/fuzzershell.c
+++ b/tool/fuzzershell.c
@@ -227,6 +227,56 @@ static void traceNoop(void *NotUsed, const char *zMsg){
#endif
/***************************************************************************
+** String accumulator object
+*/
+typedef struct Str Str;
+struct Str {
+ char *z; /* The string. Memory from malloc() */
+ sqlite3_uint64 n; /* Bytes of input used */
+ sqlite3_uint64 nAlloc; /* Bytes allocated to z[] */
+ int oomErr; /* OOM error has been seen */
+};
+
+/* Initialize a Str object */
+static void StrInit(Str *p){
+ memset(p, 0, sizeof(*p));
+}
+
+/* Append text to the end of a Str object */
+static void StrAppend(Str *p, const char *z){
+ sqlite3_uint64 n = strlen(z);
+ if( p->n + n >= p->nAlloc ){
+ char *zNew;
+ sqlite3_uint64 nNew;
+ if( p->oomErr ) return;
+ nNew = p->nAlloc*2 + 100 + n;
+ zNew = sqlite3_realloc(p->z, nNew);
+ if( zNew==0 ){
+ sqlite3_free(p->z);
+ memset(p, 0, sizeof(*p));
+ p->oomErr = 1;
+ return;
+ }
+ p->z = zNew;
+ p->nAlloc = nNew;
+ }
+ memcpy(p->z + p->n, z, n);
+ p->n += n;
+ p->z[p->n] = 0;
+}
+
+/* Return the current string content */
+static char *StrStr(Str *p){
+ return p->z;
+}
+
+/* Free the string */
+static void StrFree(Str *p){
+ sqlite3_free(p->z);
+ StrInit(p);
+}
+
+/***************************************************************************
** eval() implementation copied from ../ext/misc/eval.c
*/
/*
@@ -1028,6 +1078,8 @@ int main(int argc, char **argv){
oomCnt = 0;
}
do{
+ Str sql;
+ StrInit(&sql);
if( zDbName ){
rc = sqlite3_open_v2(zDbName, &db, SQLITE_OPEN_READWRITE, 0);
if( rc!=SQLITE_OK ){
@@ -1057,6 +1109,25 @@ int main(int argc, char **argv){
if( pageSize ) sqlexec(db, "PRAGMA pagesize=%d", pageSize);
if( doAutovac ) sqlexec(db, "PRAGMA auto_vacuum=FULL");
iStart = timeOfDay();
+
+ /* If using an input database file and that database contains a table
+ ** named "autoexec" with a column "sql", then replace the input SQL
+ ** with the concatenated text of the autoexec table. In this way,
+ ** if the database file is the input being fuzzed, the SQL text is
+ ** fuzzed at the same time. */
+ if( sqlite3_table_column_metadata(db,0,"autoexec","sql",0,0,0,0,0)==0 ){
+ sqlite3_stmt *pStmt;
+ rc = sqlite3_prepare_v2(db, "SELECT sql FROM autoexec", -1, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ StrAppend(&sql, (const char*)sqlite3_column_text(pStmt, 0));
+ StrAppend(&sql, "\n");
+ }
+ }
+ sqlite3_finalize(pStmt);
+ zSql = StrStr(&sql);
+ }
+
g.bOomEnable = 1;
if( verboseFlag ){
zErrMsg = 0;
@@ -1070,6 +1141,7 @@ int main(int argc, char **argv){
}
g.bOomEnable = 0;
iEnd = timeOfDay();
+ StrFree(&sql);
rc = sqlite3_close(db);
if( rc ){
abendError("sqlite3_close() failed with rc=%d", rc);