~ubuntu-branches/ubuntu/saucy/db/saucy-proposed

« back to all changes in this revision

Viewing changes to lang/sql/sqlite/test/corrupt.test

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson
  • Date: 2010-11-05 15:02:09 UTC
  • mfrom: (13.1.12 sid)
  • Revision ID: james.westby@ubuntu.com-20101105150209-ppvyn0619pu014xo
Tags: 5.1.19-1ubuntu1
* Resynchronise with Debian.  Remaining changes:
  - Pass --build/--host to configure to support cross-building, and don't
    override CC.
  - Disable the Java build when cross-building, for now.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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: corrupt.test,v 1.12 2009/07/13 09:41:45 danielk1977 Exp $
 
17
 
 
18
catch {file delete -force test.db test.db-journal test.bu}
 
19
 
 
20
set testdir [file dirname $argv0]
 
21
source $testdir/tester.tcl
 
22
 
 
23
# Do not use a codec for tests in this file, as the database file is
 
24
# manipulated directly using tcl scripts (using the [hexio_write] command).
 
25
#
 
26
do_not_use_codec
 
27
 
 
28
# Construct a large database for testing.
 
29
#
 
30
do_test corrupt-1.1 {
 
31
  execsql {
 
32
    BEGIN;
 
33
    CREATE TABLE t1(x);
 
34
    INSERT INTO t1 VALUES(randstr(100,100));
 
35
    INSERT INTO t1 VALUES(randstr(90,90));
 
36
    INSERT INTO t1 VALUES(randstr(80,80));
 
37
    INSERT INTO t1 SELECT x || randstr(5,5) FROM t1;
 
38
    INSERT INTO t1 SELECT x || randstr(6,6) FROM t1;
 
39
    INSERT INTO t1 SELECT x || randstr(7,7) FROM t1;
 
40
    INSERT INTO t1 SELECT x || randstr(8,8) FROM t1;
 
41
    INSERT INTO t1 VALUES(randstr(3000,3000));
 
42
    INSERT INTO t1 SELECT x || randstr(9,9) FROM t1;
 
43
    INSERT INTO t1 SELECT x || randstr(10,10) FROM t1;
 
44
    INSERT INTO t1 SELECT x || randstr(11,11) FROM t1;
 
45
    INSERT INTO t1 SELECT x || randstr(12,12) FROM t1;
 
46
    CREATE INDEX t1i1 ON t1(x);
 
47
    CREATE TABLE t2 AS SELECT * FROM t1;
 
48
    DELETE FROM t2 WHERE rowid%5!=0;
 
49
    COMMIT;
 
50
  }
 
51
} {}
 
52
integrity_check corrupt-1.2
 
53
 
 
54
# Copy file $from into $to
 
55
#
 
56
proc copy_file {from to} {
 
57
  set f [open $from]
 
58
  fconfigure $f -translation binary
 
59
  set t [open $to w]
 
60
  fconfigure $t -translation binary
 
61
  puts -nonewline $t [read $f [file size $from]]
 
62
  close $t
 
63
  close $f
 
64
}
 
65
 
 
66
# Setup for the tests.  Make a backup copy of the good database in test.bu.
 
67
# Create a string of garbage data that is 256 bytes long.
 
68
#
 
69
copy_file test.db test.bu
 
70
set fsize [file size test.db]
 
71
set junk "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
72
while {[string length $junk]<256} {append junk $junk}
 
73
set junk [string range $junk 0 255]
 
74
 
 
75
# Go through the database and write garbage data into each 256 segment
 
76
# of the file.  Then do various operations on the file to make sure that
 
77
# the database engine can recover gracefully from the corruption.
 
78
#
 
79
for {set i [expr {1*256}]} {$i<$fsize-256} {incr i 256} {
 
80
  set tn [expr {$i/256}]
 
81
  db close
 
82
  copy_file test.bu test.db
 
83
  set fd [open test.db r+]
 
84
  fconfigure $fd -translation binary
 
85
  seek $fd $i
 
86
  puts -nonewline $fd $junk
 
87
  close $fd
 
88
  do_test corrupt-2.$tn.1 {
 
89
    sqlite3 db test.db
 
90
    catchsql {SELECT count(*) FROM sqlite_master}
 
91
    set x {}
 
92
  } {}
 
93
  do_test corrupt-2.$tn.2 {
 
94
    catchsql {SELECT count(*) FROM t1}
 
95
    set x {}
 
96
  } {}
 
97
  do_test corrupt-2.$tn.3 {
 
98
    catchsql {SELECT count(*) FROM t1 WHERE x>'abcdef'}
 
99
    set x {}
 
100
  } {}
 
101
  do_test corrupt-2.$tn.4 {
 
102
    catchsql {SELECT count(*) FROM t2}
 
103
    set x {}
 
104
  } {}
 
105
  do_test corrupt-2.$tn.5 {
 
106
    catchsql {CREATE TABLE t3 AS SELECT * FROM t1}
 
107
    set x {}
 
108
  } {}
 
109
  do_test corrupt-2.$tn.6 {
 
110
    catchsql {DROP TABLE t1}
 
111
    set x {}
 
112
  } {}
 
113
  do_test corrupt-2.$tn.7 {
 
114
    catchsql {PRAGMA integrity_check}
 
115
    set x {}
 
116
  } {}
 
117
 
 
118
  # Check that no page references were leaked.
 
119
  do_test corrupt-2.$tn.8 {
 
120
    set bt [btree_from_db db]
 
121
    db_enter db
 
122
    array set stats [btree_pager_stats $bt]
 
123
    db_leave db
 
124
    set stats(ref)
 
125
  } {0}
 
126
}  
 
127
 
 
128
#------------------------------------------------------------------------
 
129
# For these tests, swap the rootpage entries of t1 (a table) and t1i1 (an
 
130
# index on t1) in sqlite_master. Then perform a few different queries
 
131
# and make sure this is detected as corruption.
 
132
#
 
133
do_test corrupt-3.1 {
 
134
  db close
 
135
  copy_file test.bu test.db
 
136
  sqlite3 db test.db
 
137
  list
 
138
} {}
 
139
do_test corrupt-3.2 {
 
140
  set t1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1i1'}]
 
141
  set t1i1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1'}]
 
142
  set cookie [expr [execsql {PRAGMA schema_version}] + 1]
 
143
  execsql "
 
144
    PRAGMA writable_schema = 1;
 
145
    UPDATE sqlite_master SET rootpage = $t1_r WHERE name = 't1';
 
146
    UPDATE sqlite_master SET rootpage = $t1i1_r WHERE name = 't1i1';
 
147
    PRAGMA writable_schema = 0;
 
148
    PRAGMA schema_version = $cookie;
 
149
  "
 
150
} {}
 
151
 
 
152
# This one tests the case caught by code in checkin [2313].
 
153
do_test corrupt-3.3 {
 
154
  db close
 
155
  sqlite3 db test.db
 
156
  catchsql {
 
157
    INSERT INTO t1 VALUES('abc');
 
158
  }
 
159
} {1 {database disk image is malformed}}
 
160
do_test corrupt-3.4 {
 
161
  db close
 
162
  sqlite3 db test.db
 
163
  catchsql {
 
164
    SELECT * FROM t1;
 
165
  }
 
166
} {1 {database disk image is malformed}}
 
167
do_test corrupt-3.5 {
 
168
  db close
 
169
  sqlite3 db test.db
 
170
  catchsql {
 
171
    SELECT * FROM t1 WHERE oid = 10;
 
172
  }
 
173
} {1 {database disk image is malformed}}
 
174
do_test corrupt-3.6 {
 
175
  db close
 
176
  sqlite3 db test.db
 
177
  catchsql {
 
178
    SELECT * FROM t1 WHERE x = 'abcde';
 
179
  }
 
180
} {1 {database disk image is malformed}}
 
181
 
 
182
do_test corrupt-4.1 {
 
183
  db close
 
184
  file delete -force test.db test.db-journal
 
185
  sqlite3 db test.db
 
186
  execsql {
 
187
    PRAGMA page_size = 1024;
 
188
    CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
 
189
  }
 
190
  for {set i 0} {$i < 10} {incr i} {
 
191
    set text [string repeat $i 220]
 
192
    execsql { INSERT INTO t1 VALUES($i, $text) }
 
193
  }
 
194
  execsql { CREATE INDEX i1 ON t1(b) }
 
195
} {}
 
196
do_test corrupt-4.2 {
 
197
  set iRoot [db one {SELECT rootpage FROM sqlite_master WHERE name = 'i1'}]
 
198
  set iOffset [hexio_get_int [hexio_read test.db [expr 12+($iRoot-1)*1024] 2]]
 
199
  set data [hexio_render_int32 [expr $iRoot - 1]]
 
200
  hexio_write test.db [expr ($iRoot-1)*1024 + $iOffset] $data
 
201
  db close
 
202
  sqlite3 db test.db
 
203
 
 
204
  # The following DELETE statement attempts to delete a cell stored on the
 
205
  # root page of index i1. After this cell is deleted it must be replaced
 
206
  # by a cell retrieved from the child page (a leaf) of the deleted cell.
 
207
  # This will fail, as the block modified the database image so that the
 
208
  # child page of the deleted cell is from a table (intkey) b-tree, not an
 
209
  # index b-tree as expected. At one point this was causing an assert()
 
210
  # to fail.
 
211
  catchsql { DELETE FROM t1 WHERE rowid = 3 }
 
212
} {1 {database disk image is malformed}}
 
213
 
 
214
do_test corrupt-5.1 {
 
215
  db close
 
216
  file delete -force test.db test.db-journal
 
217
  sqlite3 db test.db
 
218
 
 
219
  execsql { PRAGMA page_size = 1024 }
 
220
  set ct "CREATE TABLE t1(c0 "
 
221
  set i 0
 
222
  while {[string length $ct] < 950} { append ct ", c[incr i]" }
 
223
  append ct ")"
 
224
  execsql $ct
 
225
} {}
 
226
 
 
227
do_test corrupt-5.2 {
 
228
  db close
 
229
  hexio_write test.db 108 00000000 
 
230
  sqlite3 db test.db
 
231
  catchsql { SELECT * FROM sqlite_master }
 
232
} {1 {database disk image is malformed}}
 
233
 
 
234
# At one point, the specific corruption caused by this test case was
 
235
# causing a buffer overwrite. Although a crash was never demonstrated,
 
236
# running this testcase under valgrind revealed the problem.
 
237
do_test corrupt-6.1 {
 
238
  db close
 
239
  file delete -force test.db test.db-journal
 
240
  sqlite3 db test.db
 
241
  execsql { 
 
242
    PRAGMA page_size = 1024; CREATE TABLE t1(x);
 
243
  }
 
244
 
 
245
  # The root page of t1 is 1024 bytes in size. The header is 8 bytes, and
 
246
  # each of the cells inserted by the following INSERT statements consume
 
247
  # 16 bytes (including the 2 byte cell-offset array entry). So the page
 
248
  # can contain up to 63 cells.
 
249
  for {set i 0} {$i < 63} {incr i} {
 
250
    execsql { INSERT INTO t1 VALUES( randomblob(10) ) }
 
251
  }
 
252
 
 
253
  # Free the cell stored right at the end of the page (at offset pgsz-14).
 
254
  execsql { DELETE FROM t1 WHERE rowid=1 }
 
255
  set rootpage [db one {SELECT rootpage FROM sqlite_master WHERE name = 't1'}]
 
256
  db close
 
257
 
 
258
  set offset [expr ($rootpage * 1024)-14+2]
 
259
  hexio_write test.db $offset 00FF
 
260
  sqlite3 db test.db 
 
261
 
 
262
  catchsql { INSERT INTO t1 VALUES( randomblob(10) ) }
 
263
} {1 {database disk image is malformed}}
 
264
 
 
265
ifcapable oversize_cell_check {
 
266
  db close
 
267
  file delete -force test.db test.db-journal
 
268
  sqlite3 db test.db
 
269
  execsql { 
 
270
    PRAGMA page_size = 1024; CREATE TABLE t1(x);
 
271
  }
 
272
 
 
273
  do_test corrupt-7.1 {
 
274
    for {set i 0} {$i < 39} {incr i} {
 
275
      execsql {
 
276
        INSERT INTO t1 VALUES(X'000100020003000400050006000700080009000A');
 
277
      }
 
278
    }
 
279
  } {}
 
280
  db close
 
281
  
 
282
  # Corrupt the root page of table t1 so that the first offset in the 
 
283
  # cell-offset array points to the data for the SQL blob associated with
 
284
  # record (rowid=10). The root page still passes the checks in btreeInitPage(),
 
285
  # because the start of said blob looks like the start of a legitimate 
 
286
  # page cell.
 
287
  #
 
288
  # Test case cc-2 overwrites the blob so that it no longer looks like a
 
289
  # real cell. But, by the time it is overwritten, btreeInitPage() has already
 
290
  # initialized the root page, so no corruption is detected.
 
291
  #
 
292
  # Test case cc-3 inserts an extra record into t1, forcing balance-deeper
 
293
  # to run. After copying the contents of the root page to the new child,
 
294
  # btreeInitPage() is called on the child. This time, it detects corruption
 
295
  # (because the start of the blob associated with the (rowid=10) record
 
296
  # no longer looks like a real cell). At one point the code assumed that 
 
297
  # detecting corruption was not possible at that point, and an assert() failed.
 
298
  #
 
299
  set fd [open test.db r+]
 
300
  fconfigure $fd -translation binary -encoding binary
 
301
  seek $fd [expr 1024+8]
 
302
  puts -nonewline $fd "\x03\x14"
 
303
  close $fd
 
304
  
 
305
  sqlite3 db test.db
 
306
  do_test corrupt-7.2 {
 
307
    execsql { 
 
308
      UPDATE t1 SET x = X'870400020003000400050006000700080009000A' 
 
309
      WHERE rowid = 10;
 
310
    }
 
311
  } {}
 
312
  do_test corrupt-7.3 {
 
313
    catchsql {
 
314
      INSERT INTO t1 VALUES(X'000100020003000400050006000700080009000A');
 
315
    }
 
316
  } {1 {database disk image is malformed}}
 
317
}
 
318
 
 
319
db close
 
320
file delete -force test.db test.db-journal
 
321
do_test corrupt-8.1 {
 
322
  sqlite3 db test.db
 
323
  execsql {
 
324
    PRAGMA page_size = 1024;
 
325
    PRAGMA secure_delete = on;
 
326
    PRAGMA auto_vacuum = 0;
 
327
    CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
 
328
    INSERT INTO t1 VALUES(5, randomblob(1900));
 
329
  }
 
330
 
 
331
  hexio_write test.db 2044 [hexio_render_int32 2]
 
332
  hexio_write test.db 24   [hexio_render_int32 45]
 
333
 
 
334
  catchsql { INSERT OR REPLACE INTO t1 VALUES(5, randomblob(1900)) }
 
335
} {1 {database disk image is malformed}}
 
336
 
 
337
db close
 
338
file delete -force test.db test.db-journal
 
339
do_test corrupt-8.2 {
 
340
  sqlite3 db test.db
 
341
  execsql {
 
342
    PRAGMA page_size = 1024;
 
343
    PRAGMA secure_delete = on;
 
344
    PRAGMA auto_vacuum = 0;
 
345
    CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
 
346
    INSERT INTO t1 VALUES(5, randomblob(900));
 
347
    INSERT INTO t1 VALUES(6, randomblob(900));
 
348
  }
 
349
 
 
350
  hexio_write test.db 2047 FF
 
351
  hexio_write test.db 24   [hexio_render_int32 45]
 
352
 
 
353
  catchsql { INSERT INTO t1 VALUES(4, randomblob(1900)) }
 
354
} {1 {database disk image is malformed}}
 
355
 
 
356
finish_test