CoolFace
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
fuzzinvariants.c581 linesDownload Raw Back to test
1/*2** 2022-06-143**4** The author disclaims copyright to this source code.  In place of5** a legal notice, here is a blessing:6**7**    May you do good and not evil.8**    May you find forgiveness for yourself and forgive others.9**    May you share freely, never taking more than you give.10**11*************************************************************************12**13** This library is used by fuzzcheck to test query invariants.14**15** An sqlite3_stmt is passed in that has just returned SQLITE_ROW.  This16** routine does:17**18**     *   Record the output of the current row19**     *   Construct an alternative query that should return the same row20**     *   Run the alternative query and verify that it does in fact return21**         the same row22**23*/24#include "sqlite3.h"25#include <stdio.h>26#include <stdlib.h>27#include <string.h>28#include <ctype.h>29 30/* Forward references */31static char *fuzz_invariant_sql(sqlite3_stmt*, int);32static int sameValue(sqlite3_stmt*,int,sqlite3_stmt*,int,sqlite3_stmt*);33static void reportInvariantFailed(34  sqlite3_stmt *pOrig,   /* The original query */35  sqlite3_stmt *pTest,   /* The alternative test query with a missing row */36  int iRow,              /* Row number in pOrig */37  unsigned int dbOpt,    /* Optimization flags on pOrig */38  int noOpt              /* True if opt flags inverted for pTest */39);40 41/*42** Special parameter binding, for testing and debugging purposes.43**44**     $int_NNN        ->   integer value NNN45**     $text_TTTT      ->   floating point value TTT with destructor46**     $carray_clr     ->   First argument to carray() for color names47**     $carray_primes  ->   First argument to carray() for prime numbers48*/49static void bindDebugParameters(sqlite3_stmt *pStmt){50  int nVar = sqlite3_bind_parameter_count(pStmt);51  int i;52  for(i=1; i<=nVar; i++){53    const char *zVar = sqlite3_bind_parameter_name(pStmt, i);54    if( zVar==0 ) continue;55#ifdef SQLITE_ENABLE_CARRAY56    if( strcmp(zVar,"$carray_clr")==0 ){57      static char *azColorNames[] = {58        "azure", "black", "blue",   "brown", "cyan",   "fuchsia", "gold",59        "gray",  "green", "indigo", "khaki", "lime",   "magenta", "maroon",60        "navy",  "olive", "orange", "pink",  "purple", "red",     "silver",61        "tan",   "teal",  "violet", "white", "yellow"62      };63      sqlite3_carray_bind(pStmt,i,azColorNames,26,SQLITE_CARRAY_TEXT,0);64    }else65    if( strcmp(zVar,"$carray_primes")==0 ){66      static int aPrimes[] = {67        1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,68       53, 59, 61, 67, 71, 73, 79, 83, 89, 9769      };70      sqlite3_carray_bind(pStmt,i,aPrimes,26,SQLITE_CARRAY_INT32,0);71    }else72#endif73    if( strncmp(zVar, "$int_", 5)==0 ){74      sqlite3_bind_int(pStmt, i, atoi(&zVar[5]));75    }else76    if( strncmp(zVar, "$text_", 6)==0 ){77      size_t szVar = strlen(zVar);78      char *zBuf = sqlite3_malloc64( szVar-5 );79      if( zBuf ){80        memcpy(zBuf, &zVar[6], szVar-5);81        sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8);82      }83    }84  }85}86 87/*88** Do an invariant check on pStmt.  iCnt determines which invariant check to89** perform.  The first check is iCnt==0.90**91** *pbCorrupt is a flag that, if true, indicates that the database file92** is known to be corrupt.  A value of non-zero means "yes, the database93** is corrupt".  A zero value means "we do not know whether or not the94** database is corrupt".  The value might be set prior to entry, or this95** routine might set the value.96**97** Return values:98**99**     SQLITE_OK          This check was successful.100**101**     SQLITE_DONE        iCnt is out of range.  The caller typically sets102**                        up a loop on iCnt starting with zero, and increments103**                        iCnt until this code is returned.104**105**     SQLITE_CORRUPT     The invariant failed, but the underlying database106**                        file is indicating that it is corrupt, which might107**                        be the cause of the malfunction.  The *pCorrupt108**                        value will also be set.109**110**     SQLITE_INTERNAL    The invariant failed, and the database file is not111**                        corrupt.  (This never happens because this function112**                        will call abort() following an invariant failure.)113**114**     (other)            Some other kind of error occurred.115*/116int fuzz_invariant(117  sqlite3 *db,            /* The database connection */118  sqlite3_stmt *pStmt,    /* Test statement stopped on an SQLITE_ROW */119  int iCnt,               /* Invariant sequence number, starting at 0 */120  int iRow,               /* Current row number */121  int nRow,               /* Number of output rows from pStmt */122  int *pbCorrupt,         /* IN/OUT: Flag indicating a corrupt database file */123  int eVerbosity,         /* How much debugging output */124  unsigned int dbOpt      /* Default optimization flags */125){126  char *zTest;127  sqlite3_stmt *pTestStmt = 0;128  int rc;129  int i;130  int nCol;131  int nParam;132  int noOpt = (iCnt%3)==0;133 134  if( *pbCorrupt ) return SQLITE_DONE;135  nParam = sqlite3_bind_parameter_count(pStmt);136  if( nParam>100 ) return SQLITE_DONE;137  zTest = fuzz_invariant_sql(pStmt, iCnt);138  if( zTest==0 ) return SQLITE_DONE;139  if( noOpt ){140    sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, db, ~dbOpt);141  }142  rc = sqlite3_prepare_v2(db, zTest, -1, &pTestStmt, 0);143  if( noOpt ){144    sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, db, dbOpt);145  }146  if( rc ){147    if( eVerbosity ){148      printf("invariant compile failed: %s\n%s\n",149             sqlite3_errmsg(db), zTest);150    }151    sqlite3_free(zTest);152    sqlite3_finalize(pTestStmt);153    return rc;154  }155  sqlite3_free(zTest);156  bindDebugParameters(pTestStmt);157  nCol = sqlite3_column_count(pStmt);158  for(i=0; i<nCol; i++){159    rc = sqlite3_bind_value(pTestStmt,i+1+nParam,sqlite3_column_value(pStmt,i));160    if( rc!=SQLITE_OK && rc!=SQLITE_RANGE ){161      sqlite3_finalize(pTestStmt);162      return rc;163    }164  }165  if( eVerbosity>=2 ){166    char *zSql = sqlite3_expanded_sql(pTestStmt);167    printf("invariant-sql row=%d #%d:\n%s\n", iRow, iCnt, zSql);168    sqlite3_free(zSql);169  }170  while( (rc = sqlite3_step(pTestStmt))==SQLITE_ROW ){171    for(i=0; i<nCol; i++){172      if( !sameValue(pStmt, i, pTestStmt, i, 0) ) break;173    }174    if( i>=nCol ) break;175  }176  if( rc==SQLITE_DONE ){177    /* No matching output row found */178    sqlite3_stmt *pCk = 0;179    int iOrigRSO;180 181 182    /* This is not a fault if the database file is corrupt, because anything183    ** can happen with a corrupt database file */184    rc = sqlite3_prepare_v2(db, "PRAGMA integrity_check", -1, &pCk, 0);185    if( rc ){186      sqlite3_finalize(pCk);187      sqlite3_finalize(pTestStmt);188      return rc;189    }190    if( eVerbosity>=2 ){191      char *zSql = sqlite3_expanded_sql(pCk);192      printf("invariant-validity-check #1:\n%s\n", zSql);193      sqlite3_free(zSql);194    }195 196    rc = sqlite3_step(pCk);197    if( rc!=SQLITE_ROW198     || sqlite3_column_text(pCk, 0)==0199     || strcmp((const char*)sqlite3_column_text(pCk,0),"ok")!=0200    ){201      *pbCorrupt = 1;202      sqlite3_finalize(pCk);203      sqlite3_finalize(pTestStmt);204      return SQLITE_CORRUPT;205    }206    sqlite3_finalize(pCk);207 208    /*209    ** If inverting the scan order also results in a miss, assume that the210    ** query is ambiguous and do not report a fault.211    */212    sqlite3_db_config(db, SQLITE_DBCONFIG_REVERSE_SCANORDER, -1, &iOrigRSO);213    sqlite3_db_config(db, SQLITE_DBCONFIG_REVERSE_SCANORDER, !iOrigRSO, 0);214    sqlite3_prepare_v2(db, sqlite3_sql(pStmt), -1, &pCk, 0);215    sqlite3_db_config(db, SQLITE_DBCONFIG_REVERSE_SCANORDER, iOrigRSO, 0);216    if( eVerbosity>=2 ){217      char *zSql = sqlite3_expanded_sql(pCk);218      printf("invariant-validity-check #2:\n%s\n", zSql);219      sqlite3_free(zSql);220    }221    bindDebugParameters(pCk);222    while( (rc = sqlite3_step(pCk))==SQLITE_ROW ){223      for(i=0; i<nCol; i++){224        if( !sameValue(pStmt, i, pTestStmt, i, 0) ) break;225      }226      if( i>=nCol ) break;227    }228    sqlite3_finalize(pCk);229    if( rc==SQLITE_DONE ){230      sqlite3_finalize(pTestStmt);231      return SQLITE_DONE;232    }233 234    /* The original sameValue() comparison assumed a collating sequence235    ** of "binary".  It can sometimes get an incorrect result for different236    ** collating sequences.  So rerun the test with no assumptions about237    ** collations.238    */239    rc = sqlite3_prepare_v2(db,240       "SELECT ?1=?2 OR ?1=?2 COLLATE nocase OR ?1=?2 COLLATE rtrim",241       -1, &pCk, 0);242    if( rc==SQLITE_OK ){243      if( eVerbosity>=2 ){244        char *zSql = sqlite3_expanded_sql(pCk);245        printf("invariant-validity-check #3:\n%s\n", zSql);246        sqlite3_free(zSql);247      }248 249      sqlite3_reset(pTestStmt);250      bindDebugParameters(pCk);251      while( (rc = sqlite3_step(pTestStmt))==SQLITE_ROW ){252        for(i=0; i<nCol; i++){253          if( !sameValue(pStmt, i, pTestStmt, i, pCk) ) break;254        }255        if( i>=nCol ){256          sqlite3_finalize(pCk);257          goto not_a_fault;258        }259      }260    }261    sqlite3_finalize(pCk);262 263    /* Invariants do not necessarily work if there are virtual tables264    ** involved in the query */265    rc = sqlite3_prepare_v2(db, 266            "SELECT 1 FROM bytecode(?1) WHERE opcode='VOpen'", -1, &pCk, 0);267    if( rc==SQLITE_OK ){268      if( eVerbosity>=2 ){269        char *zSql = sqlite3_expanded_sql(pCk);270        printf("invariant-validity-check #4:\n%s\n", zSql);271        sqlite3_free(zSql);272      }273      sqlite3_bind_pointer(pCk, 1, pStmt, "stmt-pointer", 0);274      rc = sqlite3_step(pCk);275    }276    sqlite3_finalize(pCk);277    if( rc==SQLITE_DONE ){278      reportInvariantFailed(pStmt, pTestStmt, iRow, dbOpt, noOpt);279      return SQLITE_INTERNAL;280    }else if( eVerbosity>0 ){281      printf("invariant-error ignored due to the use of virtual tables\n");282    }283  }284not_a_fault:285  sqlite3_finalize(pTestStmt);286  return SQLITE_OK;287}288 289/*290** Generate SQL used to test a statement invariant.291**292** Return 0 if the iCnt is out of range.293**294** iCnt meanings:295**296**   0     SELECT * FROM (<query>)297**   1     SELECT DISTINCT * FROM (<query>)298**   2     SELECT * FROM (<query>) WHERE ORDER BY 1299**   3     SELECT DISTINCT * FROM (<query>) ORDER BY 1300**   4     SELECT * FROM (<query>) WHERE <all-columns>=<all-values>301**   5     SELECT DISTINCT * FROM (<query>) WHERE <all-columns=<all-values302**   6     SELECT * FROM (<query>) WHERE <all-column>=<all-value> ORDER BY 1303**   7     SELECT DISTINCT * FROM (<query>) WHERE <all-column>=<all-value>304**                           ORDER BY 1305**   N+0   SELECT * FROM (<query>) WHERE <nth-column>=<value>306**   N+1   SELECT DISTINCT * FROM (<query>) WHERE <Nth-column>=<value>307**   N+2   SELECT * FROM (<query>) WHERE <Nth-column>=<value> ORDER BY 1308**   N+3   SELECT DISTINCT * FROM (<query>) WHERE <Nth-column>=<value>309**                           ORDER BY N310**311*/312static char *fuzz_invariant_sql(sqlite3_stmt *pStmt, int iCnt){313  const char *zIn;314  size_t nIn;315  const char *zAnd = "WHERE";316  int i, j;317  sqlite3_str *pTest;318  sqlite3_stmt *pBase = 0;319  sqlite3 *db = sqlite3_db_handle(pStmt);320  int rc;321  int nCol = sqlite3_column_count(pStmt);322  int mxCnt;323  int bDistinct = 0;324  int bOrderBy = 0;325  int nParam = sqlite3_bind_parameter_count(pStmt);326  int hasGroupBy = 0;327 328  switch( iCnt % 4 ){329    case 1:  bDistinct = 1;              break;330    case 2:  bOrderBy = 1;               break;331    case 3:  bDistinct = bOrderBy = 1;   break;332  }333  iCnt /= 4;334  mxCnt = nCol;335  if( iCnt<0 || iCnt>mxCnt ) return 0;336  zIn = sqlite3_sql(pStmt);337  if( zIn==0 ) return 0;338  nIn = strlen(zIn);339  while( nIn>0 && (isspace(zIn[nIn-1]) || zIn[nIn-1]==';') ) nIn--;340  if( strchr(zIn, '?') ) return 0;341  pTest = sqlite3_str_new(0);342  sqlite3_str_appendf(pTest, "SELECT %s* FROM (",  343                      bDistinct ? "DISTINCT " : "");344  sqlite3_str_append(pTest, zIn, (int)nIn);345  sqlite3_str_append(pTest, ")", 1);346  rc = sqlite3_prepare_v2(db, sqlite3_str_value(pTest), -1, &pBase, 0);347  if( rc ){348    sqlite3_finalize(pBase);349    pBase = pStmt;350  }351  hasGroupBy = sqlite3_strlike("%GROUP BY%",zIn,0)==0;352  bindDebugParameters(pBase);353  for(i=0; i<sqlite3_column_count(pStmt); i++){354    const char *zColName = sqlite3_column_name(pBase,i);355    const char *zSuffix = zColName ? strrchr(zColName, ':') : 0;356    if( zSuffix 357     && isdigit(zSuffix[1])358     && (zSuffix[1]>'3' || isdigit(zSuffix[2]))359    ){360      /* This is a randomized column name and so cannot be used in the361      ** WHERE clause. */362      continue;363    }364    for(j=0; j<i; j++){365      const char *zPrior = sqlite3_column_name(pBase, j);366      if( sqlite3_stricmp(zPrior, zColName)==0 ) break;367    }368    if( j<i ){369      /* Duplicate column name */370      continue;371    }372    if( iCnt==0 ) continue;373    if( iCnt>1 && i+2!=iCnt ) continue;374    if( zColName==0 ) continue;375    if( sqlite3_column_type(pStmt, i)==SQLITE_NULL ){376      const char *zPlus = hasGroupBy ? "+" : "";377      sqlite3_str_appendf(pTest, " %s %s\"%w\" ISNULL", zAnd, zPlus, zColName);378    }else{379      sqlite3_str_appendf(pTest, " %s \"%w\"=?%d", zAnd, zColName, 380                          i+1+nParam);381    }382    zAnd = "AND";383  }384  if( pBase!=pStmt ) sqlite3_finalize(pBase);385  if( bOrderBy ){386    sqlite3_str_appendf(pTest, " ORDER BY %d", iCnt>2 ? iCnt-1 : 1);387  }388  return sqlite3_str_finish(pTest);389}390 391/*392** Return true if and only if v1 and is the same as v2.393*/394static int sameValue(395  sqlite3_stmt *pS1, int i1,       /* Value to text on the left */396  sqlite3_stmt *pS2, int i2,       /* Value to test on the right */397  sqlite3_stmt *pTestCompare       /* COLLATE comparison statement or NULL */398){399  int x = 1;400  int t1 = sqlite3_column_type(pS1,i1);401  int t2 = sqlite3_column_type(pS2,i2);402  if( t1!=t2 ){403    if( (t1==SQLITE_INTEGER && t2==SQLITE_FLOAT)404     || (t1==SQLITE_FLOAT && t2==SQLITE_INTEGER)405    ){406      /* Comparison of numerics is ok */407    }else{408      return 0;409    }410  }411  switch( sqlite3_column_type(pS1,i1) ){412    case SQLITE_INTEGER: {413      x =  sqlite3_column_int64(pS1,i1)==sqlite3_column_int64(pS2,i2);414      break;415    }416    case SQLITE_FLOAT: {417      x = sqlite3_column_double(pS1,i1)==sqlite3_column_double(pS2,i2);418      break;419    }420    case SQLITE_TEXT: {421      int e1 = sqlite3_value_encoding(sqlite3_column_value(pS1,i1));422      int e2 = sqlite3_value_encoding(sqlite3_column_value(pS2,i2));423      if( e1!=e2 ){424        const char *z1 = (const char*)sqlite3_column_text(pS1,i1);425        const char *z2 = (const char*)sqlite3_column_text(pS2,i2);426        x = ((z1==0 && z2==0) || (z1!=0 && z2!=0 && strcmp(z1,z1)==0));427        printf("Encodings differ.  %d on left and %d on right\n", e1, e2);428        abort();429      }430      if( pTestCompare ){431        sqlite3_bind_value(pTestCompare, 1, sqlite3_column_value(pS1,i1));432        sqlite3_bind_value(pTestCompare, 2, sqlite3_column_value(pS2,i2));433        x = sqlite3_step(pTestCompare)==SQLITE_ROW434                      && sqlite3_column_int(pTestCompare,0)!=0;435        sqlite3_reset(pTestCompare);436        break;437      }438      if( e1!=SQLITE_UTF8 ){439        int len1 = sqlite3_column_bytes16(pS1,i1);440        const unsigned char *b1 = sqlite3_column_blob(pS1,i1);441        int len2 = sqlite3_column_bytes16(pS2,i2);442        const unsigned char *b2 = sqlite3_column_blob(pS2,i2);443        if( len1!=len2 ){444          x = 0;445        }else if( len1==0 ){446          x = 1;447        }else{448          x = (b1!=0 && b2!=0 && memcmp(b1,b2,len1)==0);449        }450        break;451      }452      /* Fall through into the SQLITE_BLOB case */453    }454    case SQLITE_BLOB: {455      int len1 = sqlite3_column_bytes(pS1,i1);456      const unsigned char *b1 = sqlite3_column_blob(pS1,i1);457      int len2 = sqlite3_column_bytes(pS2,i2);458      const unsigned char *b2 = sqlite3_column_blob(pS2,i2);459      if( len1!=len2 ){460        x = 0;461      }else if( len1==0 ){462        x = 1;463      }else{464        x = (b1!=0 && b2!=0 && memcmp(b1,b2,len1)==0);465      }466      break;467    }468  }469  return x;470}471 472/*473** Print binary data as hex474*/475static void printHex(const unsigned char *a, int n, int mx){476  int j;477  for(j=0; j<mx && j<n; j++){478    printf("%02x", a[j]);479  }480  if( j<n ) printf("...");481}482 483/*484** Print a single row from the prepared statement485*/486static void printRow(sqlite3_stmt *pStmt, int iRow){487  int i, n, nCol;488  unsigned const char *data;489  nCol = sqlite3_column_count(pStmt);490  for(i=0; i<nCol; i++){491    printf("row%d.col%d = ", iRow, i);492    switch( sqlite3_column_type(pStmt, i) ){493      case SQLITE_NULL: {494        printf("NULL\n");495        break;496      }497      case SQLITE_INTEGER: {498        printf("(integer) %lld\n", sqlite3_column_int64(pStmt, i));499        break;500      }501      case SQLITE_FLOAT: {502        printf("(float) %f\n", sqlite3_column_double(pStmt, i));503        break;504      }505      case SQLITE_TEXT: {506        switch( sqlite3_value_encoding(sqlite3_column_value(pStmt,i)) ){507          case SQLITE_UTF8: {508            printf("(utf8) x'");509            n = sqlite3_column_bytes(pStmt, i);510            data = sqlite3_column_blob(pStmt, i);511            printHex(data, n, 35);512            printf("'\n");513            break;514          }515          case SQLITE_UTF16BE: {516            printf("(utf16be) x'");517            n = sqlite3_column_bytes16(pStmt, i);518            data = sqlite3_column_blob(pStmt, i);519            printHex(data, n, 35);520            printf("'\n");521            break;522          }523          case SQLITE_UTF16LE: {524            printf("(utf16le) x'");525            n = sqlite3_column_bytes16(pStmt, i);526            data = sqlite3_column_blob(pStmt, i);527            printHex(data, n, 35);528            printf("'\n");529            break;530          }531          default: {532            printf("Illegal return from sqlite3_value_encoding(): %d\n",533                sqlite3_value_encoding(sqlite3_column_value(pStmt,i)));534            abort();535          }536        }537        break;538      }539      case SQLITE_BLOB: {540        n = sqlite3_column_bytes(pStmt, i);541        data = sqlite3_column_blob(pStmt, i);542        printf("(blob %d bytes) x'", n);543        printHex(data, n, 35);544        printf("'\n");545        break;546      }547    }548  }549}550 551/*552** Report a failure of the invariant:  The current output row of pOrig553** does not appear in any row of the output from pTest.554*/555static void reportInvariantFailed(556  sqlite3_stmt *pOrig,   /* The original query */557  sqlite3_stmt *pTest,   /* The alternative test query with a missing row */558  int iRow,              /* Row number in pOrig */559  unsigned int dbOpt,    /* Optimization flags on pOrig */560  int noOpt              /* True if opt flags inverted for pTest */561){562  int iTestRow = 0;563  printf("Invariant check failed on row %d.\n", iRow);564  printf("Original query (opt-flags: 0x%08x) --------------------------\n",565         dbOpt);566  printf("%s\n", sqlite3_expanded_sql(pOrig));567  printf("Alternative query (opt-flags: 0x%08x) -----------------------\n",568         noOpt ? ~dbOpt : dbOpt);569  printf("%s\n", sqlite3_expanded_sql(pTest));570  printf("Result row that is missing from the alternative -----------------\n");571  printRow(pOrig, iRow);572  printf("Complete results from the alternative query ---------------------\n");573  sqlite3_reset(pTest);574  while( sqlite3_step(pTest)==SQLITE_ROW ){575    iTestRow++;576    printRow(pTest, iTestRow);577  }578  sqlite3_finalize(pTest);579  abort();580}581