From: drh Date: Mon, 25 May 2015 18:48:19 +0000 (+0000) Subject: First code for a new utility program to rerun checks on a large number of X-Git-Tag: version-3.8.11~220^2~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3b74d0327f86a1346a2e78c04adb07070a84c13b;p=thirdparty%2Fsqlite.git First code for a new utility program to rerun checks on a large number of fuzzer-generated test cases. FossilOrigin-Name: c5b4e363528aa1d2d5f41451f16de0aa91152b38 --- diff --git a/main.mk b/main.mk index 1b78164866..2e3e7bd16c 100644 --- a/main.mk +++ b/main.mk @@ -425,6 +425,10 @@ fuzzershell$(EXE): $(TOP)/tool/fuzzershell.c sqlite3.c sqlite3.h $(TCCX) -o fuzzershell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION\ $(TOP)/tool/fuzzershell.c sqlite3.c $(TLIBS) $(THREADLIB) +fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h + $(TCCX) -o fuzzcheck$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION\ + $(TOP)/test/fuzzcheck.c sqlite3.c $(TLIBS) $(THREADLIB) + mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c $(TCCX) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \ $(TLIBS) $(THREADLIB) diff --git a/manifest b/manifest index f15e3642d6..eb969770bb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\scase\swhere\sdatabase\scorruption\smay\scause\sSQLite\sto\swrite\spast\sthe\send\sof\sa\sbuffer. -D 2015-05-25T17:07:29.779 +C First\scode\sfor\sa\snew\sutility\sprogram\sto\srerun\schecks\son\sa\slarge\snumber\sof\nfuzzer-generated\stest\scases. +D 2015-05-25T18:48:19.012 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 0a6ae26396ec696221021780dffbb894ff3cead7 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -171,7 +171,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 59e4ac92e080c23d8e260195748c8a706dd18a20 +F main.mk 901e78812c4040c6991cd1370b3f40822c3bd830 F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea F mkopcodeh.awk d5e22023b5238985bb54a72d33e0ac71fe4f8a32 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 @@ -651,6 +651,7 @@ F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1 F test/fuzz3.test efd384b896c647b61a2c1848ba70d42aad60a7b3 F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26 +F test/fuzzcheck.c ae31f7af026968cdb04d0452fb1e8031d4ca3aa9 F test/fuzzdata1.txt 9fceb50868e0b798160e83742bd7e44e457176a0 F test/fuzzdata2.txt ba9b4467d7ec46cc85d32c0d031540cd727ae6ad F test/fuzzer1.test d4c52aaf3ef923da293a2653cfab33d02f718a36 @@ -1278,7 +1279,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 00a473c56188cd60a74559effb114140e3fe8a8d -R dc376bcfcb68a6e7c538fd875c1e9af7 -U dan -Z 5eaace98730adbf7603d90b10f2ffa48 +P 97806a78142b15f89878e25ee70dc5b0524d6793 +R 01232d1301acd5ee81dd9c394ecdbcf8 +T *branch * fuzzcheck +T *sym-fuzzcheck * +T -sym-trunk * +U drh +Z e7fd4cad8a5ae98c34f9373dc841d5b8 diff --git a/manifest.uuid b/manifest.uuid index c6cb20ab53..dfd8e45d8d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -97806a78142b15f89878e25ee70dc5b0524d6793 \ No newline at end of file +c5b4e363528aa1d2d5f41451f16de0aa91152b38 \ No newline at end of file diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c new file mode 100644 index 0000000000..d8dbe33186 --- /dev/null +++ b/test/fuzzcheck.c @@ -0,0 +1,715 @@ +/* +** 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 SQLite library using data from an external fuzzer, such as American +** Fuzzy Lop (AFL) (http://lcamtuf.coredump.cx/afl/). +** +** 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 +** ); +** +** For each database file in the DB table, the SQL text in the XSQL table +** is run against that 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. +*/ +#include +#include +#include +#include +#include +#include "sqlite3.h" + +/* +** 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 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 */ + 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 */ + char zTestName[100]; /* Name of current test */ +} g; + +/* +** Print an error message and quit. +*/ +static void fatalError(const char *zFormat, ...){ + va_list ap; + if( g.zTestName[0] ){ + fprintf(stderr, "%s (%s): ", g.zArgv0, g.zTestName); + }else{ + fprintf(stderr, "%s: ", g.zArgv0); + } + va_start(ap, zFormat); + vfprintf(stderr, zFormat, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(1); +} + +/* +** Reallocate memory. Show and error and quit if unable. +*/ +static void *safe_realloc(void *pOld, int szNew){ + void *pNew = realloc(pOld, 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; + for(i=0; i=0; i++){} + if( i>=MX_FILE ) return 0; + pNew = &g.aFile[i]; + pNew->zFilename = safe_realloc(0, strlen(zName)+1); + memcpy(pNew->zFilename, zName, strlen(zName)+1); + 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); +} + +/* +** 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" +" --help Show this help text\n" +" -q Reduced output\n" +" --quiet Reduced output\n" +" --load-sql ARGS... Load SQL scripts fro files into SOURCE-DB\n" +" --load-db ARGS... Load template databases from files into SOURCE_DB\n" +" -v Increased output\n" +" --verbose Increased output\n" + ); +} + +/* +** 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 *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; + + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + 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->pNext = 0; + memcpy(pNew->a, sqlite3_column_blob(pStmt,1), sz); + pNew->a[sz] = 0; + p->pNext = pNew; + p = pNew; + n++; + } + 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->iVersion>=1 && clockVfs->xCurrentTimeInt64!=0 ){ + clockVfs->xCurrentTimeInt64(clockVfs, &t); + }else{ + double r; + clockVfs->xCurrentTime(clockVfs, &r); + t = (sqlite3_int64)(r*86400000.0); + } + return t; +} + +/* 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 = pVFile->sz - iOfst; + memcpy(pData, pVFile->a, iAmt); + return SQLITE_IOERR_SHORT_READ; + } + memcpy(pData, pVFile->a, 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, iOfst+iAmt); + memset(pVFile->a + pVFile->sz, 0, iOfst - pVFile->sz); + pVFile->sz = 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 = 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; +} + +/* GetLastError() is never used */ +static int inmemGetLastError(sqlite3_vfs *pVfs, int n, char *z){ + 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 = 1; + 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.xCurrentTime = pDefault->xCurrentTime; + inmemVfs.xGetLastError = inmemGetLastError; + sqlite3_vfs_register(&inmemVfs, 0); +}; + +#ifndef SQLITE_OMIT_TRACE +/* +** This callback is invoked by sqlite3_trace() as each SQL statement +** starts. +*/ +static void traceCallback(void *NotUsed, const char *zMsg){ + printf("TRACE: %s\n", zMsg); + fflush(stdout); +} +static void traceNoop(void *NotUsed, const char *zMsg){ + return; +} +#endif + +/* +** 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){ + const char *zMore; + sqlite3_stmt *pStmt; + + while( zSql && zSql[0] ){ + zMore = 0; + pStmt = 0; + sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zMore); + zSql = zMore; + if( pStmt ){ + while( SQLITE_ROW==sqlite3_step(pStmt) ); + sqlite3_finalize(pStmt); + }else{ + break; + } + } +} + +int main(int argc, char **argv){ + sqlite3_int64 iBegin; /* Start time of this program */ + const char *zSourceDb = 0; /* Source database filename */ + 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[] to use for --load-db or --load-sql */ + sqlite3 *db = 0; /* The open database connection */ + 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 */ + + iBegin = timeOfDay(); + g.zArgv0 = argv[0]; + for(i=1; iid = 1; + g.nDb = 1; + } + + + /* 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"); + } + + /* Register the in-memory virtual filesystem + */ + formatVfs(); + inmemVfsRegister(); + + /* Run a test using each SQL script against each database. + */ + if( !verboseFlag && !quietFlag ){ + int i; + i = strlen(zSourceDb) - 1; + while( i>0 && zSourceDb[i-1]!='/' && zSourceDb[i-1]!='\\' ){ i--; } + printf("%s:", &zSourceDb[i]); + } + for(pSql=g.pFirstSql; pSql; pSql=pSql->pNext){ + for(pDb=g.pFirstDb; pDb; pDb=pDb->pNext){ + sqlite3_snprintf(sizeof(g.zTestName), g.zTestName, "sqlid=%d,dbid=%d", + pSql->id, pDb->id); + if( verboseFlag ){ + printf("%s\n", g.zTestName); + fflush(stdout); + }else if( !quietFlag ){ + static int prevAmt = -1; + int idx = (pSql->id-1)*g.nDb + pDb->id - 1; + int amt = idx*10/(g.nDb*g.nSql); + if( amt!=prevAmt ){ + printf(" %d%%", amt*10); + prevAmt = amt; + } + } + createVFile("main.db", pDb->sz, pDb->a); + rc = sqlite3_open_v2("main.db", &db, SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE, + "inmem"); +#ifndef SQLITE_OMIT_TRACE + sqlite3_trace(db, verboseFlag ? traceCallback : traceNoop, 0); +#endif + if( rc ) fatalError("cannot open inmem database"); + runSql(db, (char*)pSql->a); + sqlite3_close(db); + if( sqlite3_memory_used()>0 ) fatalError("memory leak"); + reformatVfs(); + g.zTestName[0] = 0; + } + } + + if( !quietFlag ){ + sqlite3_int64 iElapse = timeOfDay() - iBegin; + if( !verboseFlag ) printf("\n"); + printf("fuzzcheck: 0 errors out of %d tests in %d.%03d seconds\nSQLite %s %s\n", + g.nDb*g.nSql, (int)(iElapse/1000), (int)(iElapse%1000), + sqlite3_libversion(), sqlite3_sourceid()); + } + + /* Clean up and exit. + */ + blobListFree(g.pFirstSql); + blobListFree(g.pFirstDb); + reformatVfs(); + return 0; +}