]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix a case introduced by [4cd2a967] where a corrupt database could cause a buffer...
authordan <dan@noemail.net>
Fri, 3 Mar 2017 20:02:53 +0000 (20:02 +0000)
committerdan <dan@noemail.net>
Fri, 3 Mar 2017 20:02:53 +0000 (20:02 +0000)
FossilOrigin-Name: 5d0455fece514552ad7f283d56526f53d7c688bd

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

index b06ed90284f89d141c12f43b070f60567542cfb1..05070243d5c70084e024bb9831e0067c4ae22e67 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Before\sbeginning\san\sincremental\scheckpoint\sin\sRBU,\ssync\sthe\sdirectory\ncontaining\sthe\starget\sdatabase\sfile.\sThis\sensures\sthat\sthe\snew\sdirectory\sentry\ncreated\sby\srenaming\sthe\s*-oal\sfile\sto\s*-wal\sis\ssynced\sto\sdisk.
-D 2017-03-03T16:51:46.903
+C Fix\sa\scase\sintroduced\sby\s[4cd2a967]\swhere\sa\scorrupt\sdatabase\scould\scause\sa\sbuffer\soverwrite.
+D 2017-03-03T20:02:53.439
 F Makefile.in edb6bcdd37748d2b1c3422ff727c748df7ffe918
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc a89ea37ab5928026001569f056973b9059492fe2
@@ -340,7 +340,7 @@ F src/auth.c 930b376a9c56998557367e6f7f8aaeac82a2a792
 F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
 F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
 F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
-F src/btree.c 03149b0f3fec3c1aa3c50a17e997bb442b867632
+F src/btree.c 91baade79832becb44d85cd7c39b5ebe170666d8
 F src/btree.h e6d352808956ec163a17f832193a3e198b3fb0ac
 F src/btreeInt.h cd55d39d9916270837a88c12e701047cba0729b0
 F src/build.c 51b473eec465f471d607b54e8dbc00751c3f8a1f
@@ -618,6 +618,7 @@ F test/corruptG.test adf79b669cbfd19e28c8191a610d083ae53a6d51
 F test/corruptH.test 79801d97ec5c2f9f3c87739aa1ec2eb786f96454
 F test/corruptI.test 075fe1d75aa1d84e2949be56b6264376c41502e4
 F test/corruptJ.test 4d5ccc4bf959464229a836d60142831ef76a5aa4
+F test/corruptK.test 814a59ec699d8546b4e29005fba3d16e933ef2fe
 F test/cost.test 1eedbfd868f806f3fa08ff072b04cf270dcf61c8
 F test/count.test cb2e0f934c6eb33670044520748d2ecccd46259c
 F test/coveridxscan.test b629e896b14df2f000a99b8d170d80589c46562c
@@ -1560,7 +1561,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 081dbcfb6d82528cefecb36c4491fa6e1a790b17
-R c49d10bdecee819ceb83b85a5e3f8244
+P 915a9a28783fbb2f4c0794eb4264ce8c0b9d42f7
+R 87fee79fc77f11db6d08a83e962c36d1
 U dan
-Z 0d48f0e17be43a572b386c16c35f7b42
+Z d96b298a9dab6e01e07206495b42117b
index 91442ae41385f2651051e15f3ab2cec8c57a6262..a7791d4715d5f84a44a8cd01cc9f7488449dd27a 100644 (file)
@@ -1 +1 @@
-915a9a28783fbb2f4c0794eb4264ce8c0b9d42f7
\ No newline at end of file
+5d0455fece514552ad7f283d56526f53d7c688bd
\ No newline at end of file
index bf65852fe66854b6e4c06fdf1158680e7f4dadb5..e942dbce03c31201e80a92aa6eca2acece8c9869 100644 (file)
@@ -1355,6 +1355,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
   nCell = pPage->nCell;
   assert( nCell==get2byte(&data[hdr+3]) );
   iCellFirst = cellOffset + 2*nCell;
+  usableSize = pPage->pBt->usableSize;
 
   /* This block handles pages with two or fewer free blocks and nMaxFrag
   ** or fewer fragmented bytes. In this case it is faster to move the
@@ -1365,6 +1366,17 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
     int iFree = get2byte(&data[hdr+1]);
     if( iFree ){
       int iFree2 = get2byte(&data[iFree]);
+
+      /* pageFindSlot() has already verified that free blocks are sorted
+      ** in order of offset within the page, and that no block extends
+      ** past the end of the page. Provided the two free slots do not 
+      ** overlap, this guarantees that the memmove() calls below will not
+      ** overwrite the usableSize byte buffer, even if the database page
+      ** is corrupt.  */
+      assert( iFree2==0 || iFree2>iFree );
+      assert( iFree+get2byte(&data[iFree+2]) <= usableSize );
+      assert( iFree2==0 || iFree2+get2byte(&data[iFree2+2]) <= usableSize );
+
       if( 0==iFree2 || (data[iFree2]==0 && data[iFree2+1]==0) ){
         u8 *pEnd = &data[cellOffset + nCell*2];
         u8 *pAddr;
@@ -1372,11 +1384,14 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
         int sz = get2byte(&data[iFree+2]);
         int top = get2byte(&data[hdr+5]);
         if( iFree2 ){
+          if( iFree+sz>iFree2 ) return SQLITE_CORRUPT_BKPT;
           sz2 = get2byte(&data[iFree2+2]);
+          assert( iFree+sz+sz2+iFree2-(iFree+sz) <= usableSize );
           memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz));
           sz += sz2;
         }
         cbrk = top+sz;
+        assert( cbrk+(iFree-top) <= usableSize );
         memmove(&data[cbrk], &data[top], iFree-top);
         for(pAddr=&data[cellOffset]; pAddr<pEnd; pAddr+=2){
           pc = get2byte(pAddr);
@@ -1388,10 +1403,8 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
     }
   }
 
-  usableSize = pPage->pBt->usableSize;
   cbrk = usableSize;
   iCellLast = usableSize - 4;
-
   for(i=0; i<nCell; i++){
     u8 *pAddr;     /* The i-th cell pointer */
     pAddr = &data[cellOffset + i*2];
diff --git a/test/corruptK.test b/test/corruptK.test
new file mode 100644 (file)
index 0000000..b20c2d8
--- /dev/null
@@ -0,0 +1,113 @@
+# 2017-03-03
+#
+# 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 corruptK
+
+if {[permutation]=="mmap"} {
+  finish_test
+  return
+}
+
+# This module uses hard-coded offsets which do not work if the reserved_bytes
+# value is nonzero.
+if {[nonzero_reserved_bytes]} {finish_test; return;}
+database_may_be_corrupt
+
+# Initialize the database.
+# 
+do_execsql_test 1.1 {
+  PRAGMA page_size=1024;
+  PRAGMA auto_vacuum=0;
+  CREATE TABLE t1(x);
+
+  INSERT INTO t1 VALUES(randomblob(20));
+  INSERT INTO t1 VALUES(randomblob(100));   -- make this into a free slot
+  INSERT INTO t1 VALUES(randomblob(27));    -- this one will be corrupt
+  INSERT INTO t1 VALUES(randomblob(800));
+
+  DELETE FROM t1 WHERE rowid=2;  -- free the 100 byte slot
+  PRAGMA page_count
+} {2}
+
+
+# Corrupt the database so that the blob stored immediately before 
+# the free slot (rowid==3) has an overlarge length field. So that
+# we can use sqlite3_blob_write() to manipulate the size field of
+# the free slot.
+#
+# Then use sqlite3_blob_write() to set the size of said free slot
+# to 24 bytes (instead of the actual 100).
+#
+# Then use the new 24 byte slot. Leaving the in-memory version of
+# the page with zero free slots and a large nFree value. Then try
+# to allocate another slot to get to defragmentPage().
+#
+do_test 1.2 {
+  db close
+  hexio_write test.db [expr 1024 + 0x360] 21
+  hexio_write test.db [expr 1024 + 0x363] [format %x [expr 31*2 + 12]]
+  sqlite3 db test.db
+
+  set fd [db incrblob t1 x 3]
+  fconfigure $fd -translation binary -encoding binary
+  seek $fd 30
+  puts -nonewline $fd "\x18"
+  close $fd
+} {}
+do_execsql_test 1.3 {
+  INSERT INTO t1 VALUES(randomblob(20));
+}
+do_catchsql_test 1.4 {
+  INSERT INTO t1 VALUES(randomblob(90));
+} {1 {database disk image is malformed}}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 2.1 {
+  PRAGMA page_size=1024;
+  PRAGMA auto_vacuum=0;
+  CREATE TABLE t1(x);
+
+  INSERT INTO t1 VALUES(randomblob(20));
+  INSERT INTO t1 VALUES(randomblob(20));    -- free this one
+  INSERT INTO t1 VALUES(randomblob(20));
+  INSERT INTO t1 VALUES(randomblob(20));    -- and this one
+  INSERT INTO t1 VALUES(randomblob(20));    -- corrupt this one.
+
+  DELETE FROM t1 WHERE rowid IN(2, 4);
+  PRAGMA page_count
+} {2}
+
+do_test 2.2 {
+  db close
+  hexio_write test.db [expr 1024 + 0x388] 53
+  hexio_write test.db [expr 1024 + 0x38A] 03812C
+
+  sqlite3 db test.db
+  set fd [db incrblob t1 x 5]
+  fconfigure $fd -translation binary -encoding binary
+
+  seek $fd 22
+  puts -nonewline $fd "\x5d"
+  close $fd
+} {}
+
+do_catchsql_test 2.3 {
+  INSERT INTO t1 VALUES(randomblob(900));
+} {1 {database disk image is malformed}}
+
+
+
+
+finish_test