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