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