modernc.org/cc@v1.0.1/v2/testdata/_sqlite/test/corruptC.test (about)

     1  # 2004 August 30
     2  #
     3  # The author disclaims copyright to this source code.  In place of
     4  # a legal notice, here is a blessing:
     5  #
     6  #    May you do good and not evil.
     7  #    May you find forgiveness for yourself and forgive others.
     8  #    May you share freely, never taking more than you give.
     9  #
    10  #***********************************************************************
    11  # This file implements regression tests for SQLite library.
    12  #
    13  # This file implements tests to make sure SQLite does not crash or
    14  # segfault if it sees a corrupt database file.  It creates a base
    15  # data base file, then tests that single byte corruptions in 
    16  # increasingly larger quantities are handled gracefully.
    17  #
    18  # $Id: corruptC.test,v 1.14 2009/07/11 06:55:34 danielk1977 Exp $
    19  
    20  catch {forcedelete test.db test.db-journal test.bu}
    21  
    22  set testdir [file dirname $argv0]
    23  source $testdir/tester.tcl
    24  
    25  # Do not use a codec for tests in this file, as the database file is
    26  # manipulated directly using tcl scripts (using the [hexio_write] command).
    27  #
    28  do_not_use_codec
    29  
    30  # These tests deal with corrupt database files
    31  #
    32  database_may_be_corrupt
    33  
    34  # Construct a compact, dense database for testing.
    35  #
    36  do_test corruptC-1.1 {
    37    execsql {
    38      PRAGMA auto_vacuum = 0;
    39      PRAGMA legacy_file_format=1;
    40      BEGIN;
    41      CREATE TABLE t1(x,y);
    42      INSERT INTO t1 VALUES(1,1);
    43      INSERT OR IGNORE INTO t1 SELECT x*2,y FROM t1;
    44      INSERT OR IGNORE INTO t1 SELECT x*3,y FROM t1;
    45      INSERT OR IGNORE INTO t1 SELECT x*5,y FROM t1;
    46      INSERT OR IGNORE INTO t1 SELECT x*7,y FROM t1;
    47      INSERT OR IGNORE INTO t1 SELECT x*11,y FROM t1;
    48      INSERT OR IGNORE INTO t1 SELECT x*13,y FROM t1;
    49      CREATE INDEX t1i1 ON t1(x);
    50      CREATE TABLE t2 AS SELECT x,2 as y FROM t1 WHERE rowid%5!=0;
    51      COMMIT;
    52    }
    53  } {}
    54  
    55  ifcapable {integrityck} {
    56    integrity_check corruptC-1.2
    57  }
    58  
    59  # Generate random integer
    60  #
    61  proc random {range} {
    62    return [expr {round(rand()*$range)}]
    63  }
    64  
    65  # Setup for the tests.  Make a backup copy of the good database in test.bu.
    66  #
    67  db close
    68  forcecopy test.db test.bu
    69  sqlite3 db test.db
    70  set fsize [file size test.db]
    71  
    72  # Set a quasi-random random seed. 
    73  if {[info exists ::G(issoak)]} {
    74    # If we are doing SOAK tests, we want a different
    75    # random seed for each run.  Ideally we would like 
    76    # to use [clock clicks] or something like that here.
    77    set qseed [file mtime test.db]
    78  } else {
    79    # If we are not doing soak tests,
    80    # make it repeatable.
    81    set qseed 0
    82  }
    83  expr srand($qseed)
    84  
    85  #
    86  # First test some specific corruption tests found from earlier runs
    87  # with specific seeds.
    88  #
    89  
    90  # test that a corrupt content offset size is handled (seed 5577)
    91  do_test corruptC-2.1 {
    92    db close
    93    forcecopy test.bu test.db
    94  
    95    # insert corrupt byte(s)
    96    hexio_write test.db 2053 [format %02x 0x04]
    97  
    98    sqlite3 db test.db
    99    catchsql {PRAGMA integrity_check}
   100  } {1 {database disk image is malformed}}
   101  
   102  # test that a corrupt content offset size is handled (seed 5649)
   103  #
   104  # Update 2016-12-27:  As of check-in [0b86fbca66] "In sqlite3BtreeInsert() when
   105  # replacing a re-existing row, try to overwrite the cell directly rather than
   106  # deallocate and reallocate the cell" on 2016-12-09, this test case no longer
   107  # detects the offset size problem during the UPDATE.  We have to run a subsequent
   108  # integrity_check to see it.
   109  do_test corruptC-2.2 {
   110    db close
   111    forcecopy test.bu test.db
   112  
   113    # insert corrupt byte(s)
   114    hexio_write test.db 27   [format %02x 0x08]
   115    hexio_write test.db 233  [format %02x 0x6a]
   116    hexio_write test.db 328  [format %02x 0x67]
   117    hexio_write test.db 750  [format %02x 0x1f]
   118    hexio_write test.db 1132 [format %02x 0x52]
   119    hexio_write test.db 1133 [format %02x 0x84]
   120    hexio_write test.db 1220 [format %02x 0x01]
   121    hexio_write test.db 3688 [format %02x 0xc1]
   122    hexio_write test.db 3714 [format %02x 0x58]
   123    hexio_write test.db 3746 [format %02x 0x9a]
   124  
   125    sqlite3 db test.db
   126    db eval {UPDATE t1 SET y=1}
   127    db eval {PRAGMA integrity_check}
   128  } {/Offset .* out of range/}
   129  
   130  # test that a corrupt free cell size is handled (seed 13329)
   131  do_test corruptC-2.3 {
   132    db close
   133    forcecopy test.bu test.db
   134  
   135    # insert corrupt byte(s)
   136    hexio_write test.db 1094 [format %02x 0x76]
   137  
   138    sqlite3 db test.db
   139    catchsql {UPDATE t1 SET y=1}
   140  } {1 {database disk image is malformed}}
   141  
   142  # test that a corrupt free cell size is handled (seed 169571)
   143  do_test corruptC-2.4 {
   144    db close
   145    forcecopy test.bu test.db
   146  
   147    # insert corrupt byte(s)
   148    hexio_write test.db 3119 [format %02x 0xdf]
   149  
   150    sqlite3 db test.db
   151    catchsql {UPDATE t2 SET y='abcdef-uvwxyz'}
   152  } {1 {database disk image is malformed}}
   153  
   154  # test that a corrupt free cell size is handled (seed 169571)
   155  do_test corruptC-2.5 {
   156    db close
   157    forcecopy test.bu test.db
   158  
   159    # insert corrupt byte(s)
   160    hexio_write test.db 3119 [format %02x 0xdf]
   161    hexio_write test.db 4073 [format %02x 0xbf]
   162  
   163    sqlite3 db test.db
   164    catchsql {BEGIN; UPDATE t2 SET y='abcdef-uvwxyz'; ROLLBACK;}
   165    catchsql {PRAGMA integrity_check}
   166  } {0 {{*** in database main ***
   167  On tree page 4 cell 19: Extends off end of page} {database disk image is malformed}}}
   168  
   169  # {0 {{*** in database main ***
   170  # Corruption detected in cell 710 on page 4
   171  # Multiple uses for byte 661 of page 4
   172  # Fragmented space is 249 byte reported as 21 on page 4}}}
   173  
   174  # test that a corrupt free cell size is handled (seed 169595)
   175  do_test corruptC-2.6 {
   176    db close
   177    forcecopy test.bu test.db
   178  
   179    # insert corrupt byte(s)
   180    hexio_write test.db 619 [format %02x 0xe2]
   181    hexio_write test.db 3150 [format %02x 0xa8]
   182  
   183    sqlite3 db test.db
   184    catchsql {BEGIN; UPDATE t2 SET y='abcdef-uvwxyz'; ROLLBACK;}
   185  } {1 {database disk image is malformed}}
   186  
   187  # corruption (seed 178692)
   188  do_test corruptC-2.7 {
   189    db close
   190    forcecopy test.bu test.db
   191  
   192    # insert corrupt byte(s)
   193    hexio_write test.db 3074 [format %02x 0xa0]
   194  
   195    sqlite3 db test.db
   196    catchsql {BEGIN; UPDATE t2 SET y='abcdef-uvwxyz'; ROLLBACK;}
   197  } {1 {database disk image is malformed}}
   198  
   199  
   200  # corruption (seed 179069)
   201  # Obsolete.  With single-pass DELETE the corruption in the
   202  # main database is not detected.
   203  if 0 {
   204  do_test corruptC-2.8 {
   205    db close
   206    forcecopy test.bu test.db
   207  
   208    # insert corrupt byte(s)
   209    hexio_write test.db 1393 [format %02x 0x7d]
   210    hexio_write test.db 84 [format %02x 0x19]
   211    hexio_write test.db 3287 [format %02x 0x3b]
   212    hexio_write test.db 2564 [format %02x 0xed]
   213    hexio_write test.db 2139 [format %02x 0x55]
   214  
   215    sqlite3 db test.db
   216    catchsql {BEGIN; DELETE FROM t1 WHERE x>13; ROLLBACK;}
   217  } {1 {database disk image is malformed}}
   218  }
   219  
   220  # corruption (seed 170434)
   221  #
   222  # UPDATE: Prior to 3.8.2, this used to return SQLITE_CORRUPT. It no longer
   223  # does. That is Ok, the point of these tests is to verify that no buffer
   224  # overruns or overreads can be caused by corrupt databases.
   225  do_test corruptC-2.9 {
   226    db close
   227    forcecopy test.bu test.db
   228  
   229    # insert corrupt byte(s)
   230    hexio_write test.db 2095 [format %02x 0xd6]
   231  
   232    sqlite3 db test.db
   233    catchsql {BEGIN; DELETE FROM t1 WHERE x>13; ROLLBACK;}
   234  } {0 {}}
   235  
   236  # corruption (seed 186504)
   237  do_test corruptC-2.10 {
   238    db close
   239    forcecopy test.bu test.db
   240  
   241    # insert corrupt byte(s)
   242    hexio_write test.db 3130 [format %02x 0x02]
   243    
   244    sqlite3 db test.db
   245    catchsql {BEGIN; UPDATE t2 SET y='abcdef-uvwxyz'; ROLLBACK;}
   246  } {1 {database disk image is malformed}}
   247  
   248  # corruption (seed 1589)
   249  do_test corruptC-2.11 {
   250    db close
   251    forcecopy test.bu test.db
   252  
   253    # insert corrupt byte(s)
   254    hexio_write test.db 55 [format %02x 0xa7]
   255    
   256    sqlite3 db test.db
   257    catchsql {BEGIN; CREATE TABLE t3 AS SELECT x,3 as y FROM t2 WHERE rowid%5!=0; ROLLBACK;}
   258  } {1 {database disk image is malformed}}
   259  
   260  # corruption (seed 14166)
   261  do_test corruptC-2.12 {
   262    db close
   263    forcecopy test.bu test.db
   264  
   265    # insert corrupt byte(s)
   266    hexio_write test.db 974 [format %02x 0x2e]
   267    
   268    sqlite3 db test.db
   269    catchsql {SELECT count(*) FROM sqlite_master;}
   270  } {1 {malformed database schema (t1i1) - corrupt database}}
   271  
   272  # corruption (seed 218803)
   273  do_test corruptC-2.13 {
   274    db close
   275    forcecopy test.bu test.db
   276  
   277    # insert corrupt byte(s)
   278    hexio_write test.db 102 [format %02x 0x12]
   279    
   280    sqlite3 db test.db
   281    catchsql {BEGIN; CREATE TABLE t3 AS SELECT x,3 as y FROM t2 WHERE rowid%5!=0; ROLLBACK;}
   282  } {1 {database disk image is malformed}}
   283  
   284  do_test corruptC-2.14 {
   285    db close
   286    forcecopy test.bu test.db
   287  
   288    sqlite3 db test.db
   289    set blob [string repeat abcdefghij 10000]
   290    execsql { INSERT INTO t1 VALUES (1, $blob) }
   291  
   292    sqlite3 db test.db
   293    set filesize [file size test.db]
   294    hexio_write test.db [expr $filesize-2048] 00000001
   295    catchsql {DELETE FROM t1 WHERE rowid = (SELECT max(rowid) FROM t1)}
   296  } {1 {database disk image is malformed}}
   297  
   298  # At one point this particular corrupt database was causing a buffer
   299  # overread. Which caused a crash in a run of all.test once.
   300  #
   301  do_test corruptC-2.15 {
   302    db close
   303    forcecopy test.bu test.db
   304    hexio_write test.db 986 b9
   305    sqlite3 db test.db
   306    catchsql {SELECT count(*) FROM sqlite_master;}
   307  } {1 {database disk image is malformed}}
   308  
   309  #
   310  # Now test for a series of quasi-random seeds.
   311  # We loop over the entire file size and touch
   312  # each byte at least once.
   313  for {set tn 0} {$tn<$fsize} {incr tn 1} {
   314  
   315    # setup for test
   316    db close
   317    forcecopy test.bu test.db
   318    sqlite3 db test.db
   319  
   320    # Seek to a random location in the file, and write a random single byte
   321    # value.  Then do various operations on the file to make sure that
   322    # the database engine can handle the corruption gracefully.
   323    #
   324    set last 0
   325    for {set i 1} {$i<=512 && !$last} {incr i 1} {
   326  
   327      db close
   328      if {$i==1} {
   329        # on the first corrupt value, use location $tn
   330        # this ensures that we touch each location in the 
   331        # file at least once.
   332        set roffset $tn
   333      } else { 
   334        # insert random byte at random location
   335        set roffset [random $fsize]
   336      }
   337      set rbyte [format %02x [random 255]]
   338  
   339      # You can uncomment the following to have it trace
   340      # exactly how it's corrupting the file.  This is 
   341      # useful for generating the "seed specific" tests
   342      # above.
   343      # set rline "$roffset $rbyte"
   344      # puts stdout $rline
   345  
   346      hexio_write test.db $roffset $rbyte
   347      sqlite3 db test.db
   348  
   349      # do a few random operations to make sure that if 
   350      # they error, they error gracefully instead of crashing.
   351      do_test corruptC-3.$tn.($qseed).$i.1 {
   352        catchsql {SELECT count(*) FROM sqlite_master}
   353        set x {}
   354      } {}
   355      do_test corruptC-3.$tn.($qseed).$i.2 {
   356        catchsql {SELECT count(*) FROM t1}
   357        set x {}
   358      } {}
   359      do_test corruptC-3.$tn.($qseed).$i.3 {
   360        catchsql {SELECT count(*) FROM t1 WHERE x>13}
   361        set x {}
   362      } {}
   363      do_test corruptC-3.$tn.($qseed).$i.4 {
   364        catchsql {SELECT count(*) FROM t2}
   365        set x {}
   366      } {}
   367      do_test corruptC-3.$tn.($qseed).$i.5 {
   368        catchsql {SELECT count(*) FROM t2 WHERE x<13}
   369        set x {}
   370      } {}
   371      do_test corruptC-3.$tn.($qseed).$i.6 {
   372        catchsql {BEGIN; UPDATE t1 SET y=1; ROLLBACK;}
   373        set x {}
   374      } {}
   375      do_test corruptC-3.$tn.($qseed).$i.7 {
   376        catchsql {BEGIN; UPDATE t2 SET y='abcdef-uvwxyz'; ROLLBACK;}
   377        set x {}
   378      } {}
   379      do_test corruptC-3.$tn.($qseed).$i.8 {
   380        catchsql {BEGIN; DELETE FROM t1 WHERE x>13; ROLLBACK;}
   381        set x {}
   382      } {}
   383      do_test corruptC-3.$tn.($qseed).$i.9 {
   384        catchsql {BEGIN; DELETE FROM t2 WHERE x<13; ROLLBACK;}
   385        set x {}
   386      } {}
   387      do_test corruptC-3.$tn.($qseed).$i.10 {
   388        catchsql {BEGIN; CREATE TABLE t3 AS SELECT x,3 as y FROM t2 WHERE rowid%5!=0; ROLLBACK;}
   389        set x {}
   390      } {}
   391  
   392      # check the integrity of the database.
   393      # once the corruption is detected, we can stop.
   394      ifcapable {integrityck} {
   395        set res [ catchsql {PRAGMA integrity_check} ]
   396        set ans [lindex $res 1]
   397        if { [ string compare $ans "ok" ] != 0 } {
   398          set last -1
   399        }
   400      }
   401      # if we are not capable of doing an integrity check,
   402      # stop after corrupting 5 bytes.
   403      ifcapable {!integrityck} {
   404        if { $i > 5 } {
   405          set last -1
   406        }
   407      }
   408  
   409      # Check that no page references were leaked.
   410      # TBD:  need to figure out why this doesn't work
   411      # work with ROLLBACKs...
   412      if {0} {
   413        do_test corruptC-3.$tn.($qseed).$i.11 {
   414          set bt [btree_from_db db]
   415          db_enter db
   416          array set stats [btree_pager_stats $bt]
   417          db_leave db
   418          set stats(ref)
   419        } {0}
   420      }
   421    }
   422    # end for i
   423  
   424  }
   425  # end for tn
   426  
   427  finish_test