Ticket #3681: data_dump_infinite_recurse.diff
File data_dump_infinite_recurse.diff, 11.0 KB (added by , 10 years ago) |
---|
-
MANIFEST
From 92ad49d8266598fece30e2bdf721969112212e54 Mon Sep 17 00:00:00 2001 From: Tony Cook <tony@develop-help.com> Date: Mon, 30 Jun 2014 12:16:03 +1000 Subject: don't recurse infinitely in Data::Dumper Add a configuration variable/option to limit recursion when dumping deep data structures. Defaults the limit to 1000, which can be reduced or increase, or eliminated by setting it to 0. This patch addresses CVE-2014-4330. This bug was found and reported by: LSE Leading Security Experts GmbH employee Markus Vervier. [Patch backported to 5.20 by Dominic Hargreaves for Debian.] Origin: http://perl5.git.perl.org/perl.git/commit/19be3be6968e2337bcdfe480693fff795ecd1304 Patch-Name: fixes/data_dump_infinite_recurse.diff --- MANIFEST | 1 + dist/Data-Dumper/Dumper.pm | 23 ++++++++++++++++++++++ dist/Data-Dumper/Dumper.xs | 32 ++++++++++++++++++++++--------- dist/Data-Dumper/t/recurse.t | 45 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 dist/Data-Dumper/t/recurse.t diff --git a/MANIFEST b/MANIFEST index f4f7404..867ea7d 100644
a b dist/Data-Dumper/t/perl-74170.t Regression test for stack reallocation 2994 2994 dist/Data-Dumper/t/purity_deepcopy_maxdepth.t See if three Data::Dumper functions work 2995 2995 dist/Data-Dumper/t/qr.t See if Data::Dumper works with qr|/| 2996 2996 dist/Data-Dumper/t/quotekeys.t See if Data::Dumper::Quotekeys works 2997 dist/Data-Dumper/t/recurse.t See if Data::Dumper::Maxrecurse works 2997 2998 dist/Data-Dumper/t/seen.t See if Data::Dumper::Seen works 2998 2999 dist/Data-Dumper/t/sortkeys.t See if Data::Dumper::Sortkeys works 2999 3000 dist/Data-Dumper/t/sparseseen.t See if Data::Dumper::Sparseseen works -
dist/Data-Dumper/Dumper.pm
diff --git a/dist/Data-Dumper/Dumper.pm b/dist/Data-Dumper/Dumper.pm index 7c8a72c..49121ce 100644
a b $Useperl = 0 unless defined $Useperl; 56 56 $Sortkeys = 0 unless defined $Sortkeys; 57 57 $Deparse = 0 unless defined $Deparse; 58 58 $Sparseseen = 0 unless defined $Sparseseen; 59 $Maxrecurse = 1000 unless defined $Maxrecurse; 59 60 60 61 # 61 62 # expects an arrayref of values to be dumped. … … sub new { 92 93 'bless' => $Bless, # keyword to use for "bless" 93 94 # expdepth => $Expdepth, # cutoff depth for explicit dumping 94 95 maxdepth => $Maxdepth, # depth beyond which we give up 96 maxrecurse => $Maxrecurse, # depth beyond which we abort 95 97 useperl => $Useperl, # use the pure Perl implementation 96 98 sortkeys => $Sortkeys, # flag or filter for sorting hash keys 97 99 deparse => $Deparse, # use B::Deparse for coderefs … … sub _dump { 350 352 return qq['$val']; 351 353 } 352 354 355 # avoid recursing infinitely [perl #122111] 356 if ($s->{maxrecurse} > 0 357 and $s->{level} >= $s->{maxrecurse}) { 358 die "Recursion limit of $s->{maxrecurse} exceeded"; 359 } 360 353 361 # we have a blessed ref 354 362 my ($blesspad); 355 363 if ($realpack and !$no_bless) { … … sub Maxdepth { 680 688 defined($v) ? (($s->{'maxdepth'} = $v), return $s) : $s->{'maxdepth'}; 681 689 } 682 690 691 sub Maxrecurse { 692 my($s, $v) = @_; 693 defined($v) ? (($s->{'maxrecurse'} = $v), return $s) : $s->{'maxrecurse'}; 694 } 695 683 696 sub Useperl { 684 697 my($s, $v) = @_; 685 698 defined($v) ? (($s->{'useperl'} = $v), return $s) : $s->{'useperl'}; … … no maximum depth. 1105 1118 1106 1119 =item * 1107 1120 1121 $Data::Dumper::Maxrecurse I<or> $I<OBJ>->Maxrecurse(I<[NEWVAL]>) 1122 1123 Can be set to a positive integer that specifies the depth beyond which 1124 recursion into a structure will throw an exception. This is intended 1125 as a security measure to prevent perl running out of stack space when 1126 dumping an excessively deep structure. Can be set to 0 to remove the 1127 limit. Default is 1000. 1128 1129 =item * 1130 1108 1131 $Data::Dumper::Useperl I<or> $I<OBJ>->Useperl(I<[NEWVAL]>) 1109 1132 1110 1133 Can be set to a boolean value which controls whether the pure Perl -
dist/Data-Dumper/Dumper.xs
diff --git a/dist/Data-Dumper/Dumper.xs b/dist/Data-Dumper/Dumper.xs index 12c4ebd..49937be 100644
a b static I32 DD_dump (pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, 28 28 SV *pad, SV *xpad, SV *apad, SV *sep, SV *pair, 29 29 SV *freezer, SV *toaster, 30 30 I32 purity, I32 deepcopy, I32 quotekeys, SV *bless, 31 I32 maxdepth, SV *sortkeys, int use_sparse_seen_hash, I32 useqq );31 I32 maxdepth, SV *sortkeys, int use_sparse_seen_hash, I32 useqq, IV maxrecurse); 32 32 33 33 #ifndef HvNAME_get 34 34 #define HvNAME_get HvNAME … … DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv, 412 412 AV *postav, I32 *levelp, I32 indent, SV *pad, SV *xpad, 413 413 SV *apad, SV *sep, SV *pair, SV *freezer, SV *toaster, I32 purity, 414 414 I32 deepcopy, I32 quotekeys, SV *bless, I32 maxdepth, SV *sortkeys, 415 int use_sparse_seen_hash, I32 useqq )415 int use_sparse_seen_hash, I32 useqq, IV maxrecurse) 416 416 { 417 417 char tmpbuf[128]; 418 418 Size_t i; … … DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv, 589 589 return 1; 590 590 } 591 591 592 if (maxrecurse > 0 && *levelp >= maxrecurse) { 593 croak("Recursion limit of %" IVdf " exceeded", maxrecurse); 594 } 595 592 596 if (realpack && !no_bless) { /* we have a blessed ref */ 593 597 STRLEN blesslen; 594 598 const char * const blessstr = SvPV(bless, blesslen); … … DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv, 674 678 DD_dump(aTHX_ ival, SvPVX_const(namesv), SvCUR(namesv), retval, seenhv, 675 679 postav, levelp, indent, pad, xpad, apad, sep, pair, 676 680 freezer, toaster, purity, deepcopy, quotekeys, bless, 677 maxdepth, sortkeys, use_sparse_seen_hash, useqq); 681 maxdepth, sortkeys, use_sparse_seen_hash, useqq, 682 maxrecurse); 678 683 sv_catpvn(retval, ")}", 2); 679 684 } /* plain */ 680 685 else { … … DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv, 682 687 DD_dump(aTHX_ ival, SvPVX_const(namesv), SvCUR(namesv), retval, seenhv, 683 688 postav, levelp, indent, pad, xpad, apad, sep, pair, 684 689 freezer, toaster, purity, deepcopy, quotekeys, bless, 685 maxdepth, sortkeys, use_sparse_seen_hash, useqq); 690 maxdepth, sortkeys, use_sparse_seen_hash, useqq, 691 maxrecurse); 686 692 } 687 693 SvREFCNT_dec(namesv); 688 694 } … … DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv, 694 700 DD_dump(aTHX_ ival, SvPVX_const(namesv), SvCUR(namesv), retval, seenhv, 695 701 postav, levelp, indent, pad, xpad, apad, sep, pair, 696 702 freezer, toaster, purity, deepcopy, quotekeys, bless, 697 maxdepth, sortkeys, use_sparse_seen_hash, useqq); 703 maxdepth, sortkeys, use_sparse_seen_hash, useqq, 704 maxrecurse); 698 705 SvREFCNT_dec(namesv); 699 706 } 700 707 else if (realtype == SVt_PVAV) { … … DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv, 767 774 DD_dump(aTHX_ elem, iname, ilen, retval, seenhv, postav, 768 775 levelp, indent, pad, xpad, apad, sep, pair, 769 776 freezer, toaster, purity, deepcopy, quotekeys, bless, 770 maxdepth, sortkeys, use_sparse_seen_hash, useqq); 777 maxdepth, sortkeys, use_sparse_seen_hash, 778 useqq, maxrecurse); 771 779 if (ix < ixmax) 772 780 sv_catpvn(retval, ",", 1); 773 781 } … … DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv, 970 978 DD_dump(aTHX_ hval, SvPVX_const(sname), SvCUR(sname), retval, seenhv, 971 979 postav, levelp, indent, pad, xpad, newapad, sep, pair, 972 980 freezer, toaster, purity, deepcopy, quotekeys, bless, 973 maxdepth, sortkeys, use_sparse_seen_hash, useqq); 981 maxdepth, sortkeys, use_sparse_seen_hash, useqq, 982 maxrecurse); 974 983 SvREFCNT_dec(sname); 975 984 Safefree(nkey_buffer); 976 985 if (indent >= 2) … … DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv, 1179 1188 seenhv, postav, &nlevel, indent, pad, xpad, 1180 1189 newapad, sep, pair, freezer, toaster, purity, 1181 1190 deepcopy, quotekeys, bless, maxdepth, 1182 sortkeys, use_sparse_seen_hash, useqq); 1191 sortkeys, use_sparse_seen_hash, useqq, 1192 maxrecurse); 1183 1193 SvREFCNT_dec(e); 1184 1194 } 1185 1195 } … … Data_Dumper_Dumpxs(href, ...) 1269 1279 SV *val, *name, *pad, *xpad, *apad, *sep, *pair, *varname; 1270 1280 SV *freezer, *toaster, *bless, *sortkeys; 1271 1281 I32 purity, deepcopy, quotekeys, maxdepth = 0; 1282 IV maxrecurse = 1000; 1272 1283 char tmpbuf[1024]; 1273 1284 I32 gimme = GIMME; 1274 1285 int use_sparse_seen_hash = 0; … … Data_Dumper_Dumpxs(href, ...) 1355 1366 bless = *svp; 1356 1367 if ((svp = hv_fetch(hv, "maxdepth", 8, FALSE))) 1357 1368 maxdepth = SvIV(*svp); 1369 if ((svp = hv_fetch(hv, "maxrecurse", 10, FALSE))) 1370 maxrecurse = SvIV(*svp); 1358 1371 if ((svp = hv_fetch(hv, "sortkeys", 8, FALSE))) { 1359 1372 sortkeys = *svp; 1360 1373 if (! SvTRUE(sortkeys)) … … Data_Dumper_Dumpxs(href, ...) 1434 1447 DD_dump(aTHX_ val, SvPVX_const(name), SvCUR(name), valstr, seenhv, 1435 1448 postav, &level, indent, pad, xpad, newapad, sep, pair, 1436 1449 freezer, toaster, purity, deepcopy, quotekeys, 1437 bless, maxdepth, sortkeys, use_sparse_seen_hash, useqq); 1450 bless, maxdepth, sortkeys, use_sparse_seen_hash, 1451 useqq, maxrecurse); 1438 1452 SPAGAIN; 1439 1453 1440 1454 if (indent >= 2 && !terse) -
new file dist/Data-Dumper/t/recurse.t
diff --git a/dist/Data-Dumper/t/recurse.t b/dist/Data-Dumper/t/recurse.t new file mode 100644 index 0000000..275a89d
- + 1 #!perl 2 3 # Test the Maxrecurse option 4 5 use strict; 6 use Test::More tests => 32; 7 use Data::Dumper; 8 9 SKIP: { 10 skip "no XS available", 16 11 if $Data::Dumper::Useperl; 12 local $Data::Dumper::Useperl = 1; 13 test_recursion(); 14 } 15 16 test_recursion(); 17 18 sub test_recursion { 19 my $pp = $Data::Dumper::Useperl ? "pure perl" : "XS"; 20 $Data::Dumper::Purity = 1; # make sure this has no effect 21 $Data::Dumper::Indent = 0; 22 $Data::Dumper::Maxrecurse = 1; 23 is(eval { Dumper([]) }, '$VAR1 = [];', "$pp: maxrecurse 1, []"); 24 is(eval { Dumper([[]]) }, undef, "$pp: maxrecurse 1, [[]]"); 25 ok($@, "exception thrown"); 26 is(eval { Dumper({}) }, '$VAR1 = {};', "$pp: maxrecurse 1, {}"); 27 is(eval { Dumper({ a => 1 }) }, q($VAR1 = {'a' => 1};), 28 "$pp: maxrecurse 1, { a => 1 }"); 29 is(eval { Dumper({ a => {} }) }, undef, "$pp: maxrecurse 1, { a => {} }"); 30 ok($@, "exception thrown"); 31 is(eval { Dumper(\1) }, "\$VAR1 = \\1;", "$pp: maxrecurse 1, \\1"); 32 is(eval { Dumper(\\1) }, undef, "$pp: maxrecurse 1, \\1"); 33 ok($@, "exception thrown"); 34 $Data::Dumper::Maxrecurse = 3; 35 is(eval { Dumper(\1) }, "\$VAR1 = \\1;", "$pp: maxrecurse 3, \\1"); 36 is(eval { Dumper(\(my $s = {})) }, "\$VAR1 = \\{};", "$pp: maxrecurse 3, \\{}"); 37 is(eval { Dumper(\(my $s = { a => [] })) }, "\$VAR1 = \\{'a' => []};", 38 "$pp: maxrecurse 3, \\{ a => [] }"); 39 is(eval { Dumper(\(my $s = { a => [{}] })) }, undef, 40 "$pp: maxrecurse 3, \\{ a => [{}] }"); 41 ok($@, "exception thrown"); 42 $Data::Dumper::Maxrecurse = 0; 43 is(eval { Dumper([[[[[]]]]]) }, q($VAR1 = [[[[[]]]]];), 44 "$pp: check Maxrecurse doesn't set limit to 0 recursion"); 45 }