From: drh Date: Thu, 25 Feb 2010 02:04:35 +0000 (+0000) Subject: Enhance integrity_check to detect out-of-order rowids. This is a X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8d40cb7a5f9fa74b7d99bae74c2261dd6fd90743;p=thirdparty%2Fsqlite.git Enhance integrity_check to detect out-of-order rowids. This is a cherrypick merge of check-in [cae47c5b09]. FossilOrigin-Name: ecd22ef34d5cd516d2b6138f8b80c39daad3519d --- diff --git a/manifest b/manifest index 19aa3c4044..f2b91ae9ce 100644 --- a/manifest +++ b/manifest @@ -1,8 +1,8 @@ -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 -C Merge\sin\sthe\sfix\sfor\sVACUUM\son\sauto_vacuumed\sdatabases\swith\slarge\sschemas.\nCheck-in\s[86d50ce57f].\s\sTicket\s[da1151f97df]. -D 2010-02-25T01:54:07 +C Enhance\sintegrity_check\sto\sdetect\sout-of-order\srowids.\s\sThis\sis\sa\ncherrypick\smerge\sof\scheck-in\s[cae47c5b09]. +D 2010-02-25T02:04:35 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in c5827ead754ab32b9585487177c93bb00b9497b3 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -111,7 +111,7 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c 744e98359dfc79fed43e8dec911e33e108b06aae F src/bitvec.c 06ad2c36a9c3819c0b9cbffec7b15f58d5d834e0 F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff -F src/btree.c efdef3953c49e28f8b8fa9cc0ac5754cc1a7489a +F src/btree.c e9ded8d7791fccb3343d9aafd3bda680796adbef F src/btree.h 7944a9dac59eb3e541aad45fd2747f1051e7c63d F src/btreeInt.h 54f4245decd0409ea52cf9aee422d3d761d7ac10 F src/build.c 6ab7f8d8fa5b0ac6bfdce359e85f580b7a9b2e86 @@ -299,13 +299,14 @@ F test/corrupt3.test 263e8bb04e2728df832fddf6973cf54c91db0c32 F test/corrupt4.test acdb01afaedf529004b70e55de1a6f5a05ae7fff F test/corrupt5.test c23da7bfb20917cc7fdbb13ee25c7cc4e9fffeff F test/corrupt6.test e69b877d478224deab7b66844566258cecacd25e -F test/corrupt7.test e66cb109ed64e7ac985d8b4a3422c213d074c62d +F test/corrupt7.test 1eb2214f29474fa6b155aa3da8a7d46bf52089e1 F test/corrupt8.test 9992ef7f67cefc576b92373f6bf5ab8775280f51 F test/corrupt9.test 4aa1cb1ef091cb0e13e89a819c72911631b5176a F test/corruptA.test 99e95620b980161cb3e79f06a884a4bb8ae265ff F test/corruptB.test 66b4544104dd03d0f33ea69ddac3fa4a682cd3c2 F test/corruptC.test 691ed070baef5e1345939caadf270a52837a5064 F test/corruptD.test 3ae6e2dc6e2226c6935a8a40d4b5ee3eba75f8c0 +F test/corruptE.test dbf66cae4c0e977ca9625a9114cdd01df8967bef F test/count.test 454e1ce985c94d13efeac405ce54439f49336163 F test/crash.test 1b6ac8410689ff78028887f445062dc897c9ac89 F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651 @@ -787,14 +788,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 9588259882541e215813f48ccfa9180d8013a876 -R 1b3ce005dad313bc765273a4b8d0c293 +P ce624e39c2f7c5480937a8d2007e7ebd684665dc +R d843f9dd14d54f9d9ac0fb8fbca56eb7 U drh -Z 0aac407dd204ae64ed5ea6a13a82db48 +Z 0833a2fc5ea81f9538bc3f68db2345f2 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) -iD8DBQFLhdhDoxKgR168RlERAv2+AKCH9bpqmnpE0c2/f+XzXmMVOvZDAgCeJ5Ke -xndxhZ01ISXHfrjACQDvM9A= -=H/md +iD8DBQFLhdq3oxKgR168RlERAsU6AJ4hJDXzHpYPBgdnBpOzJH5z11UdTACghAtP +Gtx8ig9RfpMIFqXyVQXO0ck= +=5qgf -----END PGP SIGNATURE----- diff --git a/manifest.uuid b/manifest.uuid index 0c1bd86f05..d18f65eec4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ce624e39c2f7c5480937a8d2007e7ebd684665dc \ No newline at end of file +ecd22ef34d5cd516d2b6138f8b80c39daad3519d \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 385c511aa7..3dda02afc3 100644 --- a/src/btree.c +++ b/src/btree.c @@ -7393,7 +7393,9 @@ static void checkList( static int checkTreePage( IntegrityCk *pCheck, /* Context for the sanity check */ int iPage, /* Page number of the page to check */ - char *zParentContext /* Parent context */ + char *zParentContext, /* Parent context */ + i64 *pnParentMinKey, + i64 *pnParentMaxKey ){ MemPage *pPage; int i, rc, depth, d2, pgno, cnt; @@ -7404,6 +7406,8 @@ static int checkTreePage( int usableSize; char zContext[100]; char *hit = 0; + i64 nMinKey = 0; + i64 nMaxKey = 0; sqlite3_snprintf(sizeof(zContext), zContext, "Page %d: ", iPage); @@ -7446,6 +7450,16 @@ static int checkTreePage( btreeParseCellPtr(pPage, pCell, &info); sz = info.nData; if( !pPage->intKey ) sz += (int)info.nKey; + /* For intKey pages, check that the keys are in order. + */ + else if( i==0 ) nMinKey = nMaxKey = info.nKey; + else{ + if( info.nKey <= nMaxKey ){ + checkAppendMsg(pCheck, zContext, + "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey); + } + nMaxKey = info.nKey; + } assert( sz==info.nPayload ); if( (sz>info.nLocal) && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize]) @@ -7469,23 +7483,60 @@ static int checkTreePage( checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext); } #endif - d2 = checkTreePage(pCheck, pgno, zContext); + d2 = checkTreePage(pCheck, pgno, zContext, &nMinKey, i==0 ? NULL : &nMaxKey); if( i>0 && d2!=depth ){ checkAppendMsg(pCheck, zContext, "Child page depth differs"); } depth = d2; } } + if( !pPage->leaf ){ pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); sqlite3_snprintf(sizeof(zContext), zContext, "On page %d at right child: ", iPage); #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum ){ - checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, 0); + checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext); } #endif - checkTreePage(pCheck, pgno, zContext); + checkTreePage(pCheck, pgno, zContext, NULL, !pPage->nCell ? NULL : &nMaxKey); + } + + /* For intKey leaf pages, check that the min/max keys are in order + ** with any left/parent/right pages. + */ + if( pPage->leaf && pPage->intKey ){ + /* if we are a left child page */ + if( pnParentMinKey ){ + /* if we are the left most child page */ + if( !pnParentMaxKey ){ + if( nMaxKey > *pnParentMinKey ){ + checkAppendMsg(pCheck, zContext, + "Rowid %lld out of order (max larger than parent min of %lld)", + nMaxKey, *pnParentMinKey); + } + }else{ + if( nMinKey <= *pnParentMinKey ){ + checkAppendMsg(pCheck, zContext, + "Rowid %lld out of order (min less than parent min of %lld)", + nMinKey, *pnParentMinKey); + } + if( nMaxKey > *pnParentMaxKey ){ + checkAppendMsg(pCheck, zContext, + "Rowid %lld out of order (max larger than parent max of %lld)", + nMaxKey, *pnParentMaxKey); + } + *pnParentMinKey = nMaxKey; + } + /* else if we're a right child page */ + } else if( pnParentMaxKey ){ + if( nMinKey <= *pnParentMaxKey ){ + checkAppendMsg(pCheck, zContext, + "Rowid %lld out of order (min less than parent max of %lld)", + nMinKey, *pnParentMaxKey); + } + } } /* Check for complete coverage of the page @@ -7511,7 +7562,7 @@ static int checkTreePage( } if( (pc+size-1)>=usableSize ){ checkAppendMsg(pCheck, 0, - "Corruption detected in cell %d on page %d",i,iPage,0); + "Corruption detected in cell %d on page %d",i,iPage); }else{ for(j=pc+size-1; j>=pc; j--) hit[j]++; } @@ -7617,7 +7668,7 @@ char *sqlite3BtreeIntegrityCheck( checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0, 0); } #endif - checkTreePage(&sCheck, aRoot[i], "List of tree roots: "); + checkTreePage(&sCheck, aRoot[i], "List of tree roots: ", NULL, NULL); } /* Make sure every page in the file is referenced diff --git a/test/corrupt7.test b/test/corrupt7.test index 8f8d437a62..39aa62018e 100644 --- a/test/corrupt7.test +++ b/test/corrupt7.test @@ -89,7 +89,7 @@ Corruption detected in cell 15 on page 2}} sqlite3 db test.db db eval {PRAGMA integrity_check(1)} } {{*** in database main *** -Corruption detected in cell 15 on page 2}} +On tree page 2 cell 15: Rowid 0 out of order (previous was 15)}} } # The code path that was causing the buffer overrun that this test diff --git a/test/corruptE.test b/test/corruptE.test new file mode 100644 index 0000000000..35fa545b4d --- /dev/null +++ b/test/corruptE.test @@ -0,0 +1,171 @@ +# 2010 February 18 +# +# 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 specifcally +# focuses on rowid order corruption. +# +# $Id: corruptE.test,v 1.14 2009/07/11 06:55:34 danielk1977 Exp $ + +catch {file delete -force test.db test.db-journal test.bu} + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Construct a compact, dense database for testing. +# +do_test corruptE-1.1 { + execsql { + PRAGMA auto_vacuum = 0; + PRAGMA legacy_file_format=1; + BEGIN; + CREATE TABLE t1(x,y); + INSERT INTO t1 VALUES(1,1); + INSERT OR IGNORE INTO t1 SELECT x*2,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*3,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*5,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*7,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*11,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*13,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*17,y FROM t1; + INSERT OR IGNORE INTO t1 SELECT x*19,y FROM t1; + CREATE INDEX t1i1 ON t1(x); + CREATE TABLE t2 AS SELECT x,2 as y FROM t1 WHERE rowid%5!=0; + COMMIT; + } +} {} + +ifcapable {integrityck} { + integrity_check corruptE-1.2 +} + +# Copy file $from into $to +# +proc copy_file {from to} { + file copy -force $from $to +} + +# Setup for the tests. Make a backup copy of the good database in test.bu. +# +db close +copy_file test.db test.bu +sqlite3 db test.db +set fsize [file size test.db] + + +do_test corruptE-2.1 { + db close + copy_file test.bu test.db + + # insert corrupt byte(s) + hexio_write test.db 2041 [format %02x 0x2e] + + sqlite3 db test.db + + set res [ catchsql {PRAGMA integrity_check} ] + set ans [lindex $res 1] + + list [regexp {out of order.*previous was} $ans] \ + [regexp {out of order.*max larger than parent max} $ans] +} {1 1} + +do_test corruptE-2.2 { + db close + copy_file test.bu test.db + + # insert corrupt byte(s) + hexio_write test.db 2047 [format %02x 0x84] + + sqlite3 db test.db + + set res [ catchsql {PRAGMA integrity_check} ] + set ans [lindex $res 1] + + list [regexp {out of order.*previous was} $ans] \ + [regexp {out of order.*min less than parent min} $ans] +} {1 1} + +do_test corruptE-2.3 { + db close + copy_file test.bu test.db + + # insert corrupt byte(s) + hexio_write test.db 7420 [format %02x 0xa8] + hexio_write test.db 10459 [format %02x 0x8d] + + sqlite3 db test.db + + set res [ catchsql {PRAGMA integrity_check} ] + set ans [lindex $res 1] + + list [regexp {out of order.*max larger than parent min} $ans] +} {1} + +do_test corruptE-2.4 { + db close + copy_file test.bu test.db + + # insert corrupt byte(s) + hexio_write test.db 10233 [format %02x 0xd0] + + sqlite3 db test.db + + set res [ catchsql {PRAGMA integrity_check} ] + set ans [lindex $res 1] + + list [regexp {out of order.*min less than parent max} $ans] +} {1} + + +set tests [list {10233 0xd0} \ + {941 0x42} \ + {1028 0x53} \ + {2041 0xd0} \ + {2042 0x1f} \ + {2047 0xaa} \ + {2263 0x29} \ + {2274 0x75} \ + {3267 0xf2} \ + {4104 0x2c} \ + {5113 0x36} \ + {10233 0x84} \ + {10234 0x74} \ + {10239 0x41} \ + {10453 0x11} \ + {11273 0x28} \ + {11455 0x11} \ + {11461 0xe6} \ + {12281 0x99} \ + {12296 0x9e} \ + {12297 0xd7} \ + {13303 0x53} ] + +set tc 1 +foreach test $tests { + do_test corruptE-3.$tc { + db close + copy_file test.bu test.db + + # insert corrupt byte(s) + hexio_write test.db [lindex $test 0] [format %02x [lindex $test 1]] + + sqlite3 db test.db + + set res [ catchsql {PRAGMA integrity_check} ] + set ans [lindex $res 1] + + list [regexp {out of order} $ans] + } {1} + incr tc 1 +} + +finish_test