1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

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
This commit is contained in:
drh
2022-06-14 19:12:25 +00:00
parent 92d1afba9b
commit a1f79dae98
7 changed files with 385 additions and 43 deletions

View File

@ -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);
}
}else if( sqlite3_stricmp("oom",zArg1)==0
&& zArg2!=0 && zArg2[0]!=0 ){
oomCounter = atoi(zArg2);
*pFlags |= BTS_NONSELECT;
break;
}
}else if( eCode==SQLITE_ATTACH ){
/* Deny the ATTACH if it is attaching anything other than an in-memory
** database. */
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;
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<sizeof(azBadFuncs)/sizeof(azBadFuncs[0]); i++){
if( sqlite3_stricmp(azBadFuncs[i], zArg2)==0 ){
*pFlags |= BTS_BADFUNC;
break;
}
}
break;
}
case SQLITE_READ: {
/* Benign */
break;
}
default: {
*pFlags |= BTS_NONSELECT;
}
return SQLITE_DENY;
}
return SQLITE_OK;
}
/* Implementation found in fuzzinvariant.c */
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 */
);
/*
** Run the SQL text
*/
static int runDbSql(sqlite3 *db, const char *zSql){
static int runDbSql(sqlite3 *db, const char *zSql, unsigned int *pBtsFlags){
int rc;
sqlite3_stmt *pStmt;
int bCorrupt = 0;
while( isspace(zSql[0]&0x7f) ) zSql++;
if( zSql[0]==0 ) return SQLITE_OK;
if( eVerbosity>=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<sqlite3_column_count(pStmt); j++){
@ -971,6 +1036,7 @@ int runCombinedDbSqlInput(
char *zSql = 0; /* SQL text to run */
int nSql; /* Bytes of SQL text */
FuzzCtx cx; /* Fuzzing context */
unsigned int btsFlags = 0; /* Parsing flags */
if( nByte<10 ) return 0;
if( sqlite3_initialize() ) return 0;
@ -1058,7 +1124,7 @@ int runCombinedDbSqlInput(
/* 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);
sqlite3_set_authorizer(cx.db, block_troublesome_sql, &btsFlags);
#ifdef VT02_SOURCES
sqlite3_vt02_init(cx.db, 0, 0);
@ -1083,7 +1149,7 @@ int runCombinedDbSqlInput(
char cSaved = zSql[i+1];
zSql[i+1] = 0;
if( sqlite3_complete(zSql+j) ){
rc = runDbSql(cx.db, zSql+j);
rc = runDbSql(cx.db, zSql+j, &btsFlags);
j = i+1;
}
zSql[i+1] = cSaved;
@ -1093,7 +1159,7 @@ int runCombinedDbSqlInput(
}
}
if( j<i ){
runDbSql(cx.db, zSql+j);
runDbSql(cx.db, zSql+j, &btsFlags);
}
}
testrun_finished: