3 # The author disclaims copyright to this source code. In place of
4 # a legal notice, here is a blessing:
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.
10 #***********************************************************************
11 # This file implements regression tests for SQLite library.
13 # This file implements tests to make sure SQLite does not crash or
14 # segfault if it sees a corrupt database file.
16 # $Id: corrupt2.test,v 1.20 2009/04/06 17:50:03 danielk1977 Exp $
18 set testdir [file dirname $argv0]
19 source $testdir/tester.tcl
21 # Do not use a codec for tests in this file, as the database file is
22 # manipulated directly using tcl scripts (using the [hexio_write] command).
27 catch { set presql "$::G(perm:presql);" }
28 unset -nocomplain ::G(perm:presql)
30 # The following tests - corrupt2-1.* - create some databases corrupted in
31 # specific ways and ensure that SQLite detects them as corrupt.
33 do_test corrupt2-1.1 {
36 PRAGMA page_size=1024;
37 CREATE TABLE abc(a, b, c);
41 do_test corrupt2-1.2 {
43 # Corrupt the 16 byte magic string at the start of the file
44 file delete -force corrupt.db
45 file delete -force corrupt.db-journal
46 copy_file test.db corrupt.db
47 set f [open corrupt.db RDWR]
52 sqlite3 db2 corrupt.db
55 SELECT * FROM sqlite_master;
57 } {1 {file is encrypted or is not a database}}
59 do_test corrupt2-1.3 {
62 # Corrupt the page-size (bytes 16 and 17 of page 1).
63 file delete -force corrupt.db
64 file delete -force corrupt.db-journal
65 copy_file test.db corrupt.db
66 set f [open corrupt.db RDWR]
67 fconfigure $f -encoding binary
69 puts -nonewline $f "\x00\xFF"
72 sqlite3 db2 corrupt.db
75 SELECT * FROM sqlite_master;
77 } {1 {file is encrypted or is not a database}}
79 do_test corrupt2-1.4 {
82 # Corrupt the free-block list on page 1.
83 file delete -force corrupt.db
84 file delete -force corrupt.db-journal
85 copy_file test.db corrupt.db
86 set f [open corrupt.db RDWR]
87 fconfigure $f -encoding binary
89 puts -nonewline $f "\xFF\xFF"
92 sqlite3 db2 corrupt.db
95 SELECT * FROM sqlite_master;
97 } {1 {database disk image is malformed}}
99 do_test corrupt2-1.5 {
102 # Corrupt the free-block list on page 1.
103 file delete -force corrupt.db
104 file delete -force corrupt.db-journal
105 copy_file test.db corrupt.db
106 set f [open corrupt.db RDWR]
107 fconfigure $f -encoding binary
109 puts -nonewline $f "\x00\xC8"
111 puts -nonewline $f "\x00\x00"
112 puts -nonewline $f "\x10\x00"
115 sqlite3 db2 corrupt.db
118 SELECT * FROM sqlite_master;
120 } {1 {database disk image is malformed}}
123 # Corrupt a database by having 2 indices of the same name:
124 do_test corrupt2-2.1 {
126 file delete -force corrupt.db
127 file delete -force corrupt.db-journal
128 copy_file test.db corrupt.db
130 sqlite3 db2 corrupt.db
133 CREATE INDEX a1 ON abc(a);
134 CREATE INDEX a2 ON abc(b);
135 PRAGMA writable_schema = 1;
137 SET name = 'a3', sql = 'CREATE INDEX a3' || substr(sql, 16, 10000)
138 WHERE type = 'index';
139 PRAGMA writable_schema = 0;
143 sqlite3 db2 corrupt.db
146 SELECT * FROM sqlite_master;
148 } {1 {malformed database schema (a3) - index a3 already exists}}
152 do_test corrupt2-3.1 {
153 file delete -force corrupt.db
154 file delete -force corrupt.db-journal
155 sqlite3 db2 corrupt.db
159 PRAGMA auto_vacuum = 1;
160 PRAGMA page_size = 1024;
161 CREATE TABLE t1(a, b, c);
162 CREATE TABLE t2(a, b, c);
163 INSERT INTO t2 VALUES(randomblob(100), randomblob(100), randomblob(100));
164 INSERT INTO t2 SELECT * FROM t2;
165 INSERT INTO t2 SELECT * FROM t2;
166 INSERT INTO t2 SELECT * FROM t2;
167 INSERT INTO t2 SELECT * FROM t2;
172 # On the root page of table t2 (page 4), set one of the child page-numbers
173 # to 0. This corruption will be detected when SQLite attempts to update
174 # the pointer-map after moving the content of page 4 to page 3 as part
175 # of the DROP TABLE operation below.
177 set fd [open corrupt.db r+]
178 fconfigure $fd -encoding binary -translation binary
179 seek $fd [expr 1024*3 + 12]
180 set zCelloffset [read $fd 2]
181 binary scan $zCelloffset S iCelloffset
182 seek $fd [expr 1024*3 + $iCelloffset]
183 puts -nonewline $fd "\00\00\00\00"
186 sqlite3 db2 corrupt.db
191 } {1 {database disk image is malformed}}
193 do_test corrupt2-4.1 {
197 } {1 {database disk image is malformed}}
201 unset -nocomplain result
202 do_test corrupt2-5.1 {
203 file delete -force corrupt.db
204 file delete -force corrupt.db-journal
205 sqlite3 db2 corrupt.db
209 PRAGMA auto_vacuum = 0;
210 PRAGMA page_size = 1024;
211 CREATE TABLE t1(a, b, c);
212 CREATE TABLE t2(a, b, c);
213 INSERT INTO t2 VALUES(randomblob(100), randomblob(100), randomblob(100));
214 INSERT INTO t2 SELECT * FROM t2;
215 INSERT INTO t2 SELECT * FROM t2;
216 INSERT INTO t2 SELECT * FROM t2;
217 INSERT INTO t2 SELECT * FROM t2;
218 INSERT INTO t1 SELECT * FROM t2;
223 # This block links a page from table t2 into the t1 table structure.
225 set fd [open corrupt.db r+]
226 fconfigure $fd -encoding binary -translation binary
227 seek $fd [expr 1024 + 12]
228 set zCelloffset [read $fd 2]
229 binary scan $zCelloffset S iCelloffset
230 seek $fd [expr 1024 + $iCelloffset]
231 set zChildPage [read $fd 4]
232 seek $fd [expr 2*1024 + 12]
233 set zCelloffset [read $fd 2]
234 binary scan $zCelloffset S iCelloffset
235 seek $fd [expr 2*1024 + $iCelloffset]
236 puts -nonewline $fd $zChildPage
239 sqlite3 db2 corrupt.db
241 db2 eval {SELECT rowid FROM t1} {
242 set result [db2 eval {pragma integrity_check}]
246 } {{*** in database main ***
247 On tree page 2 cell 0: 2nd reference to page 10
248 On tree page 2 cell 1: Child page depth differs
249 Page 4 is never used}}
253 proc corruption_test {args} {
260 file delete -force corrupt.db
261 file delete -force corrupt.db-journal
263 sqlite3 db corrupt.db
271 sqlite3 db corrupt.db
275 ifcapable autovacuum {
276 # The tests within this block - corrupt2-6.* - aim to test corruption
277 # detection within an incremental-vacuum. When an incremental-vacuum
278 # step is executed, the last non-free page of the database file is
279 # moved into a free space in the body of the file. After doing so,
280 # the page reference in the parent page must be updated to refer
281 # to the new location. These tests test the outcome of corrupting
282 # that page reference before performing the incremental vacuum.
285 # The last page in the database page is the second page
286 # in an overflow chain.
288 corruption_test -sqlprep {
289 PRAGMA auto_vacuum = incremental;
290 PRAGMA page_size = 1024;
291 CREATE TABLE t1(a, b);
292 INSERT INTO t1 VALUES(1, randomblob(2500));
293 INSERT INTO t1 VALUES(2, randomblob(2500));
294 DELETE FROM t1 WHERE a = 1;
296 hexio_write corrupt.db [expr 1024*5] 00000008
298 do_test corrupt2-6.1 {
299 catchsql " $::presql pragma incremental_vacuum = 1 "
300 } {1 {database disk image is malformed}}
303 # The last page in the database page is a non-root b-tree page.
305 corruption_test -sqlprep {
306 PRAGMA auto_vacuum = incremental;
307 PRAGMA page_size = 1024;
308 CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
309 INSERT INTO t1 VALUES(1, randomblob(2500));
310 INSERT INTO t1 VALUES(2, randomblob(50));
311 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
312 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
313 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
314 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
315 DELETE FROM t1 WHERE a = 1;
317 hexio_write corrupt.db [expr 1024*2 + 8] 00000009
319 do_test corrupt2-6.2 {
320 catchsql " $::presql pragma incremental_vacuum = 1 "
321 } {1 {database disk image is malformed}}
324 # Set up a pointer-map entry so that the last page of the database
325 # file appears to be a b-tree root page. This should be detected
328 corruption_test -sqlprep {
329 PRAGMA auto_vacuum = incremental;
330 PRAGMA page_size = 1024;
331 CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
332 INSERT INTO t1 VALUES(1, randomblob(2500));
333 INSERT INTO t1 VALUES(2, randomblob(2500));
334 INSERT INTO t1 VALUES(3, randomblob(2500));
335 DELETE FROM t1 WHERE a = 1;
337 set nPage [expr [file size corrupt.db] / 1024]
338 hexio_write corrupt.db [expr 1024 + ($nPage-3)*5] 010000000
340 do_test corrupt2-6.3 {
341 catchsql " $::presql pragma incremental_vacuum = 1 "
342 } {1 {database disk image is malformed}}
345 corruption_test -sqlprep {
346 PRAGMA auto_vacuum = 1;
347 PRAGMA page_size = 1024;
348 CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
349 INSERT INTO t1 VALUES(1, randomblob(2500));
350 DELETE FROM t1 WHERE a = 1;
352 set nAppend [expr 1024*207 - [file size corrupt.db]]
353 set fd [open corrupt.db r+]
355 puts -nonewline $fd [string repeat x $nAppend]
357 hexio_write corrupt.db 28 00000000
359 do_test corrupt2-6.4 {
365 } {1 {database disk image is malformed}}
371 PRAGMA auto_vacuum = 0;
372 PRAGMA page_size = 1024;
373 CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
374 CREATE INDEX i1 ON t1(b);
375 INSERT INTO t1 VALUES(1, randomblob(50));
376 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
377 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
378 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
379 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
380 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
381 INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
384 corruption_test -sqlprep $sqlprep -corrupt {
385 # Set the page-flags of one of the leaf pages of the index B-Tree to
386 # 0x0D (interpreted by SQLite as "leaf page of a table B-Tree").
388 set fd [open corrupt.db r+]
389 fconfigure $fd -translation binary -encoding binary
390 seek $fd [expr 1024*2 + 8]
391 set zRightChild [read $fd 4]
392 binary scan $zRightChild I iRightChild
393 seek $fd [expr 1024*($iRightChild-1)]
394 puts -nonewline $fd "\x0D"
397 do_test corrupt2-7.1 {
398 catchsql " $::presql SELECT b FROM t1 ORDER BY b ASC "
399 } {1 {database disk image is malformed}}
402 corruption_test -sqlprep $sqlprep -corrupt {
403 # Mess up the page-header of one of the leaf pages of the index B-Tree.
404 # The corruption is detected as part of an OP_Prev opcode.
406 set fd [open corrupt.db r+]
407 fconfigure $fd -translation binary -encoding binary
408 seek $fd [expr 1024*2 + 12]
409 set zCellOffset [read $fd 2]
410 binary scan $zCellOffset S iCellOffset
411 seek $fd [expr 1024*2 + $iCellOffset]
412 set zChild [read $fd 4]
413 binary scan $zChild I iChild
414 seek $fd [expr 1024*($iChild-1)+3]
415 puts -nonewline $fd "\xFFFF"
418 do_test corrupt2-7.1 {
419 catchsql " $::presql SELECT b FROM t1 ORDER BY b DESC "
420 } {1 {database disk image is malformed}}
423 corruption_test -sqlprep $sqlprep -corrupt {
424 # Set the page-flags of one of the leaf pages of the table B-Tree to
425 # 0x0A (interpreted by SQLite as "leaf page of an index B-Tree").
427 set fd [open corrupt.db r+]
428 fconfigure $fd -translation binary -encoding binary
429 seek $fd [expr 1024*1 + 8]
430 set zRightChild [read $fd 4]
431 binary scan $zRightChild I iRightChild
432 seek $fd [expr 1024*($iRightChild-1)]
433 puts -nonewline $fd "\x0A"
436 do_test corrupt2-8.1 {
437 catchsql " $::presql SELECT * FROM t1 WHERE rowid=1000 "
438 } {1 {database disk image is malformed}}
441 corruption_test -sqlprep {
442 CREATE TABLE t1(a, b, c); CREATE TABLE t8(a, b, c); CREATE TABLE tE(a, b, c);
443 CREATE TABLE t2(a, b, c); CREATE TABLE t9(a, b, c); CREATE TABLE tF(a, b, c);
444 CREATE TABLE t3(a, b, c); CREATE TABLE tA(a, b, c); CREATE TABLE tG(a, b, c);
445 CREATE TABLE t4(a, b, c); CREATE TABLE tB(a, b, c); CREATE TABLE tH(a, b, c);
446 CREATE TABLE t5(a, b, c); CREATE TABLE tC(a, b, c); CREATE TABLE tI(a, b, c);
447 CREATE TABLE t6(a, b, c); CREATE TABLE tD(a, b, c); CREATE TABLE tJ(a, b, c);
448 CREATE TABLE x1(a, b, c); CREATE TABLE x8(a, b, c); CREATE TABLE xE(a, b, c);
449 CREATE TABLE x2(a, b, c); CREATE TABLE x9(a, b, c); CREATE TABLE xF(a, b, c);
450 CREATE TABLE x3(a, b, c); CREATE TABLE xA(a, b, c); CREATE TABLE xG(a, b, c);
451 CREATE TABLE x4(a, b, c); CREATE TABLE xB(a, b, c); CREATE TABLE xH(a, b, c);
452 CREATE TABLE x5(a, b, c); CREATE TABLE xC(a, b, c); CREATE TABLE xI(a, b, c);
453 CREATE TABLE x6(a, b, c); CREATE TABLE xD(a, b, c); CREATE TABLE xJ(a, b, c);
455 set fd [open corrupt.db r+]
456 fconfigure $fd -translation binary -encoding binary
458 set zRightChild [read $fd 4]
459 binary scan $zRightChild I iRightChild
460 seek $fd [expr 1024*($iRightChild-1)+3]
461 puts -nonewline $fd "\x00\x00"
464 do_test corrupt2-9.1 {
465 catchsql " $::presql SELECT sql FROM sqlite_master "
466 } {1 {database disk image is malformed}}
469 corruption_test -sqlprep {
470 CREATE TABLE t1(a, b, c);
471 CREATE TABLE t2(a, b, c);
472 PRAGMA writable_schema = 1;
473 UPDATE sqlite_master SET rootpage = NULL WHERE name = 't2';
475 do_test corrupt2-10.1 {
476 catchsql " $::presql SELECT * FROM t2 "
477 } {1 {malformed database schema (t2)}}
478 do_test corrupt2-10.2 {
483 corruption_test -sqlprep {
484 PRAGMA auto_vacuum = incremental;
485 CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
486 CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
487 INSERT INTO t1 VALUES(1, randstr(100,100));
488 INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
489 INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
490 INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
491 INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
492 INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
493 INSERT INTO t2 SELECT * FROM t1;
496 set offset [expr [file size corrupt.db] - 1024]
497 hexio_write corrupt.db $offset FF
498 hexio_write corrupt.db 24 12345678
500 do_test corrupt2-11.1 {
501 catchsql " $::presql PRAGMA incremental_vacuum "
502 } {1 {database disk image is malformed}}
504 corruption_test -sqlprep {
505 PRAGMA auto_vacuum = incremental;
506 CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
507 CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
508 INSERT INTO t1 VALUES(1, randstr(100,100));
509 INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
510 INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
511 INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
512 INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
513 INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
514 INSERT INTO t2 SELECT * FROM t1;
517 set pgno [expr [file size corrupt.db] / 1024]
518 hexio_write corrupt.db [expr 1024+5*($pgno-3)] 03
519 hexio_write corrupt.db 24 12345678
521 do_test corrupt2-12.1 {
522 catchsql " $::presql PRAGMA incremental_vacuum "
523 } {1 {database disk image is malformed}}
526 ifcapable autovacuum {
527 # It is not possible for the last page in a database file to be the
528 # pending-byte page (AKA the locking page). This test verifies that if
529 # an attempt is made to commit a transaction to such an auto-vacuum
530 # database SQLITE_CORRUPT is returned.
532 corruption_test -tclprep {
534 PRAGMA auto_vacuum = full;
535 PRAGMA page_size = 1024;
536 CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
537 INSERT INTO t1 VALUES(NULL, randstr(50,50));
539 for {set ii 0} {$ii < 10} {incr ii} {
540 db eval " $::presql INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 "
543 do_test corrupt2-13.1 {
545 } $::sqlite_pending_byte
546 hexio_write corrupt.db [expr $::sqlite_pending_byte+1023] 00
547 hexio_write corrupt.db 28 00000000
549 do_test corrupt2-13.2 {
551 } [expr $::sqlite_pending_byte + 1024]
552 do_test corrupt2-13.3 {
553 catchsql { DELETE FROM t1 WHERE rowid < 30; }
554 } {1 {database disk image is malformed}}