]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Prevent a possible infinite loop when trying to DROP a table from
authordrh <drh@noemail.net>
Mon, 30 Mar 2015 23:43:56 +0000 (23:43 +0000)
committerdrh <drh@noemail.net>
Mon, 30 Mar 2015 23:43:56 +0000 (23:43 +0000)
a corrupt database.

FossilOrigin-Name: 395bb3e677a6551b06ba96fc58c393132b93d1e8

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

index 98c814021d1b4f824575ef1ff0a17906edb7438b..830165f1d1234912943d4ba3c1e07a7bf3d5b0b8 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Enhancements\sto\sOSTRACE\susage\sin\sthe\sWin32\sVFS.
-D 2015-03-27T18:20:25.776
+C Prevent\sa\spossible\sinfinite\sloop\swhen\strying\sto\sDROP\sa\stable\sfrom\na\scorrupt\sdatabase.
+D 2015-03-30T23:43:56.191
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 88a3e6261286db378fdffa1124cad11b3c05f5bb
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -173,7 +173,7 @@ F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
 F src/backup.c ff743689c4d6c5cb55ad42ed9d174b2b3e71f1e3
 F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
 F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
-F src/btree.c 4f305e554d7d207375c3e29ab0335bd5a473a125
+F src/btree.c e565971caa0265d3cabc8b15d7017899a7814051
 F src/btree.h 969adc948e89e449220ff0ff724c94bb2a52e9f1
 F src/btreeInt.h 2bfefc01875d8da066504c233ec259fcb3b2ef72
 F src/build.c 0419bba592c22f6d00e6d57a2ca7136720d02c1a
@@ -433,6 +433,7 @@ F test/corruptF.test be9fde98e4c93648f1ba52b74e5318edc8f59fe4
 F test/corruptG.test 1ab3bf97ee7bdba70e0ff3ba2320657df55d1804
 F test/corruptH.test 5dd4fa98c6c1ed33b178f9e8a48c4fdd3cfc9067
 F test/corruptI.test 221ad8b7f0a9ac6b80fc577e73b5ad8cdea31243
+F test/corruptJ.test 8f584eb97b88e7b160d03edfe2f814c64e56b4ac
 F test/cost.test 19d314526616ce4473eb4e4e450fcb94499ce318
 F test/count.test 42a251178e32f617eda33f76236a7f79825a50b5
 F test/coveridxscan.test cdb47d01acc4a634a34fd25abe85189e0d0f1e62
@@ -1247,7 +1248,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 0ec08ba8a0fa188146b071a489908332693ba59a
-R 5fa56a67857aa9aa0bfe9c8eabbd7f2b
-U mistachkin
-Z 22e64f81a05ea0084d0b656c0478e6e9
+P ab5800291e1908b5b51d912feeacf748dc9be14b
+R 7b7ce803eb05ad2f210c6eb94fdd8e1c
+U drh
+Z 53c46ef4969a0037acc3578044ba6e9e
index 50a77b66aa133c0802715bd2801628a70375cf8f..b562506b7fa2c39883f0af6b8648e8df5d7300c9 100644 (file)
@@ -1 +1 @@
-ab5800291e1908b5b51d912feeacf748dc9be14b
\ No newline at end of file
+395bb3e677a6551b06ba96fc58c393132b93d1e8
\ No newline at end of file
index 52f29428f7e271fcda6dfcfdf483f1a29f4d15d2..a4eae640259916c9939e3c2894dc5a6d3f77a549 100644 (file)
@@ -7978,6 +7978,7 @@ static int clearDatabasePage(
   int i;
   int hdr;
   u16 szCell;
+  u8 hasChildren;
 
   assert( sqlite3_mutex_held(pBt->mutex) );
   if( pgno>btreePagecount(pBt) ){
@@ -7986,17 +7987,19 @@ static int clearDatabasePage(
 
   rc = getAndInitPage(pBt, pgno, &pPage, 0);
   if( rc ) return rc;
+  hasChildren = !pPage->leaf;
+  pPage->leaf = 1;  /* Block looping if the database is corrupt */
   hdr = pPage->hdrOffset;
   for(i=0; i<pPage->nCell; i++){
     pCell = findCell(pPage, i);
-    if( !pPage->leaf ){
+    if( hasChildren ){
       rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange);
       if( rc ) goto cleardatabasepage_out;
     }
     rc = clearCell(pPage, pCell, &szCell);
     if( rc ) goto cleardatabasepage_out;
   }
-  if( !pPage->leaf ){
+  if( hasChildren ){
     rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange);
     if( rc ) goto cleardatabasepage_out;
   }else if( pnChange ){
diff --git a/test/corruptJ.test b/test/corruptJ.test
new file mode 100644 (file)
index 0000000..ec884cc
--- /dev/null
@@ -0,0 +1,80 @@
+# 2015-03-30
+#
+# 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.
+#
+#***********************************************************************
+#
+# Corruption consisting of a database page that thinks it is a child
+# of itself.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix corruptJ
+
+if {[permutation]=="mmap"} {
+  finish_test
+  return
+}
+
+# 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;
+  PRAGMA auto_vacuum=0;
+  CREATE TABLE t1(a,b);
+  WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<10)
+    INSERT INTO t1(a,b) SELECT i, zeroblob(700) FROM c;
+} {}
+db close
+
+# Corrupt the root page of the t1 table such that the left-child pointer
+# for the very first cell points back to the root.  Then try to DROP the
+# table.  The clearDatabasePage() routine should not loop.
+#
+do_test 1.2 {
+  hexio_write test.db [expr {2*1024-2}] 02
+  sqlite3 db test.db
+  catchsql { DROP TABLE t1 }
+} {1 {database disk image is malformed}}
+
+# Similar test using a WITHOUT ROWID table
+#
+do_test 2.1 {
+  db close
+  forcedelete test.db
+  sqlite3 db test.db
+  db eval {
+    PRAGMA page_size=1024;
+    PRAGMA auto_vacuum=0;
+    CREATE TABLE t1(a,b,PRIMARY KEY(a,b)) WITHOUT ROWID;
+    WITH RECURSIVE c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<100)
+      INSERT INTO t1(a,b) SELECT i, zeroblob(200) FROM c;
+  }
+} {}
+
+# The table is three levels deep.  Corrupt the left child of an intermediate
+# page so that it points back to the root page.
+#
+do_test 2.2 {
+  db close
+  hexio_read test.db [expr {9*1024+391}] 8
+} {00000008814D0401}
+do_test 2.2b {
+  hexio_write test.db [expr {9*1024+391}] 00000002
+  sqlite3 db test.db
+  catchsql { DROP TABLE t1 }
+} {0 {}}
+
+finish_test