modernc.org/cc@v1.0.1/v2/testdata/_sqlite/test/corrupt.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.
    15  #
    16  # $Id: corrupt.test,v 1.12 2009/07/13 09:41:45 danielk1977 Exp $
    17  
    18  catch {forcedelete test.db test.db-journal test.bu}
    19  
    20  set testdir [file dirname $argv0]
    21  source $testdir/tester.tcl
    22  
    23  # Do not use a codec for tests in this file, as the database file is
    24  # manipulated directly using tcl scripts (using the [hexio_write] command).
    25  #
    26  do_not_use_codec
    27  
    28  # These tests deal with corrupt database files
    29  #
    30  database_may_be_corrupt
    31  
    32  # Construct a large database for testing.
    33  #
    34  do_test corrupt-1.1 {
    35    execsql {
    36      BEGIN;
    37      CREATE TABLE t1(x);
    38      INSERT INTO t1 VALUES(randstr(100,100));
    39      INSERT INTO t1 VALUES(randstr(90,90));
    40      INSERT INTO t1 VALUES(randstr(80,80));
    41      INSERT INTO t1 SELECT x || randstr(5,5) FROM t1;
    42      INSERT INTO t1 SELECT x || randstr(6,6) FROM t1;
    43      INSERT INTO t1 SELECT x || randstr(7,7) FROM t1;
    44      INSERT INTO t1 SELECT x || randstr(8,8) FROM t1;
    45      INSERT INTO t1 VALUES(randstr(3000,3000));
    46      INSERT INTO t1 SELECT x || randstr(9,9) FROM t1;
    47      INSERT INTO t1 SELECT x || randstr(10,10) FROM t1;
    48      INSERT INTO t1 SELECT x || randstr(11,11) FROM t1;
    49      INSERT INTO t1 SELECT x || randstr(12,12) FROM t1;
    50      CREATE INDEX t1i1 ON t1(x);
    51      CREATE TABLE t2 AS SELECT * FROM t1;
    52      DELETE FROM t2 WHERE rowid%5!=0;
    53      COMMIT;
    54    }
    55  } {}
    56  integrity_check corrupt-1.2
    57  
    58  # Setup for the tests.  Make a backup copy of the good database in test.bu.
    59  # Create a string of garbage data that is 256 bytes long.
    60  #
    61  forcecopy test.db test.bu
    62  set fsize [file size test.db]
    63  set junk "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    64  while {[string length $junk]<256} {append junk $junk}
    65  set junk [string range $junk 0 255]
    66  
    67  # Go through the database and write garbage data into each 256 segment
    68  # of the file.  Then do various operations on the file to make sure that
    69  # the database engine can recover gracefully from the corruption.
    70  #
    71  for {set i [expr {1*256}]} {$i<$fsize-256} {incr i 256} {
    72    set tn [expr {$i/256}]
    73    db close
    74    forcecopy test.bu test.db
    75    set fd [open test.db r+]
    76    fconfigure $fd -translation binary
    77    seek $fd $i
    78    puts -nonewline $fd $junk
    79    close $fd
    80    do_test corrupt-2.$tn.1 {
    81      sqlite3 db test.db
    82      catchsql {SELECT count(*) FROM sqlite_master}
    83      set x {}
    84    } {}
    85    do_test corrupt-2.$tn.2 {
    86      catchsql {SELECT count(*) FROM t1}
    87      set x {}
    88    } {}
    89    do_test corrupt-2.$tn.3 {
    90      catchsql {SELECT count(*) FROM t1 WHERE x>'abcdef'}
    91      set x {}
    92    } {}
    93    do_test corrupt-2.$tn.4 {
    94      catchsql {SELECT count(*) FROM t2}
    95      set x {}
    96    } {}
    97    do_test corrupt-2.$tn.5 {
    98      catchsql {CREATE TABLE t3 AS SELECT * FROM t1}
    99      set x {}
   100    } {}
   101    do_test corrupt-2.$tn.6 {
   102      catchsql {DROP TABLE t1}
   103      set x {}
   104    } {}
   105    do_test corrupt-2.$tn.7 {
   106      catchsql {PRAGMA integrity_check}
   107      set x {}
   108    } {}
   109  
   110    # Check that no page references were leaked.
   111    do_test corrupt-2.$tn.8 {
   112      set bt [btree_from_db db]
   113      db_enter db
   114      array set stats [btree_pager_stats $bt]
   115      db_leave db
   116      set stats(ref)
   117    } {0}
   118  }  
   119  
   120  #------------------------------------------------------------------------
   121  # For these tests, swap the rootpage entries of t1 (a table) and t1i1 (an
   122  # index on t1) in sqlite_master. Then perform a few different queries
   123  # and make sure this is detected as corruption.
   124  #
   125  do_test corrupt-3.1 {
   126    db close
   127    forcecopy test.bu test.db
   128    sqlite3 db test.db
   129    list
   130  } {}
   131  do_test corrupt-3.2 {
   132    set t1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1i1'}]
   133    set t1i1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1'}]
   134    set cookie [expr [execsql {PRAGMA schema_version}] + 1]
   135    execsql "
   136      PRAGMA writable_schema = 1;
   137      UPDATE sqlite_master SET rootpage = $t1_r WHERE name = 't1';
   138      UPDATE sqlite_master SET rootpage = $t1i1_r WHERE name = 't1i1';
   139      PRAGMA writable_schema = 0;
   140      PRAGMA schema_version = $cookie;
   141    "
   142  } {}
   143  
   144  # This one tests the case caught by code in checkin [2313].
   145  do_test corrupt-3.3 {
   146    db close
   147    sqlite3 db test.db
   148    catchsql {
   149      INSERT INTO t1 VALUES('abc');
   150    }
   151  } {1 {database disk image is malformed}}
   152  do_test corrupt-3.4 {
   153    db close
   154    sqlite3 db test.db
   155    catchsql {
   156      SELECT * FROM t1;
   157    }
   158  } {1 {database disk image is malformed}}
   159  do_test corrupt-3.5 {
   160    db close
   161    sqlite3 db test.db
   162    catchsql {
   163      SELECT * FROM t1 WHERE oid = 10;
   164    }
   165  } {1 {database disk image is malformed}}
   166  do_test corrupt-3.6 {
   167    db close
   168    sqlite3 db test.db
   169    catchsql {
   170      SELECT * FROM t1 WHERE x = 'abcde';
   171    }
   172  } {1 {database disk image is malformed}}
   173  
   174  do_test corrupt-4.1 {
   175    db close
   176    forcedelete test.db test.db-journal
   177    sqlite3 db test.db
   178    execsql {
   179      PRAGMA page_size = 1024;
   180      CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
   181    }
   182    for {set i 0} {$i < 10} {incr i} {
   183      set text [string repeat $i 220]
   184      execsql { INSERT INTO t1 VALUES($i, $text) }
   185    }
   186    execsql { CREATE INDEX i1 ON t1(b) }
   187  } {}
   188  do_test corrupt-4.2 {
   189    set iRoot [db one {SELECT rootpage FROM sqlite_master WHERE name = 'i1'}]
   190    set iOffset [hexio_get_int [hexio_read test.db [expr 12+($iRoot-1)*1024] 2]]
   191    set data [hexio_render_int32 [expr $iRoot - 1]]
   192    hexio_write test.db [expr ($iRoot-1)*1024 + $iOffset] $data
   193    db close
   194    sqlite3 db test.db
   195  
   196    # The following DELETE statement attempts to delete a cell stored on the
   197    # root page of index i1. After this cell is deleted it must be replaced
   198    # by a cell retrieved from the child page (a leaf) of the deleted cell.
   199    # This will fail, as the block modified the database image so that the
   200    # child page of the deleted cell is from a table (intkey) b-tree, not an
   201    # index b-tree as expected. At one point this was causing an assert()
   202    # to fail.
   203    catchsql { DELETE FROM t1 WHERE rowid = 3 }
   204  } {1 {database disk image is malformed}}
   205  
   206  do_test corrupt-5.1 {
   207    db close
   208    forcedelete test.db test.db-journal
   209    sqlite3 db test.db
   210  
   211    execsql { PRAGMA page_size = 1024 }
   212    set ct "CREATE TABLE t1(c0 "
   213    set i 0
   214    while {[string length $ct] < 950} { append ct ", c[incr i]" }
   215    append ct ")"
   216    execsql $ct
   217  } {}
   218  
   219  do_test corrupt-5.2 {
   220    db close
   221    hexio_write test.db 108 00000000 
   222    sqlite3 db test.db
   223    catchsql { SELECT * FROM sqlite_master }
   224  } {1 {database disk image is malformed}}
   225  
   226  # At one point, the specific corruption caused by this test case was
   227  # causing a buffer overwrite. Although a crash was never demonstrated,
   228  # running this testcase under valgrind revealed the problem.
   229  do_test corrupt-6.1 {
   230    db close
   231    forcedelete test.db test.db-journal
   232    sqlite3 db test.db
   233    execsql { 
   234      PRAGMA page_size = 1024; CREATE TABLE t1(x);
   235    }
   236  
   237    # The root page of t1 is 1024 bytes in size. The header is 8 bytes, and
   238    # each of the cells inserted by the following INSERT statements consume
   239    # 16 bytes (including the 2 byte cell-offset array entry). So the page
   240    # can contain up to 63 cells.
   241    for {set i 0} {$i < 63} {incr i} {
   242      execsql { INSERT INTO t1 VALUES( randomblob(10) ) }
   243    }
   244  
   245    # Free the cell stored right at the end of the page (at offset pgsz-14).
   246    execsql { DELETE FROM t1 WHERE rowid=1 }
   247    set rootpage [db one {SELECT rootpage FROM sqlite_master WHERE name = 't1'}]
   248    db close
   249  
   250    set offset [expr ($rootpage * 1024)-14+2]
   251    hexio_write test.db $offset 00FF
   252    sqlite3 db test.db 
   253  
   254    catchsql { INSERT INTO t1 VALUES( randomblob(10) ) }
   255  } {1 {database disk image is malformed}}
   256  
   257  ifcapable oversize_cell_check {
   258    db close
   259    forcedelete test.db test.db-journal
   260    sqlite3 db test.db
   261    execsql { 
   262      PRAGMA page_size = 1024; CREATE TABLE t1(x);
   263    }
   264  
   265    do_test corrupt-7.1 {
   266      for {set i 0} {$i < 39} {incr i} {
   267        execsql {
   268          INSERT INTO t1 VALUES(X'000100020003000400050006000700080009000A');
   269        }
   270      }
   271    } {}
   272    db close
   273    
   274    # Corrupt the root page of table t1 so that the first offset in the 
   275    # cell-offset array points to the data for the SQL blob associated with
   276    # record (rowid=10). The root page still passes the checks in btreeInitPage(),
   277    # because the start of said blob looks like the start of a legitimate 
   278    # page cell.
   279    #
   280    # Test case cc-2 overwrites the blob so that it no longer looks like a
   281    # real cell. But, by the time it is overwritten, btreeInitPage() has already
   282    # initialized the root page, so no corruption is detected.
   283    #
   284    # Test case cc-3 inserts an extra record into t1, forcing balance-deeper
   285    # to run. After copying the contents of the root page to the new child,
   286    # btreeInitPage() is called on the child. This time, it detects corruption
   287    # (because the start of the blob associated with the (rowid=10) record
   288    # no longer looks like a real cell). At one point the code assumed that 
   289    # detecting corruption was not possible at that point, and an assert() failed.
   290    #
   291    set fd [open test.db r+]
   292    fconfigure $fd -translation binary -encoding binary
   293    seek $fd [expr 1024+8]
   294    puts -nonewline $fd "\x03\x14"
   295    close $fd
   296    
   297    sqlite3 db test.db
   298    do_test corrupt-7.2 {
   299      execsql { 
   300        UPDATE t1 SET x = X'870400020003000400050006000700080009000A' 
   301        WHERE rowid = 10;
   302      }
   303    } {}
   304    do_test corrupt-7.3 {
   305      catchsql {
   306        INSERT INTO t1 VALUES(X'000100020003000400050006000700080009000A');
   307      }
   308    } {1 {database disk image is malformed}}
   309  }
   310  
   311  db close
   312  forcedelete test.db test.db-journal
   313  do_test corrupt-8.1 {
   314    sqlite3 db test.db
   315    execsql {
   316      PRAGMA page_size = 1024;
   317      PRAGMA secure_delete = on;
   318      PRAGMA auto_vacuum = 0;
   319      CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
   320      INSERT INTO t1 VALUES(5, randomblob(1900));
   321    }
   322  
   323    hexio_write test.db 2044 [hexio_render_int32 2]
   324    hexio_write test.db 24   [hexio_render_int32 45]
   325  
   326    catchsql { INSERT OR REPLACE INTO t1 VALUES(5, randomblob(1900)) }
   327  } {1 {database disk image is malformed}}
   328  
   329  db close
   330  forcedelete test.db test.db-journal
   331  do_test corrupt-8.2 {
   332    sqlite3 db test.db
   333    execsql {
   334      PRAGMA page_size = 1024;
   335      PRAGMA secure_delete = on;
   336      PRAGMA auto_vacuum = 0;
   337      CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
   338      INSERT INTO t1 VALUES(5, randomblob(900));
   339      INSERT INTO t1 VALUES(6, randomblob(900));
   340    }
   341  
   342    hexio_write test.db 2047 FF
   343    hexio_write test.db 24   [hexio_render_int32 45]
   344  
   345    catchsql { INSERT INTO t1 VALUES(4, randomblob(1900)) }
   346  } {1 {database disk image is malformed}}
   347  
   348  finish_test