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.9 2008/07/08 10:19:58 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} 318 319finish_test 320