xref: /sqlite-3.40.0/test/corrupt2.test (revision 3aa4b67f)
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