Annotation of embedaddon/sqlite3/test/corrupt2.test, revision 1.1.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.
15: #
16: # $Id: corrupt2.test,v 1.20 2009/04/06 17:50:03 danielk1977 Exp $
17:
18: set testdir [file dirname $argv0]
19: source $testdir/tester.tcl
20:
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).
23: #
24: do_not_use_codec
25:
26: set presql ""
27: catch { set presql "$::G(perm:presql);" }
28: unset -nocomplain ::G(perm:presql)
29:
30: # The following tests - corrupt2-1.* - create some databases corrupted in
31: # specific ways and ensure that SQLite detects them as corrupt.
32: #
33: do_test corrupt2-1.1 {
34: execsql {
35: PRAGMA auto_vacuum=0;
36: PRAGMA page_size=1024;
37: CREATE TABLE abc(a, b, c);
38: }
39: } {}
40:
41: do_test corrupt2-1.2 {
42:
43: # Corrupt the 16 byte magic string at the start of the file
44: forcedelete corrupt.db
45: forcedelete corrupt.db-journal
46: forcecopy test.db corrupt.db
47: set f [open corrupt.db RDWR]
48: seek $f 8 start
49: puts $f blah
50: close $f
51:
52: sqlite3 db2 corrupt.db
53: catchsql "
54: $::presql
55: SELECT * FROM sqlite_master;
56: " db2
57: } {1 {file is encrypted or is not a database}}
58:
59: do_test corrupt2-1.3 {
60: db2 close
61:
62: # Corrupt the page-size (bytes 16 and 17 of page 1).
63: forcedelete corrupt.db
64: forcedelete corrupt.db-journal
65: forcecopy test.db corrupt.db
66: set f [open corrupt.db RDWR]
67: fconfigure $f -encoding binary
68: seek $f 16 start
69: puts -nonewline $f "\x00\xFF"
70: close $f
71:
72: sqlite3 db2 corrupt.db
73: catchsql "
74: $::presql
75: SELECT * FROM sqlite_master;
76: " db2
77: } {1 {file is encrypted or is not a database}}
78:
79: do_test corrupt2-1.4 {
80: db2 close
81:
82: # Corrupt the free-block list on page 1.
83: forcedelete corrupt.db
84: forcedelete corrupt.db-journal
85: forcecopy test.db corrupt.db
86: set f [open corrupt.db RDWR]
87: fconfigure $f -encoding binary
88: seek $f 101 start
89: puts -nonewline $f "\xFF\xFF"
90: close $f
91:
92: sqlite3 db2 corrupt.db
93: catchsql "
94: $::presql
95: SELECT * FROM sqlite_master;
96: " db2
97: } {1 {database disk image is malformed}}
98:
99: do_test corrupt2-1.5 {
100: db2 close
101:
102: # Corrupt the free-block list on page 1.
103: forcedelete corrupt.db
104: forcedelete corrupt.db-journal
105: forcecopy test.db corrupt.db
106: set f [open corrupt.db RDWR]
107: fconfigure $f -encoding binary
108: seek $f 101 start
109: puts -nonewline $f "\x00\xC8"
110: seek $f 200 start
111: puts -nonewline $f "\x00\x00"
112: puts -nonewline $f "\x10\x00"
113: close $f
114:
115: sqlite3 db2 corrupt.db
116: catchsql "
117: $::presql
118: SELECT * FROM sqlite_master;
119: " db2
120: } {1 {database disk image is malformed}}
121: db2 close
122:
123: # Corrupt a database by having 2 indices of the same name:
124: do_test corrupt2-2.1 {
125:
126: forcedelete corrupt.db
127: forcedelete corrupt.db-journal
128: forcecopy test.db corrupt.db
129:
130: sqlite3 db2 corrupt.db
131: execsql "
132: $::presql
133: CREATE INDEX a1 ON abc(a);
134: CREATE INDEX a2 ON abc(b);
135: PRAGMA writable_schema = 1;
136: UPDATE sqlite_master
137: SET name = 'a3', sql = 'CREATE INDEX a3' || substr(sql, 16, 10000)
138: WHERE type = 'index';
139: PRAGMA writable_schema = 0;
140: " db2
141:
142: db2 close
143: sqlite3 db2 corrupt.db
144: catchsql "
145: $::presql
146: SELECT * FROM sqlite_master;
147: " db2
148: } {1 {malformed database schema (a3) - index a3 already exists}}
149:
150: db2 close
151:
152: do_test corrupt2-3.1 {
153: forcedelete corrupt.db
154: forcedelete corrupt.db-journal
155: sqlite3 db2 corrupt.db
156:
157: execsql "
158: $::presql
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;
168: " db2
169:
170: db2 close
171:
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.
176: #
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"
184: close $fd
185:
186: sqlite3 db2 corrupt.db
187: catchsql "
188: $::presql
189: DROP TABLE t1;
190: " db2
191: } {1 {database disk image is malformed}}
192:
193: do_test corrupt2-4.1 {
194: catchsql {
195: SELECT * FROM t2;
196: } db2
197: } {1 {database disk image is malformed}}
198:
199: db2 close
200:
201: unset -nocomplain result
202: do_test corrupt2-5.1 {
203: forcedelete corrupt.db
204: forcedelete corrupt.db-journal
205: sqlite3 db2 corrupt.db
206:
207: execsql "
208: $::presql
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;
219: " db2
220:
221: db2 close
222:
223: # This block links a page from table t2 into the t1 table structure.
224: #
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
237: close $fd
238:
239: sqlite3 db2 corrupt.db
240: db2 eval $::presql
241: db2 eval {SELECT rowid FROM t1} {
242: set result [db2 eval {pragma integrity_check}]
243: break
244: }
245: set result
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}}
250:
251: db2 close
252:
253: proc corruption_test {args} {
254: set A(-corrupt) {}
255: set A(-sqlprep) {}
256: set A(-tclprep) {}
257: array set A $args
258:
259: catch {db close}
260: forcedelete corrupt.db
261: forcedelete corrupt.db-journal
262:
263: sqlite3 db corrupt.db
264: db eval $::presql
265: eval $A(-tclprep)
266: db eval $A(-sqlprep)
267: db close
268:
269: eval $A(-corrupt)
270:
271: sqlite3 db corrupt.db
272: eval $A(-test)
273: }
274:
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.
283: #
284:
285: # The last page in the database page is the second page
286: # in an overflow chain.
287: #
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;
295: } -corrupt {
296: hexio_write corrupt.db [expr 1024*5] 00000008
297: } -test {
298: do_test corrupt2-6.1 {
299: catchsql " $::presql pragma incremental_vacuum = 1 "
300: } {1 {database disk image is malformed}}
301: }
302:
303: # The last page in the database page is a non-root b-tree page.
304: #
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;
316: } -corrupt {
317: hexio_write corrupt.db [expr 1024*2 + 8] 00000009
318: } -test {
319: do_test corrupt2-6.2 {
320: catchsql " $::presql pragma incremental_vacuum = 1 "
321: } {1 {database disk image is malformed}}
322: }
323:
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
326: # as corruption.
327: #
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;
336: } -corrupt {
337: set nPage [expr [file size corrupt.db] / 1024]
338: hexio_write corrupt.db [expr 1024 + ($nPage-3)*5] 010000000
339: } -test {
340: do_test corrupt2-6.3 {
341: catchsql " $::presql pragma incremental_vacuum = 1 "
342: } {1 {database disk image is malformed}}
343: }
344:
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;
351: } -corrupt {
352: set nAppend [expr 1024*207 - [file size corrupt.db]]
353: set fd [open corrupt.db r+]
354: seek $fd 0 end
355: puts -nonewline $fd [string repeat x $nAppend]
356: close $fd
357: hexio_write corrupt.db 28 00000000
358: } -test {
359: do_test corrupt2-6.4 {
360: catchsql "
361: $::presql
362: BEGIN EXCLUSIVE;
363: COMMIT;
364: "
365: } {1 {database disk image is malformed}}
366: }
367: }
368:
369:
370: set sqlprep {
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;
382: }
383:
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").
387: #
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"
395: close $fd
396: } -test {
397: do_test corrupt2-7.1 {
398: catchsql " $::presql SELECT b FROM t1 ORDER BY b ASC "
399: } {1 {database disk image is malformed}}
400: }
401:
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.
405: #
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"
416: close $fd
417: } -test {
418: do_test corrupt2-7.1 {
419: catchsql " $::presql SELECT b FROM t1 ORDER BY b DESC "
420: } {1 {database disk image is malformed}}
421: }
422:
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").
426: #
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"
434: close $fd
435: } -test {
436: do_test corrupt2-8.1 {
437: catchsql " $::presql SELECT * FROM t1 WHERE rowid=1000 "
438: } {1 {database disk image is malformed}}
439: }
440:
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);
454: } -corrupt {
455: set fd [open corrupt.db r+]
456: fconfigure $fd -translation binary -encoding binary
457: seek $fd 108
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"
462: close $fd
463: } -test {
464: do_test corrupt2-9.1 {
465: catchsql " $::presql SELECT sql FROM sqlite_master "
466: } {1 {database disk image is malformed}}
467: }
468:
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';
474: } -test {
475: do_test corrupt2-10.1 {
476: catchsql " $::presql SELECT * FROM t2 "
477: } {1 {malformed database schema (t2)}}
478: do_test corrupt2-10.2 {
479: sqlite3_errcode db
480: } {SQLITE_CORRUPT}
481: }
482:
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;
494: DELETE FROM t1;
495: } -corrupt {
496: set offset [expr [file size corrupt.db] - 1024]
497: hexio_write corrupt.db $offset FF
498: hexio_write corrupt.db 24 12345678
499: } -test {
500: do_test corrupt2-11.1 {
501: catchsql " $::presql PRAGMA incremental_vacuum "
502: } {1 {database disk image is malformed}}
503: }
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;
515: DELETE FROM t1;
516: } -corrupt {
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
520: } -test {
521: do_test corrupt2-12.1 {
522: catchsql " $::presql PRAGMA incremental_vacuum "
523: } {1 {database disk image is malformed}}
524: }
525:
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.
531: #
532: corruption_test -tclprep {
533: db eval {
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));
538: }
539: for {set ii 0} {$ii < 10} {incr ii} {
540: db eval " $::presql INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 "
541: }
542: } -corrupt {
543: do_test corrupt2-13.1 {
544: file size corrupt.db
545: } $::sqlite_pending_byte
546: hexio_write corrupt.db [expr $::sqlite_pending_byte+1023] 00
547: hexio_write corrupt.db 28 00000000
548: } -test {
549: do_test corrupt2-13.2 {
550: file size corrupt.db
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}}
555: }
556: }
557:
558: finish_test
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>