/ Check-in [e5f5ef00]
Login

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

Overview
Comment:Detect and suppress an endless loops in clearDatabasePage() that might result from a corrupt database file. This is an edited cherry-pick from [30011ad2f55c] and [395bb3e677a].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | apple-osx-385
Files: files | file ages | folders
SHA1: e5f5ef008dba226ee8611dedaa8d1c34d14227c9
User & Date: drh 2015-04-06 22:05:23
Context
2015-04-06
22:05
Detect and suppress an endless loops in clearDatabasePage() that might result from a corrupt database file. This is an edited cherry-pick from [30011ad2f55c] and [395bb3e677a]. Leaf check-in: e5f5ef00 user: drh tags: apple-osx-385
2015-04-01
13:21
Improved detection and suppression of endless loops in clearDatabasePage(). check-in: 30011ad2 user: drh tags: trunk
2014-06-05
12:53
Merge in the 3.8.5 release changes and the FTS integrity-check fix. check-in: 2dbdfa51 user: drh tags: apple-osx
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

  7422   7422     assert( sqlite3_mutex_held(pBt->mutex) );
  7423   7423     if( pgno>btreePagecount(pBt) ){
  7424   7424       return SQLITE_CORRUPT_BKPT;
  7425   7425     }
  7426   7426   
  7427   7427     rc = getAndInitPage(pBt, pgno, &pPage, 0);
  7428   7428     if( rc ) return rc;
         7429  +  if( pPage->bBusy ){
         7430  +    rc = SQLITE_CORRUPT_BKPT;
         7431  +    goto cleardatabasepage_out;
         7432  +  }
         7433  +  pPage->bBusy = 1;
  7429   7434     hdr = pPage->hdrOffset;
  7430   7435     for(i=0; i<pPage->nCell; i++){
  7431   7436       pCell = findCell(pPage, i);
  7432   7437       if( !pPage->leaf ){
  7433   7438         rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange);
  7434   7439         if( rc ) goto cleardatabasepage_out;
  7435   7440       }
................................................................................
  7446   7451     if( freePageFlag ){
  7447   7452       freePage(pPage, &rc);
  7448   7453     }else if( (rc = sqlite3PagerWrite(pPage->pDbPage))==0 ){
  7449   7454       zeroPage(pPage, pPage->aData[hdr] | PTF_LEAF);
  7450   7455     }
  7451   7456   
  7452   7457   cleardatabasepage_out:
         7458  +  pPage->bBusy = 0;
  7453   7459     releasePage(pPage);
  7454   7460     return rc;
  7455   7461   }
  7456   7462   
  7457   7463   /*
  7458   7464   ** Delete all information from a single table in the database.  iTable is
  7459   7465   ** the page number of the root of the table.  After this routine returns,

Changes to src/btreeInt.h.

   275    275     u8 nOverflow;        /* Number of overflow cell bodies in aCell[] */
   276    276     u8 intKey;           /* True if intkey flag is set */
   277    277     u8 leaf;             /* True if leaf flag is set */
   278    278     u8 hasData;          /* True if this page stores data */
   279    279     u8 hdrOffset;        /* 100 for page 1.  0 otherwise */
   280    280     u8 childPtrSize;     /* 0 if leaf==1.  4 if leaf==0 */
   281    281     u8 max1bytePayload;  /* min(maxLocal,127) */
          282  +  u8 bBusy;            /* Prevent endless loop in clearDatabasePage() */
   282    283     u16 maxLocal;        /* Copy of BtShared.maxLocal or BtShared.maxLeaf */
   283    284     u16 minLocal;        /* Copy of BtShared.minLocal or BtShared.minLeaf */
   284    285     u16 cellOffset;      /* Index in aData of first cell pointer */
   285    286     u16 nFree;           /* Number of free bytes on the page */
   286    287     u16 nCell;           /* Number of cells on this page, local and ovfl */
   287    288     u16 maskPage;        /* Mask for page offset */
   288    289     u16 aiOvfl[5];       /* Insert the i-th overflow cell before the aiOvfl-th

Added test/corruptJ.test.

            1  +# 2015-03-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  +#
           12  +# Corruption consisting of a database page that thinks it is a child
           13  +# of itself.
           14  +#
           15  +
           16  +set testdir [file dirname $argv0]
           17  +source $testdir/tester.tcl
           18  +set testprefix corruptJ
           19  +
           20  +if {[permutation]=="mmap"} {
           21  +  finish_test
           22  +  return
           23  +}
           24  +
           25  +# Do not use a codec for tests in this file, as the database file is
           26  +# manipulated directly using tcl scripts (using the [hexio_write] command).
           27  +#
           28  +do_not_use_codec
           29  +database_may_be_corrupt
           30  +
           31  +# Initialize the database.
           32  +#
           33  +do_execsql_test 1.1 {
           34  +  PRAGMA page_size=1024;
           35  +  PRAGMA auto_vacuum=0;
           36  +  CREATE TABLE t1(a,b);
           37  +  WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<10)
           38  +    INSERT INTO t1(a,b) SELECT i, zeroblob(700) FROM c;
           39  +} {}
           40  +db close
           41  +
           42  +# Corrupt the root page of the t1 table such that the left-child pointer
           43  +# for the very first cell points back to the root.  Then try to DROP the
           44  +# table.  The clearDatabasePage() routine should not loop.
           45  +#
           46  +do_test 1.2 {
           47  +  hexio_write test.db [expr {2*1024-2}] 02
           48  +  sqlite3 db test.db
           49  +  catchsql { DROP TABLE t1 }
           50  +} {1 {database disk image is malformed}}
           51  +
           52  +# Similar test using a WITHOUT ROWID table
           53  +#
           54  +do_test 2.1 {
           55  +  db close
           56  +  forcedelete test.db
           57  +  sqlite3 db test.db
           58  +  db eval {
           59  +    PRAGMA page_size=1024;
           60  +    PRAGMA auto_vacuum=0;
           61  +    CREATE TABLE t1(a,b,PRIMARY KEY(a,b)) WITHOUT ROWID;
           62  +    WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<100)
           63  +      INSERT INTO t1(a,b) SELECT i, zeroblob(200) FROM c;
           64  +  }
           65  +} {}
           66  +
           67  +# The table is three levels deep.  Corrupt the left child of an intermediate
           68  +# page so that it points back to the root page.
           69  +#
           70  +do_test 2.2 {
           71  +  db close
           72  +  hexio_read test.db [expr {9*1024+391}] 8
           73  +} {0000000B814D0401}
           74  +do_test 2.2b {
           75  +  hexio_write test.db [expr {9*1024+391}] 00000002
           76  +  sqlite3 db test.db
           77  +  catchsql { PRAGMA secure_delete=ON; DROP TABLE t1; }
           78  +} {1 {database disk image is malformed}}
           79  +
           80  +finish_test