summaryrefslogtreecommitdiffstats
path: root/test/corruptI.test
blob: 65ef3762586aab1da2da0b33788ee19c439503c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# 2014-01-20
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix corruptI

if {[permutation]=="mmap"} {
  finish_test
  return
}

# This module uses hard-coded offsets which do not work if the reserved_bytes
# value is nonzero.
if {[nonzero_reserved_bytes]} {finish_test; return;}

database_may_be_corrupt

# Initialize the database.
#
do_execsql_test 1.1 {
  PRAGMA page_size=1024;
  PRAGMA auto_vacuum=0;
  CREATE TABLE t1(a);
  CREATE INDEX i1 ON t1(a);
  INSERT INTO t1 VALUES('abcdefghijklmnop');
} {}
db close

do_test 1.2 {
  set offset [hexio_get_int [hexio_read test.db [expr 2*1024 + 8] 2]]
  set off [expr 2*1024 + $offset + 1]
  hexio_write test.db $off 7f06
  sqlite3 db test.db
  catchsql { SELECT * FROM t1 WHERE a = 10 }
} {0 {}}

do_test 1.3 {
  db close
  set offset [hexio_get_int [hexio_read test.db [expr 2*1024 + 8] 2]]
  set off [expr 2*1024 + $offset + 1]
  hexio_write test.db $off FFFF7f02
  sqlite3 db test.db
  catchsql { SELECT * FROM t1 WHERE a = 10 }
} {1 {database disk image is malformed}}

do_test 2.0 {
  execsql {
    CREATE TABLE r(x);
    INSERT INTO r VALUES('ABCDEFGHIJK');
    CREATE INDEX r1 ON r(x);
  }
  set pg [db one {SELECT rootpage FROM sqlite_master WHERE name = 'r1'}]
} {5}

do_test 2.1 {
  db close
  set offset [hexio_get_int [hexio_read test.db [expr (5-1)*1024 + 8] 2]]
  set off [expr (5-1)*1024 + $offset + 1]
  hexio_write test.db $off FFFF0004
  sqlite3 db test.db
  catchsql { SELECT * FROM r WHERE x >= 10.0 }
} {1 {database disk image is malformed}}

do_test 2.2 {
  catchsql { SELECT * FROM r WHERE x >= 10 }
} {1 {database disk image is malformed}}

if {[db one {SELECT sqlite_compileoption_used('ENABLE_OVERSIZE_CELL_CHECK')}]} {
  # The following tests only work if OVERSIZE_CELL_CHECK is disabled
} else {
  reset_db
  do_execsql_test 3.1 {
     PRAGMA auto_vacuum=0;
     PRAGMA page_size = 512;
     CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
     WITH s(a, b) AS (
       SELECT 2, 'abcdefghij'
       UNION ALL
       SELECT a+2, b FROM s WHERe a < 40
     )
     INSERT INTO t1 SELECT * FROM s;
   } {}
   
   do_test 3.2 {
     hexio_write test.db [expr 512+3] 0054
     db close
     sqlite3 db test.db
     execsql { INSERT INTO t1 VALUES(5, 'klmnopqrst') }
     execsql { INSERT INTO t1 VALUES(7, 'klmnopqrst') }
   } {}
   
   db close
   sqlite3 db test.db
   do_catchsql_test 3.3 {
     INSERT INTO t1 VALUES(9, 'klmnopqrst');
   } {1 {database disk image is malformed}}
} ;# end-if !defined(ENABLE_OVERSIZE_CELL_CHECK)


#-------------------------------------------------------------------------
# Test that an assert() failure discovered by AFL corrupt database file
# testing has been fixed.
#
reset_db
do_execsql_test 4.0 {
  PRAGMA page_size = 65536;
  PRAGMA autovacuum = 0;
  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
  INSERT INTO t1 VALUES(-1, 'abcdefghij');
  INSERT INTO t1 VALUES(0, 'abcdefghij');
}

set root [db one {SELECT rootpage FROM sqlite_master}]
set offset [expr ($root-1) * 65536]

do_test 4.1 {
  db close
  hexio_write test.db [expr $offset + 8 + 2] 0000
  hexio_write test.db [expr $offset + 5] 0000
  sqlite3 db test.db
  catchsql { DELETE FROM t1 WHERE a=0 }
} {1 {database disk image is malformed}}


#-------------------------------------------------------------------------
# Database properties:
#
#   * Incremental vacuum mode.
#   * Database root table has a single leaf page.
#   * Free list consists of a single trunk page.
#
# The db is then corrupted by adding the root table leaf page as a free-list
# leaf page (so that it is referenced twice).
#
# Then, a new table is created. The new root page is the current free-list
# trunk. This means that the root table leaf page is made into the new
# free list trunk, which corrupts its header. Then, when the new entry is
# inserted into the root table, things would get chaotic.
#
reset_db
do_test 5.0 {
  execsql {
    PRAGMA page_size = 512;
    PRAGMA auto_vacuum = 2;
  }
  for {set i 3} {1} {incr i} {
    execsql "CREATE TABLE t${i}(x)"
    if {[db one {PRAGMA page_count}]>$i} break
  }
  set nPage [db one {PRAGMA page_count}]
  execsql {
    CREATE TABLE t100(x);
    DROP TABLE t100;
  }
} {}

do_execsql_test 5.1 { 
  PRAGMA page_count 
} [expr $nPage+1]

do_test 5.2 { 
  # The last page of the db is now the only leaf of the sqlite_master table.
  # Corrupt the db by adding it to the free-list as well (the second last
  # page of the db is the free-list trunk).
  db close
  hexio_write test.db [expr 512*($nPage-1)] [
    format "%.8X%.8X%.8X" 0 1 [expr $nPage+1]
  ]
} {12}

do_test 5.3 {
  sqlite3 db test.db
  catchsql { CREATE TABLE tx(x); }
} {1 {database disk image is malformed}}


#-------------------------------------------------------------------------
# Set the payload size of a cell to just less than 2^32 bytes (not
# possible in an uncorrupted db). Then try to delete the cell. At one
# point this led to an integer overflow that caused an assert() to fail.
#
reset_db
do_execsql_test 6.0 {
  PRAGMA page_size = 512;
  PRAGMA auto_vacuum=0;
  CREATE TABLE t1(x);
  INSERT INTO t1 VALUES(zeroblob(300));
  INSERT INTO t1 VALUES(zeroblob(600));
} {}
do_test 6.1 {
  db close
  hexio_write test.db 616 8FFFFFFF7F02
  sqlite3 db test.db
  execsql { DELETE FROM t1 WHERE rowid=2 }
} {}

#-------------------------------------------------------------------------
# See what happens if the sqlite_master entry associated with a PRIMARY
# KEY or UNIQUE index is removed. 
#
reset_db
do_execsql_test 7.0 {
  PRAGMA auto_vacuum=0;
  CREATE TABLE t1(x PRIMARY KEY, y);
  INSERT INTO t1 VALUES('a', 'A');
  INSERT INTO t1 VALUES('b', 'A');
  INSERT INTO t1 VALUES('c', 'A');
  SELECT name FROM sqlite_master;
} {t1 sqlite_autoindex_t1_1}
sqlite3_db_config db DEFENSIVE 0
do_execsql_test 7.1 {
  PRAGMA writable_schema = 1;
  DELETE FROM sqlite_master WHERE name = 'sqlite_autoindex_t1_1';
}
do_test 7.2 {
  db close
  sqlite3 db test.db
  catchsql { UPDATE t1 SET x='d' AND y='D' WHERE rowid = 2 }
} {1 {database disk image is malformed}}

#-------------------------------------------------------------------------
# At one point an assert() would fail if attempt was made to free page 1.
#
reset_db
do_execsql_test 8.0 {
  PRAGMA auto_vacuum=0;
  CREATE TABLE t1(x);
  INSERT INTO t1 VALUES(zeroblob(300));
  INSERT INTO t1 VALUES(zeroblob(300));
  INSERT INTO t1 VALUES(zeroblob(300));
  INSERT INTO t1 VALUES(zeroblob(300));
} {}

do_test 8.1 {
  db close
  hexio_write test.db [expr 1024 + 8] 00000001
  sqlite3 db test.db
  catchsql { DELETE FROM t1 }
} {1 {database disk image is malformed}}

do_test 8.2 {
  db close
  sqlite3 db test.db
  execsql { PRAGMA integrity_check }
} {/.*in database main.*/}


finish_test