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: corrupt2.test,v 1.17 2008/09/10 11:28:38 danielk1977 Exp $ 17 18set testdir [file dirname $argv0] 19source $testdir/tester.tcl 20 21# The following tests - corrupt2-1.* - create some databases corrupted in 22# specific ways and ensure that SQLite detects them as corrupt. 23# 24do_test corrupt2-1.1 { 25 execsql { 26 PRAGMA auto_vacuum=0; 27 PRAGMA page_size=1024; 28 CREATE TABLE abc(a, b, c); 29 } 30} {} 31 32do_test corrupt2-1.2 { 33 34 # Corrupt the 16 byte magic string at the start of the file 35 file delete -force corrupt.db 36 file delete -force corrupt.db-journal 37 copy_file test.db corrupt.db 38 set f [open corrupt.db RDWR] 39 seek $f 8 start 40 puts $f blah 41 close $f 42 43 sqlite3 db2 corrupt.db 44 catchsql { 45 SELECT * FROM sqlite_master; 46 } db2 47} {1 {file is encrypted or is not a database}} 48 49do_test corrupt2-1.3 { 50 db2 close 51 52 # Corrupt the page-size (bytes 16 and 17 of page 1). 53 file delete -force corrupt.db 54 file delete -force corrupt.db-journal 55 copy_file test.db corrupt.db 56 set f [open corrupt.db RDWR] 57 fconfigure $f -encoding binary 58 seek $f 16 start 59 puts -nonewline $f "\x00\xFF" 60 close $f 61 62 sqlite3 db2 corrupt.db 63 catchsql { 64 SELECT * FROM sqlite_master; 65 } db2 66} {1 {file is encrypted or is not a database}} 67 68do_test corrupt2-1.4 { 69 db2 close 70 71 # Corrupt the free-block list on page 1. 72 file delete -force corrupt.db 73 file delete -force corrupt.db-journal 74 copy_file test.db corrupt.db 75 set f [open corrupt.db RDWR] 76 fconfigure $f -encoding binary 77 seek $f 101 start 78 puts -nonewline $f "\xFF\xFF" 79 close $f 80 81 sqlite3 db2 corrupt.db 82 catchsql { 83 SELECT * FROM sqlite_master; 84 } db2 85} {1 {database disk image is malformed}} 86 87do_test corrupt2-1.5 { 88 db2 close 89 90 # Corrupt the free-block list on page 1. 91 file delete -force corrupt.db 92 file delete -force corrupt.db-journal 93 copy_file test.db corrupt.db 94 set f [open corrupt.db RDWR] 95 fconfigure $f -encoding binary 96 seek $f 101 start 97 puts -nonewline $f "\x00\xC8" 98 seek $f 200 start 99 puts -nonewline $f "\x00\x00" 100 puts -nonewline $f "\x10\x00" 101 close $f 102 103 sqlite3 db2 corrupt.db 104 catchsql { 105 SELECT * FROM sqlite_master; 106 } db2 107} {1 {database disk image is malformed}} 108db2 close 109 110# Corrupt a database by having 2 indices of the same name: 111do_test corrupt2-2.1 { 112 113 file delete -force corrupt.db 114 file delete -force corrupt.db-journal 115 copy_file test.db corrupt.db 116 117 sqlite3 db2 corrupt.db 118 execsql { 119 CREATE INDEX a1 ON abc(a); 120 CREATE INDEX a2 ON abc(b); 121 PRAGMA writable_schema = 1; 122 UPDATE sqlite_master 123 SET name = 'a3', sql = 'CREATE INDEX a3' || substr(sql, 16, 10000) 124 WHERE type = 'index'; 125 PRAGMA writable_schema = 0; 126 } db2 127 128 db2 close 129 sqlite3 db2 corrupt.db 130 catchsql { 131 SELECT * FROM sqlite_master; 132 } db2 133} {1 {malformed database schema (a3) - index a3 already exists}} 134 135db2 close 136 137do_test corrupt2-3.1 { 138 file delete -force corrupt.db 139 file delete -force corrupt.db-journal 140 sqlite3 db2 corrupt.db 141 142 execsql { 143 PRAGMA auto_vacuum = 1; 144 PRAGMA page_size = 1024; 145 CREATE TABLE t1(a, b, c); 146 CREATE TABLE t2(a, b, c); 147 INSERT INTO t2 VALUES(randomblob(100), randomblob(100), randomblob(100)); 148 INSERT INTO t2 SELECT * FROM t2; 149 INSERT INTO t2 SELECT * FROM t2; 150 INSERT INTO t2 SELECT * FROM t2; 151 INSERT INTO t2 SELECT * FROM t2; 152 } db2 153 154 db2 close 155 156 # On the root page of table t2 (page 4), set one of the child page-numbers 157 # to 0. This corruption will be detected when SQLite attempts to update 158 # the pointer-map after moving the content of page 4 to page 3 as part 159 # of the DROP TABLE operation below. 160 # 161 set fd [open corrupt.db r+] 162 fconfigure $fd -encoding binary -translation binary 163 seek $fd [expr 1024*3 + 12] 164 set zCelloffset [read $fd 2] 165 binary scan $zCelloffset S iCelloffset 166 seek $fd [expr 1024*3 + $iCelloffset] 167 puts -nonewline $fd "\00\00\00\00" 168 close $fd 169 170 sqlite3 db2 corrupt.db 171 catchsql { 172 DROP TABLE t1; 173 } db2 174} {1 {database disk image is malformed}} 175 176do_test corrupt2-4.1 { 177 catchsql { 178 SELECT * FROM t2; 179 } db2 180} {1 {database disk image is malformed}} 181 182db2 close 183 184unset -nocomplain result 185do_test corrupt2-5.1 { 186 file delete -force corrupt.db 187 file delete -force corrupt.db-journal 188 sqlite3 db2 corrupt.db 189 190 execsql { 191 PRAGMA auto_vacuum = 0; 192 PRAGMA page_size = 1024; 193 CREATE TABLE t1(a, b, c); 194 CREATE TABLE t2(a, b, c); 195 INSERT INTO t2 VALUES(randomblob(100), randomblob(100), randomblob(100)); 196 INSERT INTO t2 SELECT * FROM t2; 197 INSERT INTO t2 SELECT * FROM t2; 198 INSERT INTO t2 SELECT * FROM t2; 199 INSERT INTO t2 SELECT * FROM t2; 200 INSERT INTO t1 SELECT * FROM t2; 201 } db2 202 203 db2 close 204 205 # This block links a page from table t2 into the t1 table structure. 206 # 207 set fd [open corrupt.db r+] 208 fconfigure $fd -encoding binary -translation binary 209 seek $fd [expr 1024 + 12] 210 set zCelloffset [read $fd 2] 211 binary scan $zCelloffset S iCelloffset 212 seek $fd [expr 1024 + $iCelloffset] 213 set zChildPage [read $fd 4] 214 seek $fd [expr 2*1024 + 12] 215 set zCelloffset [read $fd 2] 216 binary scan $zCelloffset S iCelloffset 217 seek $fd [expr 2*1024 + $iCelloffset] 218 puts -nonewline $fd $zChildPage 219 close $fd 220 221 sqlite3 db2 corrupt.db 222 db2 eval {SELECT rowid FROM t1} { 223 set result [db2 eval {pragma integrity_check}] 224 break 225 } 226 set result 227} {{*** in database main *** 228Page 10: sqlite3BtreeInitPage() returns error code 11 229On tree page 3 cell 1: Child page depth differs 230On tree page 2 cell 0: 2nd reference to page 10 231On tree page 2 cell 1: Child page depth differs 232Page 4 is never used}} 233 234db2 close 235 236proc corruption_test {args} { 237 set A(-corrupt) {} 238 array set A $args 239 240 catch {db close} 241 file delete -force corrupt.db 242 file delete -force corrupt.db-journal 243 244 sqlite3 db corrupt.db 245 db eval $A(-sqlprep) 246 db close 247 248 eval $A(-corrupt) 249 250 sqlite3 db corrupt.db 251 eval $A(-test) 252} 253 254ifcapable autovacuum { 255 # The tests within this block - corrupt2-6.* - aim to test corruption 256 # detection within an incremental-vacuum. When an incremental-vacuum 257 # step is executed, the last non-free page of the database file is 258 # moved into a free space in the body of the file. After doing so, 259 # the page reference in the parent page must be updated to refer 260 # to the new location. These tests test the outcome of corrupting 261 # that page reference before performing the incremental vacuum. 262 # 263 264 # The last page in the database page is the second page 265 # in an overflow chain. 266 # 267 corruption_test -sqlprep { 268 PRAGMA auto_vacuum = incremental; 269 PRAGMA page_size = 1024; 270 CREATE TABLE t1(a, b); 271 INSERT INTO t1 VALUES(1, randomblob(2500)); 272 INSERT INTO t1 VALUES(2, randomblob(2500)); 273 DELETE FROM t1 WHERE a = 1; 274 } -corrupt { 275 hexio_write corrupt.db [expr 1024*5] 00000008 276 } -test { 277 do_test corrupt2-6.1 { 278 catchsql { pragma incremental_vacuum = 1 } 279 } {1 {database disk image is malformed}} 280 } 281 282 # The last page in the database page is a non-root b-tree page. 283 # 284 corruption_test -sqlprep { 285 PRAGMA auto_vacuum = incremental; 286 PRAGMA page_size = 1024; 287 CREATE TABLE t1(a INTEGER PRIMARY KEY, b); 288 INSERT INTO t1 VALUES(1, randomblob(2500)); 289 INSERT INTO t1 VALUES(2, randomblob(50)); 290 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; 291 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; 292 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; 293 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; 294 DELETE FROM t1 WHERE a = 1; 295 } -corrupt { 296 hexio_write corrupt.db [expr 1024*2 + 8] 00000009 297 } -test { 298 do_test corrupt2-6.2 { 299 catchsql { pragma incremental_vacuum = 1 } 300 } {1 {database disk image is malformed}} 301 } 302 303 # Set up a pointer-map entry so that the last page of the database 304 # file appears to be a b-tree root page. This should be detected 305 # as corruption. 306 # 307 corruption_test -sqlprep { 308 PRAGMA auto_vacuum = incremental; 309 PRAGMA page_size = 1024; 310 CREATE TABLE t1(a INTEGER PRIMARY KEY, b); 311 INSERT INTO t1 VALUES(1, randomblob(2500)); 312 INSERT INTO t1 VALUES(2, randomblob(2500)); 313 INSERT INTO t1 VALUES(3, randomblob(2500)); 314 DELETE FROM t1 WHERE a = 1; 315 } -corrupt { 316 set nPage [expr [file size corrupt.db] / 1024] 317 hexio_write corrupt.db [expr 1024 + ($nPage-3)*5] 010000000 318 } -test { 319 do_test corrupt2-6.3 { 320 catchsql { pragma incremental_vacuum = 1 } 321 } {1 {database disk image is malformed}} 322 } 323 324 corruption_test -sqlprep { 325 PRAGMA auto_vacuum = 1; 326 PRAGMA page_size = 1024; 327 CREATE TABLE t1(a INTEGER PRIMARY KEY, b); 328 INSERT INTO t1 VALUES(1, randomblob(2500)); 329 DELETE FROM t1 WHERE a = 1; 330 } -corrupt { 331 set nAppend [expr 1024*207 - [file size corrupt.db]] 332 set fd [open corrupt.db r+] 333 seek $fd 0 end 334 puts -nonewline $fd [string repeat x $nAppend] 335 close $fd 336 } -test { 337 do_test corrupt2-6.4 { 338 catchsql { 339 BEGIN EXCLUSIVE; 340 COMMIT; 341 } 342 } {1 {database disk image is malformed}} 343 } 344} 345 346 347set sqlprep { 348 PRAGMA auto_vacuum = 0; 349 PRAGMA page_size = 1024; 350 CREATE TABLE t1(a INTEGER PRIMARY KEY, b); 351 CREATE INDEX i1 ON t1(b); 352 INSERT INTO t1 VALUES(1, randomblob(50)); 353 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; 354 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; 355 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; 356 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; 357 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; 358 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1; 359} 360 361corruption_test -sqlprep $sqlprep -corrupt { 362 # Set the page-flags of one of the leaf pages of the index B-Tree to 363 # 0x0D (interpreted by SQLite as "leaf page of a table B-Tree"). 364 # 365 set fd [open corrupt.db r+] 366 fconfigure $fd -translation binary -encoding binary 367 seek $fd [expr 1024*2 + 8] 368 set zRightChild [read $fd 4] 369 binary scan $zRightChild I iRightChild 370 seek $fd [expr 1024*($iRightChild-1)] 371 puts -nonewline $fd "\x0D" 372 close $fd 373} -test { 374 do_test corrupt2-7.1 { 375 catchsql { SELECT b FROM t1 ORDER BY b ASC } 376 } {1 {database disk image is malformed}} 377} 378 379corruption_test -sqlprep $sqlprep -corrupt { 380 # Mess up the page-header of one of the leaf pages of the index B-Tree. 381 # The corruption is detected as part of an OP_Prev opcode. 382 # 383 set fd [open corrupt.db r+] 384 fconfigure $fd -translation binary -encoding binary 385 seek $fd [expr 1024*2 + 12] 386 set zCellOffset [read $fd 2] 387 binary scan $zCellOffset S iCellOffset 388 seek $fd [expr 1024*2 + $iCellOffset] 389 set zChild [read $fd 4] 390 binary scan $zChild I iChild 391 seek $fd [expr 1024*($iChild-1)+3] 392 puts -nonewline $fd "\xFFFF" 393 close $fd 394} -test { 395 do_test corrupt2-7.1 { 396 catchsql { SELECT b FROM t1 ORDER BY b DESC } 397 } {1 {database disk image is malformed}} 398} 399 400corruption_test -sqlprep $sqlprep -corrupt { 401 # Set the page-flags of one of the leaf pages of the table B-Tree to 402 # 0x0A (interpreted by SQLite as "leaf page of an index B-Tree"). 403 # 404 set fd [open corrupt.db r+] 405 fconfigure $fd -translation binary -encoding binary 406 seek $fd [expr 1024*1 + 8] 407 set zRightChild [read $fd 4] 408 binary scan $zRightChild I iRightChild 409 seek $fd [expr 1024*($iRightChild-1)] 410 puts -nonewline $fd "\x0A" 411 close $fd 412} -test { 413 do_test corrupt2-8.1 { 414 catchsql { SELECT * FROM t1 WHERE rowid=1000 } 415 } {1 {database disk image is malformed}} 416} 417 418corruption_test -sqlprep { 419 CREATE TABLE t1(a, b, c); CREATE TABLE t8(a, b, c); CREATE TABLE tE(a, b, c); 420 CREATE TABLE t2(a, b, c); CREATE TABLE t9(a, b, c); CREATE TABLE tF(a, b, c); 421 CREATE TABLE t3(a, b, c); CREATE TABLE tA(a, b, c); CREATE TABLE tG(a, b, c); 422 CREATE TABLE t4(a, b, c); CREATE TABLE tB(a, b, c); CREATE TABLE tH(a, b, c); 423 CREATE TABLE t5(a, b, c); CREATE TABLE tC(a, b, c); CREATE TABLE tI(a, b, c); 424 CREATE TABLE t6(a, b, c); CREATE TABLE tD(a, b, c); CREATE TABLE tJ(a, b, c); 425 CREATE TABLE x1(a, b, c); CREATE TABLE x8(a, b, c); CREATE TABLE xE(a, b, c); 426 CREATE TABLE x2(a, b, c); CREATE TABLE x9(a, b, c); CREATE TABLE xF(a, b, c); 427 CREATE TABLE x3(a, b, c); CREATE TABLE xA(a, b, c); CREATE TABLE xG(a, b, c); 428 CREATE TABLE x4(a, b, c); CREATE TABLE xB(a, b, c); CREATE TABLE xH(a, b, c); 429 CREATE TABLE x5(a, b, c); CREATE TABLE xC(a, b, c); CREATE TABLE xI(a, b, c); 430 CREATE TABLE x6(a, b, c); CREATE TABLE xD(a, b, c); CREATE TABLE xJ(a, b, c); 431} -corrupt { 432 set fd [open corrupt.db r+] 433 fconfigure $fd -translation binary -encoding binary 434 seek $fd 108 435 set zRightChild [read $fd 4] 436 binary scan $zRightChild I iRightChild 437 seek $fd [expr 1024*($iRightChild-1)+3] 438 puts -nonewline $fd "\x00\x00" 439 close $fd 440} -test { 441 do_test corrupt2-9.1 { 442 catchsql { SELECT sql FROM sqlite_master } 443 } {1 {database disk image is malformed}} 444} 445 446corruption_test -sqlprep { 447 CREATE TABLE t1(a, b, c); 448 CREATE TABLE t2(a, b, c); 449 PRAGMA writable_schema = 1; 450 UPDATE sqlite_master SET rootpage = NULL WHERE name = 't2'; 451} -test { 452 do_test corrupt2-10.1 { 453 catchsql { SELECT * FROM t2 } 454 } {1 {malformed database schema (t2)}} 455 do_test corrupt2-10.2 { 456 sqlite3_errcode db 457 } {SQLITE_CORRUPT} 458} 459 460finish_test 461