From: drh <> Date: Tue, 14 Jun 2022 19:12:25 +0000 (+0000) Subject: Attempt to enhance fuzzcheck to do some simple invariant testing on queries. X-Git-Tag: version-3.39.0~33^2~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a1f79dae98b0cc87e144e626dab9cec2729f68da;p=thirdparty%2Fsqlite.git Attempt to enhance fuzzcheck to do some simple invariant testing on queries. This is an incremental check-in for a work-in-progress. FossilOrigin-Name: ce2d780163b3a28486904860a1815acc4169c09b971cfd199bb58d1e9a57b000 --- diff --git a/Makefile.in b/Makefile.in index a8d101b492..da75b76a59 100644 --- a/Makefile.in +++ b/Makefile.in @@ -630,7 +630,7 @@ FUZZCHECK_OPT += -DSQLITE_ENABLE_RTREE FUZZCHECK_OPT += -DSQLITE_ENABLE_GEOPOLY FUZZCHECK_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB FUZZCHECK_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB -FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c +FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c $(TOP)/test/fuzzinvariants.c DBFUZZ_OPT = # This is the default Makefile target. The objects listed here diff --git a/Makefile.msc b/Makefile.msc index d67b7f0b86..42da9b9e66 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -1705,7 +1705,7 @@ FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_GEOPOLY FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DBSTAT_VTAB FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB -FUZZCHECK_SRC = $(TOP)\test\fuzzcheck.c $(TOP)\test\ossfuzz.c +FUZZCHECK_SRC = $(TOP)\test\fuzzcheck.c $(TOP)\test\ossfuzz.c $(TOP)\test\fuzzinvariants.c OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c DBFUZZ_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION KV_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ diff --git a/main.mk b/main.mk index be16034e55..1926a08859 100644 --- a/main.mk +++ b/main.mk @@ -546,6 +546,9 @@ FUZZCHECK_OPT += -DSQLITE_ENABLE_RTREE FUZZCHECK_OPT += -DSQLITE_ENABLE_GEOPOLY FUZZCHECK_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB FUZZCHECK_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB +FUZZSRC += $(TOP)/test/fuzzcheck.c +FUZZSRC += $(TOP)/test/ossfuzz.c +FUZZSRC += $(TOP)/test/fuzzinvariants.c DBFUZZ_OPT = KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ ST_OPT = -DSQLITE_THREADSAFE=0 @@ -604,10 +607,10 @@ dbfuzz2$(EXE): $(TOP)/test/dbfuzz2.c sqlite3.c sqlite3.h $(TCCX) -I. -g -O0 -DSTANDALONE -o dbfuzz2$(EXE) \ $(DBFUZZ2_OPTS) $(TOP)/test/dbfuzz2.c sqlite3.c $(TLIBS) $(THREADLIB) -fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(TOP)/test/ossfuzz.c +fuzzcheck$(EXE): $(FUZZSRC) sqlite3.c sqlite3.h $(TCCX) -o fuzzcheck$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_ENABLE_MEMSYS5 $(FUZZCHECK_OPT) -DSQLITE_OSS_FUZZ \ - $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS) $(THREADLIB) + $(FUZZSRC) sqlite3.c $(TLIBS) $(THREADLIB) ossshell$(EXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h $(TCCX) -o ossshell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ diff --git a/manifest b/manifest index 0d7cb7378d..a4d83cb6ad 100644 --- a/manifest +++ b/manifest @@ -1,11 +1,11 @@ -C Do\snot\sremove\sthe\sEP_CanBeNull\sflag\sfrom\sexpressions\sduring\sa\sLEFT\sJOIN\nstrength\sreduction\sif\sthe\squery\salso\scontains\sa\sRIGHT\sJOIN.\sFix\sfor\nthe\sproblem\sidentified\sby\n[forum/forumpost/b40696f50145d21c|forum\spost\sb40696f50145d21c]. -D 2022-06-13T12:42:24.645 +C Attempt\sto\senhance\sfuzzcheck\sto\sdo\ssome\ssimple\sinvariant\stesting\son\squeries.\nThis\sis\san\sincremental\scheck-in\sfor\sa\swork-in-progress. +D 2022-06-14T19:12:25.257 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in e45046a016985c8fedfc5e7aad88ee396033a1e9b4e929fd6e1033e4c39dba95 +F Makefile.in bccb0ed3f05fc41aee15da77c844c48b5da419cbb9af35b8a147536c9ad1c822 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 -F Makefile.msc b28a8a7a977e7312f6859f560348e1eb110c21bd6cf9fab0d16537c0a514eef3 +F Makefile.msc de7cb3e095ce2fdc33513ccd76ebdaeda1483d0ddab0410fe65cbdeadd4c0ee1 F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e F VERSION fa8e7d2d1cc962f9e14c6d410387cf75860ee139462763fda887c1be4261f824 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@ -491,7 +491,7 @@ F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 80ac3d2f82eb21c6fb4423f9c7f8e207abb51be6676845a00e246cfb22f9c77c +F main.mk a5412510e5ec952915a7fda34e02079bb4e6ff8f97903f2a3d9ad2dee3e18044 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -1085,7 +1085,7 @@ F test/fuzz3.test 9c813e6613b837cb7a277b0383cd66bfa07042b4cf0317157c35852f30043c F test/fuzz4.test c229bcdb45518a89e1d208a21343e061503460ac69fae1539320a89f572eb634 F test/fuzz_common.tcl b7197de6ed1ee8250a4f82d67876f4561b42ee8cbbfc6160dcb66331bad3f830 F test/fuzz_malloc.test f348276e732e814802e39f042b1f6da6362a610af73a528d8f76898fde6b22f2 -F test/fuzzcheck.c e34696a5db46738118b2efd14fb71f8458ecf0f482df8bbae18fa1d64db9ab7b +F test/fuzzcheck.c 08d629c91b8307630273eb1e2e9a12bf0cf83fa2934e2b94681e11bf43819ef2 F test/fuzzdata1.db 3e86d9cf5aea68ddb8e27c02d7dfdaa226347426c7eb814918e4d95475bf8517 F test/fuzzdata2.db 128b3feeb78918d075c9b14b48610145a0dd4c8d6f1ca7c2870c7e425f5bf31f F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba @@ -1097,6 +1097,7 @@ F test/fuzzdata8.db ca9a97f401b06b0d5376139ec7e1f9e773e13345a9a2d9ccc0032cdbfede F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8 F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14 F test/fuzzerfault.test f64c4aef4c9e9edf1d6dc0d3f1e65dcc81e67c996403c88d14f09b74807a42bc +F test/fuzzinvariants.c 37b1f99fe4a6335983651b6b97a250dacf03b9ef81a189e2b52aeeace88ab1b1 F test/gcfault.test dd28c228a38976d6336a3fc42d7e5f1ad060cb8c F test/gencol1.test cc0dbb0ee116e5602e18ea7d47f2a0f76b26e09a823b7c36ef254370c2b0f3c1 F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98 @@ -1976,8 +1977,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 342c501f532523347e6c339351e02043dd6ee9e11a291224b65ea72bd6c2ba40 -R a249dd322f3d0191f7349eb16a9ed1fc +P b1be2259e2e08ec22a88bc9a18b3ab4d83246ad4c635c05cdf80d3eff84df06a +R d9a53a2b707897d91318ee68037f4638 +T *branch * query-invariant-tests +T *sym-query-invariant-tests * +T -sym-trunk * U drh -Z 7bab454819c3e667fed9b310a0634268 +Z 709cfc900b1da90b6bf9416298b653df # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7a1cda9eb4..0b38499e49 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b1be2259e2e08ec22a88bc9a18b3ab4d83246ad4c635c05cdf80d3eff84df06a \ No newline at end of file +ce2d780163b3a28486904860a1815acc4169c09b971cfd199bb58d1e9a57b000 \ No newline at end of file diff --git a/test/fuzzcheck.c b/test/fuzzcheck.c index 2ea0898efa..e9a80ddb60 100644 --- a/test/fuzzcheck.c +++ b/test/fuzzcheck.c @@ -830,6 +830,13 @@ static int progress_handler(void *pClientData) { return rc; } +/* +** Flag bits set by block_troublesome_sql() +*/ +#define BTS_SELECT 0x000001 +#define BTS_NONSELECT 0x000002 +#define BTS_BADFUNC 0x000004 + /* ** Disallow debugging pragmas such as "PRAGMA vdbe_debug" and ** "PRAGMA parser_trace" since they can dramatically increase the @@ -838,63 +845,121 @@ static int progress_handler(void *pClientData) { ** Also block ATTACH if attaching a file from the filesystem. */ static int block_troublesome_sql( - void *Notused, + void *pClientData, int eCode, const char *zArg1, const char *zArg2, const char *zArg3, const char *zArg4 ){ - (void)Notused; - (void)zArg2; + unsigned int *pFlags = (unsigned int*)pClientData; (void)zArg3; (void)zArg4; - if( eCode==SQLITE_PRAGMA ){ - if( sqlite3_stricmp("busy_timeout",zArg1)==0 - && (zArg2==0 || strtoll(zArg2,0,0)>100 || strtoll(zArg2,0,10)>100) - ){ - return SQLITE_DENY; - }else if( eVerbosity==0 ){ - if( sqlite3_strnicmp("vdbe_", zArg1, 5)==0 - || sqlite3_stricmp("parser_trace", zArg1)==0 - || sqlite3_stricmp("temp_store_directory", zArg1)==0 + switch( eCode ){ + case SQLITE_PRAGMA: { + if( sqlite3_stricmp("busy_timeout",zArg1)==0 + && (zArg2==0 || strtoll(zArg2,0,0)>100 || strtoll(zArg2,0,10)>100) ){ return SQLITE_DENY; + }else if( eVerbosity==0 ){ + 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( sqlite3_stricmp("oom",zArg1)==0 + && zArg2!=0 && zArg2[0]!=0 ){ + oomCounter = atoi(zArg2); + } + *pFlags |= BTS_NONSELECT; + break; + } + case SQLITE_ATTACH: { + /* Deny the ATTACH if it is attaching anything other than an in-memory + ** database. */ + *pFlags |= BTS_NONSELECT; + if( zArg1==0 ) return SQLITE_DENY; + if( strcmp(zArg1,":memory:")==0 ) return SQLITE_OK; + if( sqlite3_strglob("file:*[?]vfs=memdb", zArg1)==0 + && sqlite3_strglob("file:*[^/a-zA-Z0-9_.]*[?]vfs=memdb", zArg1)!=0 + ){ + return SQLITE_OK; + } + return SQLITE_DENY; + } + case SQLITE_SELECT: { + *pFlags |= BTS_SELECT; + break; + } + case SQLITE_FUNCTION: { + static const char *azBadFuncs[] = { + "random", + "randomblob", + "rtreedepth", + }; + int i; + for(i=0; i=4 ){ printf("RUNNING-SQL: [%s]\n", zSql); fflush(stdout); } + (*pBtsFlags) = 0; rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); if( rc==SQLITE_OK ){ + int nRow = 0; while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){ + nRow++; + if( (*pBtsFlags)==BTS_SELECT ){ + int iCnt = 0; + for(iCnt=0; iCnt<99999; iCnt++){ + rc = fuzz_invariant(db, pStmt, iCnt, nRow, &bCorrupt); + if( rc==SQLITE_DONE ) break; + if( eVerbosity>0 ){ + if( rc==SQLITE_OK ){ + printf("invariant-check: ok\n"); + }else if( rc==SQLITE_CORRUPT ){ + printf("invariant-check: failed due to database corruption\n"); + } + } + } + } if( eVerbosity>=5 ){ int j; for(j=0; j +#include +#include +#include + +/* Forward references */ +static char *fuzz_invariant_sql(sqlite3_stmt*, int); +static int sameValue(sqlite3_value*,sqlite3_value*); +static void reportInvariantFailed(sqlite3_stmt*,sqlite3_stmt*,int); + +/* +** Do an invariant check on pStmt. iCnt determines which invariant check to +** perform. The first check is iCnt==0. +** +** *pbCorrupt is a flag that, if true, indicates that the database file +** is known to be corrupt. A value of non-zero means "yes, the database +** is corrupt". A zero value means "we do not know whether or not the +** database is corrupt". The value might be set prior to entry, or this +** routine might set the value. +** +** Return values: +** +** SQLITE_OK This check was successful. +** +** SQLITE_DONE iCnt is out of range. +** +** SQLITE_CORRUPT The invariant failed, but the underlying database +** file is indicating that it is corrupt, which might +** be the cause of the malfunction. +** +** SQLITE_INTERNAL The invariant failed, and the database file is not +** corrupt. (This never happens because this function +** will call abort() following an invariant failure.) +** +** (other) Some other kind of error occurred. +*/ +int fuzz_invariant( + sqlite3 *db, /* The database connection */ + sqlite3_stmt *pStmt, /* Test statement stopped on an SQLITE_ROW */ + int iCnt, /* Invariant sequence number, starting at 0 */ + int iRow, /* The row number for pStmt */ + int *pbCorrupt /* IN/OUT: Flag indicating a corrupt database file */ +){ + char *zTest; + sqlite3_stmt *pTestStmt = 0; + int rc; + int i; + int nCol; + + if( *pbCorrupt ) return SQLITE_DONE; + zTest = fuzz_invariant_sql(pStmt, iCnt); + if( zTest==0 ) return SQLITE_DONE; + rc = sqlite3_prepare_v2(db, zTest, -1, &pTestStmt, 0); + sqlite3_free(zTest); + if( rc ){ + sqlite3_finalize(pTestStmt); + return rc; + } + nCol = sqlite3_column_count(pStmt); + for(i=0; i=nCol ) break; + } + if( rc!=SQLITE_ROW ){ + /* No matching output row found */ + sqlite3_stmt *pCk = 0; + rc = sqlite3_prepare_v2(db, "PRAGMA integrity_check", -1, &pCk, 0); + if( rc ){ + sqlite3_finalize(pCk); + sqlite3_finalize(pTestStmt); + return rc; + } + rc = sqlite3_step(pCk); + if( rc!=SQLITE_ROW + || sqlite3_column_text(pCk, 0)==0 + || strcmp((const char*)sqlite3_column_text(pCk,0),"ok")!=0 + ){ + *pbCorrupt = 1; + sqlite3_finalize(pCk); + sqlite3_finalize(pTestStmt); + return SQLITE_CORRUPT; + } + sqlite3_finalize(pCk); + reportInvariantFailed(pStmt, pTestStmt, iRow); + return SQLITE_INTERNAL; + } + sqlite3_finalize(pTestStmt); + return SQLITE_OK; +} + + +/* +** Generate SQL used to test a statement invariant. +** +** Return 0 if the iCnt is out of range. +*/ +static char *fuzz_invariant_sql(sqlite3_stmt *pStmt, int iCnt){ + const char *zIn; + size_t nIn; + const char *zAnd = "WHERE"; + int i; + sqlite3_str *pTest; + sqlite3_stmt *pBase = 0; + sqlite3 *db = sqlite3_db_handle(pStmt); + int rc; + + if( iCnt<0 || iCnt>0 ) return 0; + zIn = sqlite3_sql(pStmt); + if( zIn==0 ) return 0; + nIn = strlen(zIn); + while( nIn>0 && (isspace(zIn[nIn-1]) || zIn[nIn-1]==';') ) nIn--; + if( strchr(zIn, '?') ) return 0; + pTest = sqlite3_str_new(0); + sqlite3_str_appendf(pTest, "SELECT * FROM (%.*s)", (int)nIn, zIn); + rc = sqlite3_prepare_v2(db, sqlite3_str_value(pTest), -1, &pBase, 0); + if( rc ){ + sqlite3_finalize(pBase); + pBase = pStmt; + } + for(i=0; i