]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Handle a few obscure problems that could manifest if a database corrupted in a certai...
authordan <dan@noemail.net>
Mon, 20 Jan 2014 18:25:44 +0000 (18:25 +0000)
committerdan <dan@noemail.net>
Mon, 20 Jan 2014 18:25:44 +0000 (18:25 +0000)
FossilOrigin-Name: eba8a564e62f84a9620008beead80081fe90a1b7

manifest
manifest.uuid
src/btree.c
test/corruptH.test [new file with mode: 0644]

index 6691d6c5ff651f840039b8653eedd3c117b8524f..23358e38e55a21fb6ffb07c865c492525b7f6a89 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Remove\san\sunused\s#define\sand\sadd\san\sassert(),\sboth\sassociated\swith\sWITH\slogic.
-D 2014-01-20T14:58:55.056
+C Handle\sa\sfew\sobscure\sproblems\sthat\scould\smanifest\sif\sa\sdatabase\scorrupted\sin\sa\scertain\sway\swas\swritten\sby\sa\sconnection\sin\sthe\smiddle\sof\sa\sSELECT\sstatement\son\sthe\ssame\sdb.
+D 2014-01-20T18:25:44.841
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -166,7 +166,7 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
 F src/backup.c a729e63cf5cd1829507cb7b8e89f99b95141bb53
 F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
 F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
-F src/btree.c c15e1722696b66c4029c487acfb830b0985bf142
+F src/btree.c 02e1a4e71d8fc37e9fd5216c15d989d148a77c87
 F src/btree.h a61ddebc78c66795a2b93181321a116746302cc9
 F src/btreeInt.h f038e818bfadf75afbd09819ed93c26a333d39e0
 F src/build.c 7e6c275ab1731510d6f793d0f88373ab3e858e69
@@ -406,6 +406,7 @@ F test/corruptD.test b3c205fac7952b1de645ce44bb02335cd9e3e040
 F test/corruptE.test 193b4ca4e927e77c1d5f4f56203ddc998432a7ee
 F test/corruptF.test be9fde98e4c93648f1ba52b74e5318edc8f59fe4
 F test/corruptG.test c150f156dace653c00a121ad0f5772a0568c41ba
+F test/corruptH.test 0a247f3dc8a8f3578db5f639d86c6bb4d520207f
 F test/count.test 42a251178e32f617eda33f76236a7f79825a50b5
 F test/coveridxscan.test cdb47d01acc4a634a34fd25abe85189e0d0f1e62
 F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f
@@ -1151,7 +1152,7 @@ F tool/vdbe-compress.tcl 0cf56e9263a152b84da86e75a5c0cdcdb7a47891
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
-P 8a973912e98c9b1bb9d3f914527d35c1e7f2011a
-R bcd03400ea7bd2e959020317f5125b8b
-U drh
-Z f5ee43186682e26d4e29624d49787797
+P a06235e0f6aa1e8fefa3f2873ee035eac9dac750
+R 05b4cba80caa92c648883df94b9ec5d5
+U dan
+Z 37a297a819c7d62ae4b0e647e3e25c85
index 9e604f5cb8586b7c119247e45a050ac4161288bd..ad1f20fbcfe0a6ff2b26d321e986f638ff854dd8 100644 (file)
@@ -1 +1 @@
-a06235e0f6aa1e8fefa3f2873ee035eac9dac750
\ No newline at end of file
+eba8a564e62f84a9620008beead80081fe90a1b7
\ No newline at end of file
index e28a97c846af0ed5fd2e15bb32b0d76af1d2053b..032f3d8c78605f40fe3c304d1d8fb069f09d4d10 100644 (file)
@@ -3754,7 +3754,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
     int iPage = pCur->iPage;
     memset(&info, 0, sizeof(info));
     btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info);
-    assert( memcmp(&info, &pCur->info, sizeof(info))==0 );
+    assert( CORRUPT_DB || memcmp(&info, &pCur->info, sizeof(info))==0 );
   }
 #else
   #define assertCellInfo(x)
@@ -4390,26 +4390,24 @@ static int moveToRoot(BtCursor *pCur){
       return rc;
     }
     pCur->iPage = 0;
-
-    /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
-    ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
-    ** NULL, the caller expects a table b-tree. If this is not the case,
-    ** return an SQLITE_CORRUPT error.  */
-    assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 );
-    if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){
-      return SQLITE_CORRUPT_BKPT;
-    }
   }
-
-  /* Assert that the root page is of the correct type. This must be the
-  ** case as the call to this function that loaded the root-page (either
-  ** this call or a previous invocation) would have detected corruption 
-  ** if the assumption were not true, and it is not possible for the flags 
-  ** byte to have been modified while this cursor is holding a reference
-  ** to the page.  */
   pRoot = pCur->apPage[0];
   assert( pRoot->pgno==pCur->pgnoRoot );
-  assert( pRoot->isInit && (pCur->pKeyInfo==0)==pRoot->intKey );
+
+  /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
+  ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
+  ** NULL, the caller expects a table b-tree. If this is not the case,
+  ** return an SQLITE_CORRUPT error. 
+  **
+  ** Earlier versions of SQLite assumed that this test could not fail
+  ** if the root page was already loaded when this function was called (i.e.
+  ** if pCur->iPage>=0). But this is not so if the database is corrupted 
+  ** in such a way that page pRoot is linked into a second b-tree table 
+  ** (or the freelist).  */
+  assert( pRoot->intKey==1 || pRoot->intKey==0 );
+  if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){
+    return SQLITE_CORRUPT_BKPT;
+  }
 
   pCur->aiIdx[0] = 0;
   pCur->info.nSize = 0;
@@ -5251,6 +5249,7 @@ end_allocate_page:
   if( rc==SQLITE_OK ){
     if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
       releasePage(*ppPage);
+      *ppPage = 0;
       return SQLITE_CORRUPT_BKPT;
     }
     (*ppPage)->isInit = 0;
diff --git a/test/corruptH.test b/test/corruptH.test
new file mode 100644 (file)
index 0000000..23f8063
--- /dev/null
@@ -0,0 +1,150 @@
+# 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 corruptH
+
+# Do not use a codec for tests in this file, as the database file is
+# manipulated directly using tcl scripts (using the [hexio_write] command).
+#
+do_not_use_codec
+database_may_be_corrupt
+
+# Initialize the database.
+#
+do_execsql_test 1.1 {
+  PRAGMA page_size=1024;
+
+  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+  INSERT INTO t1 VALUES(1, 'one');
+  INSERT INTO t1 VALUES(2, 'two');
+
+  CREATE TABLE t2(x);
+  INSERT INTO t2 VALUES(randomblob(200));
+  INSERT INTO t2 SELECT randomblob(200) FROM t2;
+  INSERT INTO t2 SELECT randomblob(200) FROM t2;
+  INSERT INTO t2 SELECT randomblob(200) FROM t2;
+  INSERT INTO t2 SELECT randomblob(200) FROM t2;
+  INSERT INTO t2 SELECT randomblob(200) FROM t2;
+  INSERT INTO t2 SELECT randomblob(200) FROM t2;
+} {}
+
+# Corrupt the file so that the root page of t1 is also linked into t2 as
+# a leaf page.
+#
+do_test 1.2 {
+  db eval { SELECT name, rootpage FROM sqlite_master } { 
+    set r($name) $rootpage 
+  }
+  db close
+  hexio_write test.db [expr {($r(t2)-1)*1024 + 11}] [format %.2X $r(t1)]
+  sqlite3 db test.db
+} {}
+
+do_test 1.3 {
+  db eval { PRAGMA secure_delete=1 }
+  list [catch {
+    db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
+      db eval { DELETE FROM t2 }
+    }
+  } msg] $msg
+} {1 {database disk image is malformed}}
+
+#-------------------------------------------------------------------------
+reset_db
+
+# Initialize the database.
+#
+do_execsql_test 2.1 {
+  PRAGMA page_size=1024;
+
+  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+  INSERT INTO t1 VALUES(1, 'one');
+  INSERT INTO t1 VALUES(2, 'two');
+
+  CREATE TABLE t3(x);
+
+  CREATE TABLE t2(x PRIMARY KEY) WITHOUT ROWID;
+  INSERT INTO t2 VALUES(randomblob(100));
+
+  DROP TABLE t3;
+} {}
+
+do_test 2.2 {
+  db eval { SELECT name, rootpage FROM sqlite_master } { 
+    set r($name) $rootpage 
+  }
+  db close
+  set fl [hexio_get_int [hexio_read test.db 32 4]]
+
+  hexio_write test.db [expr {($fl-1) * 1024 + 0}] 00000000 
+  hexio_write test.db [expr {($fl-1) * 1024 + 4}] 00000001 
+  hexio_write test.db [expr {($fl-1) * 1024 + 8}] [format %.8X $r(t1)]
+  hexio_write test.db 36 00000002
+
+  sqlite3 db test.db
+} {}
+
+do_test 2.3 {
+  list [catch {
+  db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
+    db eval { 
+      INSERT INTO t2 SELECT randomblob(100) FROM t2;
+      INSERT INTO t2 SELECT randomblob(100) FROM t2;
+      INSERT INTO t2 SELECT randomblob(100) FROM t2;
+      INSERT INTO t2 SELECT randomblob(100) FROM t2;
+      INSERT INTO t2 SELECT randomblob(100) FROM t2;
+    }
+  }
+  } msg] $msg
+} {1 {database disk image is malformed}}
+
+#-------------------------------------------------------------------------
+reset_db
+
+# Initialize the database.
+#
+do_execsql_test 3.1 {
+  PRAGMA page_size=1024;
+
+  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+  INSERT INTO t1 VALUES(1, 'one');
+  INSERT INTO t1 VALUES(2, 'two');
+
+  CREATE TABLE t2(c INTEGER PRAGMA KEY, d);
+  INSERT INTO t2 VALUES(1, randomblob(1100));
+} {}
+
+do_test 3.2 {
+  db eval { SELECT name, rootpage FROM sqlite_master } { 
+    set r($name) $rootpage 
+  }
+  db close
+
+  hexio_write test.db [expr {($r(t2)-1) * 1024 + 1020}] 00000002
+
+  sqlite3 db test.db
+} {}
+
+do_test 3.3 {
+  list [catch {
+  db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
+    db eval { 
+      DELETE FROM t2 WHERE c=1;
+    }
+  }
+  } msg] $msg
+} {1 {database disk image is malformed}}
+
+finish_test
+