/ Check-in [682053d1]
Login

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

Overview
Comment:Fix a problem in sqlite3BtreeDelete() in which deleting an entry from a corrupt database can leave a btree page with zero cells.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 682053d1e603c21b8085c39db618a39b23ec8d2c4d822fd19634db0e03038ea2
User & Date: drh 2018-12-13 21:05:45
Context
2018-12-13
21:11
Add extra tests for database corruption inside the defragmentPage() routine, as dbfuzz2 has found ways for corruption to leak into that point. Add test cases in fuzzdata7.db. check-in: 997b6511 user: drh tags: trunk
21:05
Fix a problem in sqlite3BtreeDelete() in which deleting an entry from a corrupt database can leave a btree page with zero cells. check-in: 682053d1 user: drh tags: trunk
20:49
Add the "decode_hexdb" TCL command to testfixture. Add the dbfuzz001.test module to demonstration how to use decode_hexdb to deserialize a dbtotxt database description for use in a corruption test. check-in: 1f583c53 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to Makefile.in.

  1186   1186   TESTFIXTURE_FLAGS += -DTCLSH_INIT_PROC=sqlite3TestInit
  1187   1187   TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
  1188   1188   TESTFIXTURE_FLAGS += -DBUILD_sqlite
  1189   1189   TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
  1190   1190   TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024
  1191   1191   TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB
  1192   1192   TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DBPAGE_VTAB
         1193  +TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_DESERIALIZE
  1193   1194   
  1194   1195   TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la
  1195   1196   TESTFIXTURE_SRC1 = sqlite3.c
  1196   1197   TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c
  1197   1198   TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION))
  1198   1199   
  1199   1200   testfixture$(TEXE):	$(TESTFIXTURE_SRC)

Changes to Makefile.msc.

  2294   2294   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE=""
  2295   2295   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN)
  2296   2296   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
  2297   2297   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024
  2298   2298   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
  2299   2299   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DBPAGE_VTAB=1
  2300   2300   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_JSON1=1
         2301  +TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_DESERIALIZE=1
  2301   2302   TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS)
  2302   2303   
  2303   2304   TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2)
  2304   2305   TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C)
  2305   2306   !IF $(USE_AMALGAMATION)==0
  2306   2307   TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
  2307   2308   !ELSE

Changes to src/btree.c.

  8619   8619     **
  8620   8620     ** Or, if the current delete will not cause a rebalance, then the cursor
  8621   8621     ** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately
  8622   8622     ** before or after the deleted entry. In this case set bSkipnext to true.  */
  8623   8623     if( bPreserve ){
  8624   8624       if( !pPage->leaf 
  8625   8625        || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
         8626  +     || pPage->nCell==1  /* See dbfuzz001.test for a test case */
  8626   8627       ){
  8627   8628         /* A b-tree rebalance will be required after deleting this entry.
  8628   8629         ** Save the cursor key.  */
  8629   8630         rc = saveCursorKey(pCur);
  8630   8631         if( rc ) return rc;
  8631   8632       }else{
  8632   8633         bSkipnext = 1;

Changes to test/dbfuzz001.test.

    15     15   source $testdir/tester.tcl
    16     16   
    17     17   ifcapable !deserialize {
    18     18     finish_test
    19     19     return
    20     20   }
    21     21   
           22  +# In the following database file, there is 384 bytes of free space
           23  +# on page 8 that does not appear on the freeblock list.
           24  +#
    22     25   do_test dbfuzz001-100 {
    23     26     sqlite3 db {}
    24     27     db deserialize [decode_hexdb {
    25     28       | size 5632 pagesize 512 filename c4.db
    26     29       | page 1 offset 0
    27     30       |      0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00   SQLite format 3.
    28     31       |     16: 02 00 01 01 00 40 20 20 00 00 00 02 00 00 00 0b   .....@  ........
................................................................................
   162    165       |    464: 01 c5 01 0d 41 00 00 48 01 54 00 01 f7 01 ec 01   ....A..H.T......
   163    166       |    480: c5 01 0d 40 00 00 48 01 54 00 01 f7 01 ec 01 c5   ...@..H.T.......
   164    167       |    496: 01 0d 3f 00 00 48 01 54 00 01 f7 01 ec 01 c5 01   ..?..H.T........
   165    168       | end c4.db
   166    169     }]
   167    170     db eval {PRAGMA integrity_check}
   168    171   } {/Fragmentation of 384 bytes reported as 0 on page 8/}
          172  +
          173  +# The DELETE query below deletes the very last cell from page 8.
          174  +# Prior to a certain fix to sqlite3BtreeDelete() and because of the
          175  +# corruption to the freeblock list on page 8, this would fail to
          176  +# cause a rebalance operation, which would leave the btree in a weird
          177  +# state that would lead to segfaults and or assertion faults.
          178  +#
          179  +do_execsql_test dbfuzz001-110 {
          180  +  DELETE FROM t3 WHERE x IS NOT NULL AND +rowid=6;
          181  +} {}
   169    182   
   170    183   finish_test