libdb btree page split on zero index corrupts db
authorTom Yu <tlyu@mit.edu>
Fri, 1 Feb 2008 01:03:11 +0000 (01:03 +0000)
committerTom Yu <tlyu@mit.edu>
Fri, 1 Feb 2008 01:03:11 +0000 (01:03 +0000)
Splitting a btree page on index 0 can corrupt the database if the key
length plus data length is exactly a certain value.  This certain size
causes the item to get the left page to itself, and causes the right
page to contain an erroneous additional index "hole" having an
uninitialized value.  This bug may be one of the remaining causes of
unexplained database corruption reported over the years.  Shawn Emery
provided useful data from actual instances of this corruption.

Add a test case for this bug.  (Raw libdb test rather than kdb; the
latter would be much harder.)

ticket: new
target_version: 1.6.4
tags: pullup
component: krb5-kdc

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@20214 dc483132-0cff-0310-8789-dd5450dbe970

src/plugins/kdb/db2/libdb2/btree/bt_split.c
src/plugins/kdb/db2/libdb2/test/run.test

index 62ec823a18acbdd3a1ee5363ab915278fc4a3af7..9b2708e760ec84864dde054d6c757e9d30823f96 100644 (file)
@@ -727,7 +727,7 @@ bt_psplit(t, h, l, r, pskip, ilen)
         * the right page.
         */
        if (skip <= off) {
-               skip = 0;
+               skip = (indx_t)-1;
                rval = l;
        } else {
                rval = r;
@@ -737,7 +737,7 @@ bt_psplit(t, h, l, r, pskip, ilen)
        for (off = 0; nxt < top; ++off) {
                if (skip == nxt) {
                        ++off;
-                       skip = 0;
+                       skip = (indx_t)-1;
                }
                switch (h->flags & P_TYPE) {
                case P_BINTERNAL:
index 48c3a63d0e38068ee5244b969731efe2d78dbafe..377d6f794c53c001efde145ada3216221ab0a560 100644 (file)
@@ -34,7 +34,7 @@ main()
        bindir=/bin/.
 
        if [ $# -eq 0 ]; then
-               for t in 1 2 3 4 5 6 7 8 9 10 11 12 13 20; do
+               for t in 1 2 3 4 5 6 7 8 9 10 11 12 13 20 40; do
                        test$t
                done
        else
@@ -45,7 +45,7 @@ main()
                        [0-9]*)
                                test$1;;
                        btree)
-                               for t in 1 2 3 7 8 9 10 12 13; do
+                               for t in 1 2 3 7 8 9 10 12 13 40; do
                                        test$t
                                done;;
                        hash)
@@ -743,4 +743,54 @@ bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed"
        done
 }
 
+# Test for a weird page split condition where an insertion into index
+# 0 of a page that would cause the new item to be the only item on the
+# left page results in index 0 of the right page being erroneously
+# skipped; this only happens with one particular key+data length for
+# each page size.
+test40 () {
+       echo "Test 40: btree: page split on index 0"
+       e=:
+       for psize in 512 1024 2048 4096 8192; do
+               echo "    page size $psize"
+               kdsizes=`awk 'BEGIN {
+                       psize = '$psize'; hsize = int(psize/2);
+                       for (kdsize = hsize-40; kdsize <= hsize; kdsize++) {
+                               print kdsize;
+                       }
+               }' /dev/null`
+
+               # Use a series of keylen+datalen values in the right
+               # neighborhood to find the one that triggers the bug.
+               # We could compute the exact size that triggers the
+               # bug but this additional fuzz may be useful.
+
+               # Insert keys in reverse order to maximize the chances
+               # for a split on index 0.
+
+               for kdsize in $kdsizes; do
+                       awk 'BEGIN {
+                               kdsize = '$kdsize';
+                               for (i = 8; i-- > 0; ) {
+                                       s = sprintf("a%03d:%09d", i, kdsize);
+                                       for (j = 0; j < kdsize-20; j++) {
+                                               s = s "x";
+                                       }
+                                       printf("p\nka%03d\nd%s\n", i, s);
+                               }
+                               print "o";
+                       }' /dev/null > $TMP2
+                       sed -n 's/^d//p' $TMP2 | sort > $TMP1
+                       $PROG -o $TMP3 -i psize=$psize btree $TMP2
+                       if (cmp -s $TMP1 $TMP3); then :
+                       else
+                               echo "test40: btree: page size $psize, \
+keylen+datalen=$kdsize failed"
+                               e='exit 1'
+                       fi
+               done
+       done
+       $e
+}
+
 main $*