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