Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -1904,10 +1904,20 @@ xbin: threadtest5 sqlite3$(T.exe): shell.c sqlite3.c $(T.link) -o $@ \ shell.c sqlite3.c \ + $(CFLAGS.readline) $(SHELL_OPT) \ + $(LDFLAGS.libsqlite3) $(LDFLAGS.readline) + +# The following only works if CC=clang. Suggested command line: +# +# make clean sqlite3_fuzzer CC=clang +# +sqlite3_fuzzer$(T.exe): shell.c sqlite3.c + $(T.link) -DSQLITE_SHELL_FUZZER -fsanitize=fuzzer -o $@ \ + shell.c sqlite3.c \ $(CFLAGS.readline) $(SHELL_OPT) \ $(LDFLAGS.libsqlite3) $(LDFLAGS.readline) # # Build sqlite3$(T.exe) by default except in wasi-sdk builds. Yes, the Index: src/shell.c.in ================================================================== --- src/shell.c.in +++ src/shell.c.in @@ -244,10 +244,21 @@ # define SQLITE_CIO_NO_CLASSIFY # define SQLITE_CIO_NO_TRANSLATE # define SQLITE_CIO_NO_SETMODE # define SQLITE_CIO_NO_FLUSH #endif + +#ifdef SQLITE_SHELL_FUZZER +/* When running with -fsanitize=fuzzer, an "exit()" is really a longjmp +** back to the end of the LLVMFuzzerTestOneInput() routine. */ +# include +static jmp_buf shellSetJmpEnv; +static void shell_exit(int rc){ longjmp(shellSetJmpEnv, rc); } +#else +/* For normal builds, exit() is an exit() */ +static void shell_exit(int rc){ exit(rc); } +#endif #define eputz(z) sqlite3_fputs(z,stderr) #define sputz(fp,z) sqlite3_fputs(z,fp) /* True if the timer is enabled */ @@ -486,17 +497,19 @@ for(i=0; iacAwait[0] = c; p->zScannerAwaits = p->acAwait; } } +#ifndef SQLITE_SHELL_FUZZER /* Upon demand, derive the continuation prompt to display. */ static char *dynamicContinuePrompt(void){ if( continuePrompt[0]==0 || (dynPrompt.zScannerAwaits==0 && dynPrompt.inParenLevel == 0) ){ return continuePrompt; @@ -578,16 +592,17 @@ PROMPT_LEN_MAX-4); } } return dynPrompt.dynamicPrompt; } +#endif /* !defined(SQLITE_SHELL_FUZZER) */ #endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */ /* Indicate out-of-memory and exit. */ static void shell_out_of_memory(void){ eputz("Error: out of memory\n"); - exit(1); + shell_exit(1); } /* Check a pointer to see if it is NULL. If it is NULL, exit with an ** out-of-memory error. */ @@ -907,10 +922,11 @@ } #endif #undef STAT_CHR_SRC } +#ifndef SQLITE_SHELL_FUZZER /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() ** fails. @@ -944,10 +960,11 @@ break; } } return zLine; } +#endif /* !defined(SQLITE_SHELL_FUZZER) */ /* ** Retrieve a single line of input text. ** ** If in==0 then read from standard input and prompt before each line. @@ -959,11 +976,11 @@ ** ** The result is stored in space obtained from malloc() and must either ** be freed by the caller or else passed back into this routine via the ** zPrior argument for reuse. */ -#ifndef SQLITE_SHELL_FIDDLE +#if !defined(SQLITE_SHELL_FIDDLE) && !defined(SQLITE_SHELL_FUZZER) static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ char *zPrompt; char *zResult; if( in!=0 ){ zResult = local_getline(zPrior, in); @@ -1677,11 +1694,11 @@ char *zMsg; va_start(ap, zErrMsg); zMsg = sqlite3_vmprintf(zErrMsg, ap); va_end(ap); sqlite3_fprintf(stderr, "line %d: %s\n", p->lineno, zMsg); - exit(1); + shell_exit(1); } } /* ** SQL function: edit(VALUE) @@ -2241,18 +2258,20 @@ if( bSep ){ sqlite3_fputs(p->colSeparator, p->out); } } +#ifndef SQLITE_SHELL_FUZZER /* ** This routine runs when the user presses Ctrl-C */ static void interrupt_handler(int NotUsed){ UNUSED_PARAMETER(NotUsed); - if( ++seenInterrupt>1 ) exit(1); + if( ++seenInterrupt>1 ) shell_exit(1); if( globalDb ) sqlite3_interrupt(globalDb); } +#endif /* !defined(SQLITE_SHELL_FUZZER) */ #if (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) /* ** This routine runs for console events (e.g. Ctrl-C) on Win32 */ @@ -5603,18 +5622,18 @@ } if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ sqlite3_fprintf(stderr,"Error: unable to open database \"%s\": %s\n", zDbFilename, sqlite3_errmsg(p->db)); if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){ - exit(1); + shell_exit(1); } sqlite3_close(p->db); sqlite3_open(":memory:", &p->db); if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ sqlite3_fputs("Also: unable to open substitute in-memory database.\n", stderr); - exit(1); + shell_exit(1); }else{ sqlite3_fprintf(stderr, "Notice: using substitute in-memory database instead of \"%s\"\n", zDbFilename); } @@ -8830,11 +8849,11 @@ } }else #ifndef SQLITE_SHELL_FIDDLE if( c=='e' && cli_strncmp(azArg[0], "exit", n)==0 ){ - if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc); + if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) shell_exit(rc); rc = 2; }else #endif /* The ".explain" command is automatic now. It is largely pointless. It @@ -9795,11 +9814,11 @@ eputz("Usage: .nonce NONCE\n"); rc = 1; }else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){ sqlite3_fprintf(stderr,"line %d: incorrect nonce: \"%s\"\n", p->lineno, azArg[1]); - exit(1); + shell_exit(1); }else{ p->bSafeMode = 0; return 0; /* Return immediately to bypass the safe mode reset ** at the end of this procedure */ } @@ -12171,10 +12190,55 @@ zLine[nZ] = 0; return zLine; } #endif /* SQLITE_SHELL_FIDDLE */ +#ifdef SQLITE_SHELL_FUZZER +/* +** Global state variables used to store the fuzzer's input +*/ +static const char *aFuzzData; /* Fuzzer input */ +static size_t nFuzz; /* Bytes of content in aFuzzData */ +static size_t nFuzzUsed; /* Bytes read so far */ + +/* +** Alternate one_input_line() impl for when the CLI is being fuzzed. +** The input comes from the fuzzer. +*/ +static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ + char *zLine = 0; + size_t n; + size_t nPrior; + size_t nLine; + + UNUSED_PARAMETER(in); + UNUSED_PARAMETER(isContinuation); + if( nFuzz<=nFuzzUsed ) return 0; + for(n=nFuzzUsed; n0 && zLine[nLine]=='\n' ) nLine--; + if( nLine>0 && zLine[nLine]=='\r' ) nLine--; + zLine[nLine] = 0; + nFuzzUsed += n; + return zLine; +} +#endif /* SQLITE_SHELL_FUZZER */ + /* ** Read input from *in and process it. If *in==0 then input ** is interactive - the user is typing it it. Otherwise, input ** is coming from a file or device. A prompt is issued and history ** is saved only if input is interactive. An interrupt signal will @@ -12430,15 +12494,15 @@ p->in = sqlite3_fopen(sqliterc,"rb"); if( p->in ){ if( stdin_is_interactive ){ sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc); } - if( process_input(p) && bail_on_error ) exit(1); + if( process_input(p) && bail_on_error ) shell_exit(1); fclose(p->in); }else if( sqliterc_override!=0 ){ sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc); - if( bail_on_error ) exit(1); + if( bail_on_error ) shell_exit(1); } p->in = inSaved; p->lineno = savedLineno; sqlite3_free(zBuf); } @@ -12516,11 +12580,11 @@ if( showDetail ){ sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions); }else{ eputz("Use the -help option for additional information\n"); } - exit(0); + shell_exit(0); } /* ** Internal check: Verify that the SQLite is uninitialized. Print a ** error message if it is initialized. @@ -12587,11 +12651,11 @@ */ static char *cmdline_option_value(int argc, char **argv, int i){ if( i==argc ){ sqlite3_fprintf(stderr, "%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); - exit(1); + shell_exit(1); } return argv[i]; } static void sayAbnormalExit(void){ @@ -12607,10 +12671,13 @@ # endif #endif #ifdef SQLITE_SHELL_FIDDLE # define main fiddle_main +#endif +#ifdef SQLITE_SHELL_FUZZER +# define main fuzzer_main #endif #if SQLITE_SHELL_IS_UTF8 int SQLITE_CDECL main(int argc, char **argv){ #else @@ -12640,14 +12707,17 @@ char **argvToFree = 0; int argcToFree = 0; #endif setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ -#ifdef SQLITE_SHELL_FIDDLE +#if defined(SQLITE_SHELL_FIDDLE) stdin_is_interactive = 0; stdout_is_console = 1; data.wasm.zDefaultDbName = "/fiddle.sqlite3"; +#elif defined(SQLITE_SHELL_FUZZER) + stdin_is_interactive = 0; + stdout_is_console = 0; #else stdin_is_interactive = isatty(0); stdout_is_console = isatty(1); #endif atexit(sayAbnormalExit); @@ -12662,11 +12732,11 @@ "attach debugger to process %d and press ENTER to continue...", GETPID()); if( sqlite3_fgets(zLine, sizeof(zLine), stdin)!=0 && cli_strcmp(zLine,"stop")==0 ){ - exit(1); + shell_exit(1); } }else{ #if defined(_WIN32) || defined(WIN32) #if SQLITE_OS_WINRT __debugbreak(); @@ -12678,11 +12748,11 @@ #endif } } #endif /* Register a valid signal handler early, before much else is done. */ -#ifdef SIGINT +#if defined(SIGINT) && !defined(SQLITE_SHELL_FUZZER) signal(SIGINT, interrupt_handler); #elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) if( !SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE) ){ eputz("No ^C handler.\n"); } @@ -12691,11 +12761,11 @@ #if USE_SYSTEM_SQLITE+0!=1 if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ sqlite3_fprintf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", sqlite3_sourceid(), SQLITE_SOURCE_ID); - exit(1); + shell_exit(1); } #endif main_init(&data); /* On Windows, we must translate command-line arguments into UTF-8. @@ -12915,11 +12985,11 @@ sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs); if( pVfs ){ sqlite3_vfs_register(pVfs, 1); }else{ sqlite3_fprintf(stderr,"no such VFS: \"%s\"\n", zVfs); - exit(1); + shell_exit(1); } } if( data.pAuxDb->zDbFilename==0 ){ #ifndef SQLITE_OMIT_MEMORYDB @@ -13268,10 +13338,38 @@ shell_main_exit: #endif return rc; } +#ifdef SQLITE_SHELL_FUZZER +/* The fuzzer invokes this routine for each test case. aData[] contains +** text that is presumed input to the shell. +*/ +int LLVMFuzzerTestOneInput(const uint8_t *aData, size_t nByte){ + char *argv[4]; + enableTimer = 0; + bail_on_error = 0; + stdin_is_interactive = 0; + stdout_is_console = 0; + globalDb = 0; + seenInterrupt = 0; + Argv0 = 0; + argv[0] = "fuzzing-sqlite3"; + argv[1] = ":memory:"; + argv[2] = "--safe"; + argv[3] = 0; + aFuzzData = (const char*)aData; + nFuzz = nByte; + nFuzzUsed = 0; + if( setjmp(shellSetJmpEnv)==0 ){ + (void)fuzzer_main(3, argv); + } + sqlite3_shutdown(); + return 0; +} +#endif /* SQLITE_SHELL_FUZZER */ + #ifdef SQLITE_SHELL_FIDDLE /* Only for emcc experimentation purposes. */ int fiddle_experiment(int a,int b){ return a + b;