Data::Dumper: handle huge inputs on 64-bit platforms
authorAaron Crane <arc@cpan.org>
Tue, 12 Jan 2016 00:05:40 +0000 (00:05 +0000)
committerAaron Crane <arc@cpan.org>
Tue, 12 Jan 2016 00:41:24 +0000 (00:41 +0000)
Several quantities relating to string escaping were being stored in 32-bit
variables. On a 64-bit system, pathological inputs could overflow the
available space and cause incorrect output.

The test for this requires about 10 GB of memory, so it is disabled except
when PERL_TEST_MEMORY is set to at least 10.

There are other questionable-looking uses of I32 in Dumper.xs, but they
don't seem to be exploitable. (It helps, for example, that the core hash API
restricts key lengths to 2**31-1.) That said, it may be worth auditing the
code rather more carefully for potential problems.

MANIFEST
dist/Data-Dumper/Dumper.pm
dist/Data-Dumper/Dumper.xs
dist/Data-Dumper/t/huge.t [new file with mode: 0644]

index 0d41bbf..e75199d 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -3069,6 +3069,7 @@ dist/Data-Dumper/t/dumper.t       See if Data::Dumper works
 dist/Data-Dumper/t/dumpperl.t  See if Data::Dumper::Dumpperl works
 dist/Data-Dumper/t/freezer.t   See if Data::Dumper::Freezer works
 dist/Data-Dumper/t/freezer_useperl.t   See if Data::Dumper works
+dist/Data-Dumper/t/huge.t      See if Data::Dumper works on huge inputs
 dist/Data-Dumper/t/indent.t    See if Data::Dumper::Indent works
 dist/Data-Dumper/t/lib/Testing.pm      Functions used in testing Data-Dumper
 dist/Data-Dumper/t/misc.t      Miscellaneous tests for Data-Dumper
index ace9b78..13be89d 100644 (file)
@@ -10,7 +10,7 @@
 package Data::Dumper;
 
 BEGIN {
-    $VERSION = '2.159'; # Don't forget to set version and release
+    $VERSION = '2.160'; # Don't forget to set version and release
 }               # date in POD below!
 
 #$| = 1;
@@ -1472,7 +1472,7 @@ modify it under the same terms as Perl itself.
 
 =head1 VERSION
 
-Version 2.159  (December 15 2015)
+Version 2.160  (January 12 2016)
 
 =head1 SEE ALSO
 
index 3440891..8220241 100644 (file)
@@ -65,9 +65,9 @@ typedef struct {
     int trailingcomma;
 } Style;
 
-static I32 num_q (const char *s, STRLEN slen);
-static I32 esc_q (char *dest, const char *src, STRLEN slen);
-static I32 esc_q_utf8 (pTHX_ SV *sv, const char *src, STRLEN slen, I32 do_utf8, I32 useqq);
+static STRLEN num_q (const char *s, STRLEN slen);
+static STRLEN esc_q (char *dest, const char *src, STRLEN slen);
+static STRLEN esc_q_utf8 (pTHX_ SV *sv, const char *src, STRLEN slen, I32 do_utf8, I32 useqq);
 static bool globname_needs_quote(const char *s, STRLEN len);
 static bool key_needs_quote(const char *s, STRLEN len);
 static bool safe_decimal_number(const char *p, STRLEN len);
@@ -217,10 +217,10 @@ safe_decimal_number(const char *p, STRLEN len) {
 }
 
 /* count the number of "'"s and "\"s in string */
-static I32
+static STRLEN
 num_q(const char *s, STRLEN slen)
 {
-    I32 ret = 0;
+    STRLEN ret = 0;
 
     while (slen > 0) {
        if (*s == '\'' || *s == '\\')
@@ -235,10 +235,10 @@ num_q(const char *s, STRLEN slen)
 /* returns number of chars added to escape "'"s and "\"s in s */
 /* slen number of characters in s will be escaped */
 /* destination must be long enough for additional chars */
-static I32
+static STRLEN
 esc_q(char *d, const char *s, STRLEN slen)
 {
-    I32 ret = 0;
+    STRLEN ret = 0;
 
     while (slen > 0) {
        switch (*s) {
@@ -257,7 +257,7 @@ esc_q(char *d, const char *s, STRLEN slen)
 }
 
 /* this function is also misused for implementing $Useqq */
-static I32
+static STRLEN
 esc_q_utf8(pTHX_ SV* sv, const char *src, STRLEN slen, I32 do_utf8, I32 useqq)
 {
     char *r, *rstart;
@@ -886,7 +886,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
            SV *sname;
            HE *entry = NULL;
            char *key;
-           I32 klen;
+           STRLEN klen;
            SV *hval;
            AV *keys = NULL;
        
@@ -974,10 +974,10 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
             for (i = 0; 1; i++) {
                char *nkey;
                 char *nkey_buffer = NULL;
-               I32 nticks = 0;
+                STRLEN nticks = 0;
                SV* keysv;
                STRLEN keylen;
-                I32 nlen;
+                STRLEN nlen;
                bool do_utf8 = FALSE;
 
                if (style->sortkeys) {
@@ -1059,7 +1059,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
                 sv_catsv(retval, style->pair);
                 if (style->indent >= 2) {
                    char *extra;
-                   I32 elen = 0;
+                    STRLEN elen = 0;
                    newapad = newSVsv(apad);
                    New(0, extra, klen+4+1, char);
                    while (elen < (klen+4))
@@ -1104,8 +1104,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
        }
 
        if (realpack && !no_bless) {  /* free blessed allocs */
-           I32 plen;
-           I32 pticks;
+            STRLEN plen, pticks;
 
             if (style->indent >= 2) {
                SvREFCNT_dec(apad);
diff --git a/dist/Data-Dumper/t/huge.t b/dist/Data-Dumper/t/huge.t
new file mode 100644 (file)
index 0000000..09343b7
--- /dev/null
@@ -0,0 +1,33 @@
+#!./perl -w
+#
+# automated tests for Data::Dumper that need large amounts of memory; they
+# are skipped unless PERL_TEST_MEMORY is set, and at least 10
+#
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use Config;
+use Data::Dumper;
+
+BEGIN {
+    plan skip_all => 'Data::Dumper was not built'
+        if $Config{extensions} !~ m{\b Data/Dumper \b}x;
+    plan skip_all => 'Need 64-bit pointers for this test'
+        if $Config{ptrsize} < 8;
+    plan skip_all => 'Need ~10 GiB of core for this test'
+        if !$ENV{PERL_TEST_MEMORY} || $ENV{PERL_TEST_MEMORY} < 10;
+}
+
+plan tests => 1;
+
+{
+    my $input = q/'/ x 2**31;
+    my $len = length Dumper($input);
+    # Each single-quote will get backslashed, so the output must have
+    # stricly more than twice as many characters as the input.
+    cmp_ok($len, '>', 2**32, 'correct output for huge all-quotable value');
+    undef $input;
+}