]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Make the btree layer robust when faced with a corrupt database that
authordrh <drh@noemail.net>
Fri, 11 Jul 2008 03:34:09 +0000 (03:34 +0000)
committerdrh <drh@noemail.net>
Fri, 11 Jul 2008 03:34:09 +0000 (03:34 +0000)
contains duplicate entries on the freelist.  Ticket #3209. (CVS 5392)

FossilOrigin-Name: 30825f74d60d8ace39bafd06814017ceefeb4fa4

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

index aaf4425c8bceae8f1db096e0d07d6490e12860f0..355d9b426fa8df20a2b88ae3beffaa9519bcbf6b 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Additional\stest\scoverage\sin\sbtree.c.\s\sAdded\scorruption\stests\sfor\nthe\sptrmap\spages\sof\san\sautovacuumed\sdatabase\s(corrupt8.test).\s(CVS\s5391)
-D 2008-07-11T02:21:41
+C Make\sthe\sbtree\slayer\srobust\swhen\sfaced\swith\sa\scorrupt\sdatabase\sthat\ncontains\sduplicate\sentries\son\sthe\sfreelist.\s\sTicket\s#3209.\s(CVS\s5392)
+D 2008-07-11T03:34:10
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in a03f7cb4f7ad50bc53a788c6c544430e81f95de4
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -95,7 +95,7 @@ F src/attach.c b18ba42c77f7d3941f5d23d2ca20fa1d841a4e91
 F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627
 F src/bitvec.c 95c86bd18d8fedf0533f5af196192546e10a7e7d
 F src/btmutex.c 483ced3c52205b04b97df69161fadbf87f4f1ea2
-F src/btree.c 71ba242014031cc6b30761094adad35ad4896a19
+F src/btree.c 89f1122f865f44a26ed65e59c998969bcb12b9c8
 F src/btree.h 9373128fbd6509a281e0d356cb15f9cffbfa876c
 F src/btreeInt.h d59e58d39950a17c0fb7e004c90ab7696d3e7df5
 F src/build.c bac7233d984be3805aaa41cf500f7ee12dc97249
@@ -133,7 +133,7 @@ F src/os_common.h 24525d8b7bce66c374dfc1810a6c9043f3359b60
 F src/os_os2.c 6c33e61f0fab256b0136650cdee35c3eaab2fa04
 F src/os_unix.c 1df6108efdb7957a9f28b9700600e58647c9c12d
 F src/os_win.c 2bf2f8cd700299564cc236262c2668e1e02c626a
-F src/pager.c 08169a94414b03d80fc2c026a1d0fdf5367bdcbe
+F src/pager.c bb286b2fc0c7c87d0a8cbfee96a3e953da1e53dd
 F src/pager.h 6aa3050a3c684475a5a9dbad5ff1cebad612acba
 F src/parse.y 097bff733e89fbf554a07d9327046718ce364011
 F src/pragma.c 6fad83fbcc7ec6e76d91fe2805fe972ff3af6a0c
@@ -248,7 +248,8 @@ F test/corrupt4.test acdb01afaedf529004b70e55de1a6f5a05ae7fff
 F test/corrupt5.test 7796d5bdfe155ed824cee9dff371f49da237cfe0
 F test/corrupt6.test e69b877d478224deab7b66844566258cecacd25e
 F test/corrupt7.test f0ff354eb2f0a23035fbd06724b87cac95b55cc1
-F test/corrupt8.test c8ebf7cfe9fca7818a71907a2e433c4a38dbf838
+F test/corrupt8.test 9992ef7f67cefc576b92373f6bf5ab8775280f51
+F test/corrupt9.test 2a1bf91834dc6f7adead1a4fabb5887393147dc6
 F test/crash.test 1b6ac8410689ff78028887f445062dc897c9ac89
 F test/crash2.test 26d7a4c5520201e5de2c696ea51ab946b59dc0e9
 F test/crash3.test 0b09687ae1a3ccbcefdfaeb4b963e26e36255d76
@@ -602,7 +603,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 1dbced29de5f59ba2ebf877edcadf171540374d1
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
-P 8fc462b6b7afe390463ea7b010fd3230d9acc358
-R 4385bebdc0676f5f0e502792343cbd11
+P 620b472133438607c412e0c21d2a27605a89a414
+R f7e332178c4f29b825342cc58c343fbe
 U drh
-Z f5f817655b5188ade4dd50e6a7a1f252
+Z b54ec49dda70f42678882a2b718e31d1
index 27ca667f5cc9f61bd1ba421f622940581922fa3f..0d5b021d38252c6173a204bedd70702905f092ce 100644 (file)
@@ -1 +1 @@
-620b472133438607c412e0c21d2a27605a89a414
\ No newline at end of file
+30825f74d60d8ace39bafd06814017ceefeb4fa4
\ No newline at end of file
index 940bfb64bc72981e440932b95931c6812b92b08a..5390e4f7c057cd40aeaf8d2c7dec1895ae4476ba 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.476 2008/07/11 02:21:41 drh Exp $
+** $Id: btree.c,v 1.477 2008/07/11 03:34:10 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** See the header comment on "btreeInt.h" for additional information.
@@ -5302,6 +5302,7 @@ static int balance_nonroot(MemPage *pPage){
     MemPage *pNew = apNew[i];
     assert( j<nMaxCells );
     assert( pNew->pgno==pgnoNew[i] );
+    zeroPage(pNew, pageFlags);
     assemblePage(pNew, cntNew[i]-j, &apCell[j], &szCell[j]);
     assert( pNew->nCell>0 || (nNew==1 && cntNew[0]==0) );
     assert( pNew->nOverflow==0 );
@@ -5578,7 +5579,7 @@ static int balance_deeper(MemPage *pPage){
   cdata = pChild->aData;
   memcpy(cdata, &data[hdr], pPage->cellOffset+2*pPage->nCell-hdr);
   memcpy(&cdata[brk], &data[brk], usableSize-brk);
-  assert( pChild->isInit==0 );
+  if( pChild->isInit ) return SQLITE_CORRUPT;
   rc = sqlite3BtreeInitPage(pChild, pPage);
   if( rc ) goto balancedeeper_out;
   memcpy(pChild->aOvfl, pPage->aOvfl, pPage->nOverflow*sizeof(pPage->aOvfl[0]));
index 2e0cd4bce9c6948569640ea39d2026239e8a4976..1722043a8d61d5cd2b73f8aaa5bb275f6c93dadf 100644 (file)
@@ -18,7 +18,7 @@
 ** file simultaneously, or one process from reading the database while
 ** another is writing.
 **
-** @(#) $Id: pager.c,v 1.464 2008/07/10 00:32:42 drh Exp $
+** @(#) $Id: pager.c,v 1.465 2008/07/11 03:34:10 drh Exp $
 */
 #ifndef SQLITE_OMIT_DISKIO
 #include "sqliteInt.h"
@@ -4547,8 +4547,12 @@ void sqlite3PagerDontRollback(DbPage *pPg){
   ** has not been previously called during the same transaction.
   ** And if DontWrite() has previously been called, the following
   ** conditions must be met.
+  **
+  ** (Later:)  Not true.  If the database is corrupted by having duplicate
+  ** pages on the freelist (ex: corrupt9.test) then the following is not
+  ** necessarily true:
   */
-  assert( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize );
+  /* assert( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ); */
 
   assert( pPager->pInJournal!=0 );
   sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
index f615781cca84e3bf875c1c18f8830fc6241e9493..994f4aaffbaf14416de349638f0f5be54cb0b1ea 100644 (file)
@@ -1,4 +1,4 @@
-# 2008 June 11
+# 2008 July 9
 #
 # The author disclaims copyright to this source code.  In place of
 # a legal notice, here is a blessing:
@@ -14,7 +14,7 @@
 # segfault if it sees a corrupt database file.  It specifically focuses
 # on corrupt pointer map pages.
 #
-# $Id: corrupt8.test,v 1.1 2008/07/11 02:21:41 drh Exp $
+# $Id: corrupt8.test,v 1.2 2008/07/11 03:34:10 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
diff --git a/test/corrupt9.test b/test/corrupt9.test
new file mode 100644 (file)
index 0000000..ca51fbb
--- /dev/null
@@ -0,0 +1,130 @@
+# 2008 July 9
+#
+# 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.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+# This file implements tests to make sure SQLite does not crash or
+# segfault if it sees a corrupt database file.  It specifically focuses
+# on corruption in the form of duplicate entries no the freelist.
+#
+# $Id: corrupt9.test,v 1.1 2008/07/11 03:34:10 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# We must have the page_size pragma for these tests to work.
+#
+ifcapable !pager_pragmas {
+  finish_test
+  return
+}
+
+# Return the offset to the first (trunk) page of the freelist.  Return
+# zero of the freelist is empty.
+#
+proc freelist_trunk_offset {filename} {
+  if {[hexio_read $filename 36 4]==0} {return 0}
+  set pgno [hexio_get_int [hexio_read $filename 32 4]]
+  return [expr {($pgno-1)*[hexio_get_int [hexio_read $filename 16 2]]}]
+}
+
+# This procedure looks at the first trunk page of the freelist and
+# corrupts that page by overwriting up to N entries with duplicates
+# of the first entry.
+#
+proc corrupt_freelist {filename N} {
+  set offset [freelist_trunk_offset $filename]
+  if {$offset==0} {error "Freelist is empty"}
+  set cnt [hexio_get_int [hexio_read $filename [expr {$offset+4}] 4]]
+  set pgno [hexio_read $filename [expr {$offset+8}] 4]
+  for {set i 12} {$N>0 && $i<8+4*$cnt} {incr i 4; incr N -1} {
+    hexio_write $filename [expr {$offset+$i}] $pgno
+  }
+}
+
+# Create a database to work with.  Make sure there are plenty of
+# entries on the freelist.
+#
+do_test corrupt9-1.1 {
+  execsql {
+    PRAGMA page_size=1024;
+    CREATE TABLE t1(x);
+    INSERT INTO t1(x) VALUES(1);
+    INSERT INTO t1(x) VALUES(2);
+    INSERT INTO t1(x) SELECT x+2 FROM t1;
+    INSERT INTO t1(x) SELECT x+4 FROM t1;
+    INSERT INTO t1(x) SELECT x+8 FROM t1;
+    INSERT INTO t1(x) SELECT x+16 FROM t1;
+    INSERT INTO t1(x) SELECT x+32 FROM t1;
+    INSERT INTO t1(x) SELECT x+64 FROM t1;
+    INSERT INTO t1(x) SELECT x+128 FROM t1;
+    INSERT INTO t1(x) SELECT x+256 FROM t1;
+    CREATE TABLE t2(a,b);
+    INSERT INTO t2 SELECT x, x*x FROM t1;
+    CREATE INDEX i1 ON t1(x);
+    CREATE INDEX i2 ON t2(b,a);
+    DROP INDEX i2;
+  }
+  expr {[file size test.db]>1024*24}
+} {1}
+integrity_check corrupt9-1.2
+
+# Corrupt the freelist by adding duplicate entries to the freelist.
+# Make sure the corruption is detected.
+#
+db close
+file copy -force test.db test.db-template
+
+corrupt_freelist test.db 1
+sqlite3 db test.db
+do_test corrupt9-2.1 {
+  set x [db eval {PRAGMA integrity_check}]
+  expr {$x!="ok"}
+} {1}
+do_test corrupt9-2.2 {
+  catchsql {
+    CREATE INDEX i2 ON t2(b,a);
+    REINDEX;
+  }
+} {1 {database disk image is malformed}}
+
+
+db close
+file copy -force test.db-template test.db
+corrupt_freelist test.db 2
+sqlite3 db test.db
+do_test corrupt9-3.1 {
+  set x [db eval {PRAGMA integrity_check}]
+  expr {$x!="ok"}
+} {1}
+do_test corrupt9-3.2 {
+  catchsql {
+    CREATE INDEX i2 ON t2(b,a);
+    REINDEX;
+  }
+} {1 {database disk image is malformed}}
+
+db close
+file copy -force test.db-template test.db
+corrupt_freelist test.db 3
+sqlite3 db test.db
+do_test corrupt9-4.1 {
+  set x [db eval {PRAGMA integrity_check}]
+  expr {$x!="ok"}
+} {1}
+do_test corrupt9-4.2 {
+  catchsql {
+    CREATE INDEX i2 ON t2(b,a);
+    REINDEX;
+  }
+} {1 {database disk image is malformed}}
+
+finish_test