This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
(perl # 132147) improve robustness against corrupt SDBM databases
authorTony Cook <tony@develop-help.com>
Mon, 19 Nov 2018 03:04:20 +0000 (14:04 +1100)
committerTony Cook <tony@develop-help.com>
Mon, 19 Nov 2018 03:04:20 +0000 (14:04 +1100)
This merge makes a few changes to the SDBM database handling:

- in a few places, a corrupt page could be loaded, but despite
  failing validation, it would still be cached, so a second call
  would try to use the corrupt page, causing buffer overflows

- some code didn't validate on page load at all.

- adds three extra checks to the page validator

MANIFEST
ext/SDBM_File/pair.c
ext/SDBM_File/sdbm.c
ext/SDBM_File/t/corrupt.t [new file with mode: 0644]

index f9cbd75..0062673 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -4212,6 +4212,7 @@ ext/SDBM_File/sdbm.h      SDBM kit
 ext/SDBM_File/SDBM_File.pm     SDBM extension Perl module
 ext/SDBM_File/SDBM_File.xs     SDBM extension external subroutines
 ext/SDBM_File/t/constants.t    See if SDBM_File constants work
+ext/SDBM_File/t/corrupt.t      See if SDBM_File handles corrupt files
 ext/SDBM_File/t/prep.t         See if SDBM_File with extra argument works
 ext/SDBM_File/t/sdbm.t         See if SDBM_File works
 ext/SDBM_File/tune.h   SDBM kit
index 2e4d807..c12ad33 100644 (file)
@@ -269,6 +269,20 @@ splpage(char *pag, char *New, long int sbit)
  * reasonable, and all offsets in the index should be in order.
  * this could be made more rigorous.
  */
+/*
+  Layout of a page is:
+  Top of block:
+  number of keys/values (short)
+  Array of (number of keys/values) offsets, alternating between key offsets
+  and value offsets (shorts)
+  End of block:
+   - value/key data, last key ends at end of block (bytes)
+
+  So:
+    N key0off val0off key1off val1off ... val1 key1 val0 key0
+
+  Be careful to note N is the number of offsets, *not* the number of keys.
+ */
 int
 chkpage(char *pag)
 {
@@ -283,11 +297,17 @@ chkpage(char *pag)
                 off = PBLKSIZ;
                 for (ino++; n > 0; ino += 2) {
                         if (ino[0] > off || ino[1] > off ||
-                            ino[1] > ino[0])
+                            ino[1] > ino[0] || ino[1] <= 0)
                                 return 0;
                         off = ino[1];
                         n -= 2;
                 }
+                /* there must be an even number of offsets */
+                if (n != 0)
+                    return 0;
+                /* check the key/value offsets don't overlap the key/value data */
+                if ((char *)ino > pag + off)
+                    return 0;
         }
         return 1;
 }
index 2099857..bdb5f47 100644 (file)
@@ -398,6 +398,12 @@ sdbm_firstkey(DBM *db)
        if (lseek(db->pagf, OFF_PAG(0), SEEK_SET) < 0
            || read(db->pagf, db->pagbuf, PBLKSIZ) < 0)
                return ioerr(db), nullitem;
+        if (!chkpage(db->pagbuf)) {
+            errno = EINVAL;
+            ioerr(db);
+            db->pagbno = -1;
+            return nullitem;
+        }
        db->pagbno = 0;
        db->blkptr = 0;
        db->keyptr = 0;
@@ -446,8 +452,12 @@ getpage(DBM *db, long int hash)
                if (lseek(db->pagf, OFF_PAG(pagb), SEEK_SET) < 0
                    || read(db->pagf, db->pagbuf, PBLKSIZ) < 0)
                        return 0;
-               if (!chkpage(db->pagbuf))
-                       return 0;
+               if (!chkpage(db->pagbuf)) {
+                    errno = EINVAL;
+                    db->pagbno = -1;
+                    ioerr(db);
+                    return 0;
+                }
                db->pagbno = pagb;
 
                debug(("pag read: %d\n", pagb));
@@ -543,8 +553,12 @@ getnext(DBM *db)
                db->pagbno = db->blkptr;
                if (read(db->pagf, db->pagbuf, PBLKSIZ) <= 0)
                        break;
-               if (!chkpage(db->pagbuf))
-                       break;
+               if (!chkpage(db->pagbuf)) {
+                    errno = EINVAL;
+                    db->pagbno = -1;
+                    ioerr(db);
+                    break;
+                }
        }
 
        return ioerr(db), nullitem;
diff --git a/ext/SDBM_File/t/corrupt.t b/ext/SDBM_File/t/corrupt.t
new file mode 100644 (file)
index 0000000..f619b78
--- /dev/null
@@ -0,0 +1,152 @@
+#!./perl
+use strict;
+use Test::More;
+use MIME::Base64;
+use File::Temp 'tempfile';
+use SDBM_File;
+use Fcntl qw(O_RDWR);
+use Errno qw(EINVAL);
+use Config;
+
+$Config{byteorder} =~ /^1234(?:5678)?$/
+  or plan skip_all => "test SDBM databases are little-endian";
+$Config{shortsize} == 2
+  or plan skip_all => "test SDBM databases have short size 2";
+
+my ($dirfh, $dirname) = tempfile(UNLINK => 1);
+my ($pagfh, $pagname) = tempfile(UNLINK => 1);
+close $dirfh;
+close pagefh;
+
+# these might only fail under ASAN
+while (my $testdata = do { local $/ = "END\n"; <DATA>; }) {
+    my ($note, $base64) = $testdata =~ /\A([^\n]+)\n(.*)/s
+      or die;
+    my $raw = decode_base64($base64);
+    open my $pagfh, ">:raw", $pagname
+      or die "Cannot recreate $pagname: $!";
+    print $pagfh $base64;
+    close $pagfh;
+    open my $dirfh, ">:raw", $dirname
+      or die "Cannot truncate $dirname: $!";
+    close $dirfh;
+
+    my %dbm;
+    my $sdbm = tie %dbm, 'SDBM_File', $dirname, O_RDWR, 0666, $pagname;
+    
+    ok(tied %dbm, "$note: tied successfully");
+    my $value = $dbm{foo};
+    pass("$note: no crash fetching a named key");
+    for my $key (sort keys %dbm) {
+        my $tmp = $dbm{$key};
+    }
+    pass("$note: no crash iterating over keys");
+    is(0+$!, EINVAL, "$note: errno set");
+    ok($sdbm->error, "$note: error flag set");
+}
+
+
+done_testing();
+
+__DATA__
+132147
+CgD+g/4D/QP8A/sD2gPZA8wDywPIAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABm
+OmZxYlpYWEhaZ0ExbDFQY2R7U0hBfWhLVVdoQnVuZWx0R1NONHMwTi9MTU9wRzI3UT1jYmJhYQ==
+END
+132151
+AiD/A/4DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAGZo
+END
+fuzz failure 54
+EADwA78DsQNpA1YDIgMWA8ECswJqAmACPQImAuEBywGqtgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjAAAAAAAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAsQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPP9hwAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBAAAAAABtAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHgs
+eHh4YWJjZGVmZ2hpamtsbW5vcHFyNDg2NHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
+eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHjkeHh4eHh4eGFiY2RlZmdoaWprbG1ub3BxcnMy
+MTU5eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eEYxMjM0NTY3NjI2eHh4eHh4eHh4
+eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
+eHh4eHh4eGFiY2RlZmdoaWoyMzU4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
+eHh4X3h4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eDEyMzQ1Njc4
+MzE3MXh4eHh4eHh4eHiYeHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHhh
+YmNkZWZnaGlqa2xtbm8zMDE2eHh4eLh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
+eHiqeHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4MTIzNDU2Yzg5MDI3NjJ4eHh4eHh4eHh4
+eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eE14eHh4eHh4YWJjZGVmZ2hpamtsbW5vcA==
+END
+fuzz failure 181
+BAD/A8MDuAOduwAAAAAAAAAAAAAAAAAAAAAAAAAAUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eDEyMzQ1NjcyOTczeHh4eHh4
+eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4MQ==
+END
+fuzz failure 695
+EADoA+cD3wN/A3kDIAMFA+wC3wKeApkCgQJ3AmgCYgJdtwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ADMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGoAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAACTAAAAAAAAAAAAAAAAAAB4eHh4eGFiY2RlZnh4eHh4eHh4eHh4
+eHh4eGFiY2RlZjkyMzZ4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHgxMzUyMnh4eHh4eHh4eHh4eHh4
+eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4YWJjZGVm
+Z2hpNTUxOXh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHhhYmNkZWZnaGlqa2xtbm9wcXJzdHV2dzg1
+NTF4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
+eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eDEyMTE3NHh4eHh4eHh4eHh4eHh4eHh4
+eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
+eHh4eHh4eHh4eHh4eHh4eHh4eHh4eDEyMzQ1NTMyeGFiY2RlZmdoaWprbG1ub3BxcnN0ODE1Mg==
+END