/ Check-in [97806a78]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix a case where database corruption may cause SQLite to write past the end of a buffer.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:97806a78142b15f89878e25ee70dc5b0524d6793
User & Date: dan 2015-05-25 17:07:29
References
2015-05-26
17:33
Add a cast to one side of the test added by [97806a78] in order to avoid a signed/unsigned comparison warning. check-in: 5b46d2a7 user: dan tags: trunk
Context
2015-05-25
18:48
First code for a new utility program to rerun checks on a large number of fuzzer-generated test cases. check-in: c5b4e363 user: drh tags: fuzzcheck
18:47
Fix a couple of btree asserts that would fail when encountering 32-bit rollover in cell payload size fields (cell payloads this large always indicate corruption). check-in: 8fa0937a user: dan tags: trunk
17:07
Fix a case where database corruption may cause SQLite to write past the end of a buffer. check-in: 97806a78 user: dan tags: trunk
15:03
Fix an assert() in btree routine freeSpace() that may be false if the database is corrupt. check-in: 00a473c5 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
  assert( gap<=65536 );
  /* EVIDENCE-OF: R-29356-02391 If the database uses a 65536-byte page size
  ** and the reserved space is zero (the usual value for reserved space)
  ** then the cell content offset of an empty page wants to be 65536.
  ** However, that integer is too large to be stored in a 2-byte unsigned
  ** integer, so a value of 0 is used in its place. */
  top = get2byteNotZero(&data[hdr+5]);
  if( gap>top ) return SQLITE_CORRUPT_BKPT;

  /* If there is enough space between gap and top for one more cell pointer
  ** array entry offset, and if the freelist is not empty, then search the
  ** freelist looking for a free slot big enough to satisfy the request.
  */
  testcase( gap+2==top );
  testcase( gap+1==top );







|







1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
  assert( gap<=65536 );
  /* EVIDENCE-OF: R-29356-02391 If the database uses a 65536-byte page size
  ** and the reserved space is zero (the usual value for reserved space)
  ** then the cell content offset of an empty page wants to be 65536.
  ** However, that integer is too large to be stored in a 2-byte unsigned
  ** integer, so a value of 0 is used in its place. */
  top = get2byteNotZero(&data[hdr+5]);
  if( gap>top || top>pPage->pBt->usableSize ) return SQLITE_CORRUPT_BKPT;

  /* If there is enough space between gap and top for one more cell pointer
  ** array entry offset, and if the freelist is not empty, then search the
  ** freelist looking for a free slot big enough to satisfy the request.
  */
  testcase( gap+2==top );
  testcase( gap+1==top );

Changes to test/corruptI.test.

126
127
128
129
130
131
132
133





















































134
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
  execsql { DELETE FROM t1 WHERE a=0 }
} {}






















































finish_test








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

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
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
  execsql { DELETE FROM t1 WHERE a=0 }
} {}


#-------------------------------------------------------------------------
# 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}}


finish_test