Hi Alexander, Alexander Bluhm wrote on Fri, Oct 24, 2014 at 10:55:07PM +0200: > On Fri, Oct 24, 2014 at 10:40:55PM +0200, Alexander Bluhm wrote:
>> Here is the diff that applies to -current. I have compared it with >> the perl git and with Data::Dumper on CPAN. It looks correct. Confirmed. > I have forgotten to cvs add dist/Data-Dumper/t/recurse.t > so here is the diff with the new test. > > ok? Reads good. Also checked that the test suite succeeds and that mitigation is effective (on i386). So *if* we decide to patch it, ok schwarze@ for this version of the patch. It physically and logically conflicts with future Perl updates, though (changes to the same lines; changing the parameter lists of the same functions in different ways). I think it would be nice to hear how Andrew thinks such issues should be addressed to minimize the pain during future Perl updates. >> Alternatively we could update Data::Dumper to 2.154. I'd say answering that question is at least in part Andrew's call. I'm not sure whether that makes the upcoming 1.20 update easier or harder. Yours, Ingo > Index: gnu/usr.bin/perl/MANIFEST > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/gnu/usr.bin/perl/MANIFEST,v > retrieving revision 1.29 > diff -u -p -u -p -r1.29 MANIFEST > --- gnu/usr.bin/perl/MANIFEST 24 Mar 2014 15:05:12 -0000 1.29 > +++ gnu/usr.bin/perl/MANIFEST 24 Oct 2014 20:19:35 -0000 > @@ -3155,6 +3155,7 @@ dist/Data-Dumper/t/perl-74170.t Regressi > dist/Data-Dumper/t/purity_deepcopy_maxdepth.t See if three > Data::Dumper functions work > dist/Data-Dumper/t/qr.t See if Data::Dumper works with qr|/| > dist/Data-Dumper/t/quotekeys.t See if Data::Dumper::Quotekeys works > +dist/Data-Dumper/t/recurse.t See if Data::Dumper::Maxrecurse works > dist/Data-Dumper/t/seen.t See if Data::Dumper::Seen works > dist/Data-Dumper/t/sortkeys.t See if Data::Dumper::Sortkeys works > dist/Data-Dumper/t/sparseseen.t See if Data::Dumper::Sparseseen works > Index: gnu/usr.bin/perl/patchlevel.h > =================================================================== > RCS file: /data/mirror/openbsd/cvs/src/gnu/usr.bin/perl/patchlevel.h,v > retrieving revision 1.34 > diff -u -p -u -p -r1.34 patchlevel.h > --- gnu/usr.bin/perl/patchlevel.h 5 Sep 2014 06:53:07 -0000 1.34 > +++ gnu/usr.bin/perl/patchlevel.h 24 Oct 2014 20:25:05 -0000 > @@ -134,6 +134,7 @@ hunk. > static const char * const local_patches[] = { > NULL > ,"Update libnet to 1.27" > + ,"CVE-2014-4330" > #ifdef PERL_GIT_UNCOMMITTED_CHANGES > ,"uncommitted-changes" > #endif > Index: gnu/usr.bin/perl/dist/Data-Dumper/Dumper.pm > =================================================================== > RCS file: > /data/mirror/openbsd/cvs/src/gnu/usr.bin/perl/dist/Data-Dumper/Dumper.pm,v > retrieving revision 1.1.1.3 > diff -u -p -u -p -r1.1.1.3 Dumper.pm > --- gnu/usr.bin/perl/dist/Data-Dumper/Dumper.pm 24 Mar 2014 14:58:59 > -0000 1.1.1.3 > +++ gnu/usr.bin/perl/dist/Data-Dumper/Dumper.pm 24 Oct 2014 20:19:35 > -0000 > @@ -56,6 +56,7 @@ $Useperl = 0 unless defined $ > $Sortkeys = 0 unless defined $Sortkeys; > $Deparse = 0 unless defined $Deparse; > $Sparseseen = 0 unless defined $Sparseseen; > +$Maxrecurse = 1000 unless defined $Maxrecurse; > > # > # expects an arrayref of values to be dumped. > @@ -92,6 +93,7 @@ sub new { > 'bless' => $Bless, # keyword to use for "bless" > # expdepth => $Expdepth, # cutoff depth for explicit dumping > maxdepth => $Maxdepth, # depth beyond which we give up > + maxrecurse => $Maxrecurse, # depth beyond which we abort > useperl => $Useperl, # use the pure Perl implementation > sortkeys => $Sortkeys, # flag or filter for sorting hash keys > deparse => $Deparse, # use B::Deparse for coderefs > @@ -351,6 +353,12 @@ sub _dump { > return qq['$val']; > } > > + # avoid recursing infinitely [perl #122111] > + if ($s->{maxrecurse} > 0 > + and $s->{level} >= $s->{maxrecurse}) { > + die "Recursion limit of $s->{maxrecurse} exceeded"; > + } > + > # we have a blessed ref > my ($blesspad); > if ($realpack and !$no_bless) { > @@ -683,6 +691,11 @@ sub Maxdepth { > defined($v) ? (($s->{'maxdepth'} = $v), return $s) : $s->{'maxdepth'}; > } > > +sub Maxrecurse { > + my($s, $v) = @_; > + defined($v) ? (($s->{'maxrecurse'} = $v), return $s) : $s->{'maxrecurse'}; > +} > + > sub Useperl { > my($s, $v) = @_; > defined($v) ? (($s->{'useperl'} = $v), return $s) : $s->{'useperl'}; > @@ -1105,6 +1118,16 @@ we don't venture into a structure. Has > C<Data::Dumper::Purity> is set. (Useful in debugger when we often don't > want to see more than enough). Default is 0, which means there is > no maximum depth. > + > +=item * > + > +$Data::Dumper::Maxrecurse I<or> $I<OBJ>->Maxrecurse(I<[NEWVAL]>) > + > +Can be set to a positive integer that specifies the depth beyond which > +recursion into a structure will throw an exception. This is intended > +as a security measure to prevent perl running out of stack space when > +dumping an excessively deep structure. Can be set to 0 to remove the > +limit. Default is 1000. > > =item * > > Index: gnu/usr.bin/perl/dist/Data-Dumper/Dumper.xs > =================================================================== > RCS file: > /data/mirror/openbsd/cvs/src/gnu/usr.bin/perl/dist/Data-Dumper/Dumper.xs,v > retrieving revision 1.1.1.3 > diff -u -p -u -p -r1.1.1.3 Dumper.xs > --- gnu/usr.bin/perl/dist/Data-Dumper/Dumper.xs 24 Mar 2014 14:58:59 > -0000 1.1.1.3 > +++ gnu/usr.bin/perl/dist/Data-Dumper/Dumper.xs 24 Oct 2014 20:22:57 > -0000 > @@ -26,7 +26,8 @@ static I32 DD_dump (pTHX_ SV *val, const > SV *pad, SV *xpad, SV *apad, SV *sep, SV *pair, > SV *freezer, SV *toaster, > I32 purity, I32 deepcopy, I32 quotekeys, SV *bless, > - I32 maxdepth, SV *sortkeys, int use_sparse_seen_hash); > + I32 maxdepth, SV *sortkeys, int use_sparse_seen_hash, > + IV maxrecurse); > > #ifndef HvNAME_get > #define HvNAME_get HvNAME > @@ -298,7 +299,7 @@ DD_dump(pTHX_ SV *val, const char *name, > AV *postav, I32 *levelp, I32 indent, SV *pad, SV *xpad, > SV *apad, SV *sep, SV *pair, SV *freezer, SV *toaster, I32 purity, > I32 deepcopy, I32 quotekeys, SV *bless, I32 maxdepth, SV *sortkeys, > - int use_sparse_seen_hash) > + int use_sparse_seen_hash, IV maxrecurse) > { > char tmpbuf[128]; > U32 i; > @@ -475,6 +476,10 @@ DD_dump(pTHX_ SV *val, const char *name, > return 1; > } > > + if (maxrecurse > 0 && *levelp >= maxrecurse) { > + croak("Recursion limit of %" IVdf " exceeded", maxrecurse); > + } > + > if (realpack && !no_bless) { /* we have a > blessed ref */ > STRLEN blesslen; > const char * const blessstr = SvPV(bless, blesslen); > @@ -524,7 +529,7 @@ DD_dump(pTHX_ SV *val, const char *name, > DD_dump(aTHX_ ival, SvPVX_const(namesv), SvCUR(namesv), retval, > seenhv, > postav, levelp, indent, pad, xpad, apad, sep, pair, > freezer, toaster, purity, deepcopy, quotekeys, bless, > - maxdepth, sortkeys, use_sparse_seen_hash); > + maxdepth, sortkeys, use_sparse_seen_hash, maxrecurse); > sv_catpvn(retval, ")}", 2); > } /* plain */ > else { > @@ -532,7 +537,7 @@ DD_dump(pTHX_ SV *val, const char *name, > DD_dump(aTHX_ ival, SvPVX_const(namesv), SvCUR(namesv), retval, > seenhv, > postav, levelp, indent, pad, xpad, apad, sep, pair, > freezer, toaster, purity, deepcopy, quotekeys, bless, > - maxdepth, sortkeys, use_sparse_seen_hash); > + maxdepth, sortkeys, use_sparse_seen_hash, maxrecurse); > } > SvREFCNT_dec(namesv); > } > @@ -544,7 +549,7 @@ DD_dump(pTHX_ SV *val, const char *name, > DD_dump(aTHX_ ival, SvPVX_const(namesv), SvCUR(namesv), retval, > seenhv, > postav, levelp, indent, pad, xpad, apad, sep, pair, > freezer, toaster, purity, deepcopy, quotekeys, bless, > - maxdepth, sortkeys, use_sparse_seen_hash); > + maxdepth, sortkeys, use_sparse_seen_hash, maxrecurse); > SvREFCNT_dec(namesv); > } > else if (realtype == SVt_PVAV) { > @@ -617,7 +622,7 @@ DD_dump(pTHX_ SV *val, const char *name, > DD_dump(aTHX_ elem, iname, ilen, retval, seenhv, postav, > levelp, indent, pad, xpad, apad, sep, pair, > freezer, toaster, purity, deepcopy, quotekeys, bless, > - maxdepth, sortkeys, use_sparse_seen_hash); > + maxdepth, sortkeys, use_sparse_seen_hash, maxrecurse); > if (ix < ixmax) > sv_catpvn(retval, ",", 1); > } > @@ -824,7 +829,7 @@ DD_dump(pTHX_ SV *val, const char *name, > DD_dump(aTHX_ hval, SvPVX_const(sname), SvCUR(sname), retval, > seenhv, > postav, levelp, indent, pad, xpad, newapad, sep, pair, > freezer, toaster, purity, deepcopy, quotekeys, bless, > - maxdepth, sortkeys, use_sparse_seen_hash); > + maxdepth, sortkeys, use_sparse_seen_hash, maxrecurse); > SvREFCNT_dec(sname); > Safefree(nkey_buffer); > if (indent >= 2) > @@ -1033,7 +1038,7 @@ DD_dump(pTHX_ SV *val, const char *name, > seenhv, postav, &nlevel, indent, pad, xpad, > newapad, sep, pair, freezer, toaster, purity, > deepcopy, quotekeys, bless, maxdepth, > - sortkeys, use_sparse_seen_hash); > + sortkeys, use_sparse_seen_hash, maxrecurse); > SvREFCNT_dec(e); > } > } > @@ -1113,6 +1118,7 @@ Data_Dumper_Dumpxs(href, ...) > SV *val, *name, *pad, *xpad, *apad, *sep, *pair, *varname; > SV *freezer, *toaster, *bless, *sortkeys; > I32 purity, deepcopy, quotekeys, maxdepth = 0; > + IV maxrecurse = 1000; > char tmpbuf[1024]; > I32 gimme = GIMME; > int use_sparse_seen_hash = 0; > @@ -1201,6 +1207,8 @@ Data_Dumper_Dumpxs(href, ...) > bless = *svp; > if ((svp = hv_fetch(hv, "maxdepth", 8, FALSE))) > maxdepth = SvIV(*svp); > + if ((svp = hv_fetch(hv, "maxrecurse", 10, FALSE))) > + maxrecurse = SvIV(*svp); > if ((svp = hv_fetch(hv, "sortkeys", 8, FALSE))) { > sortkeys = *svp; > if (! SvTRUE(sortkeys)) > @@ -1280,7 +1288,8 @@ Data_Dumper_Dumpxs(href, ...) > DD_dump(aTHX_ val, SvPVX_const(name), SvCUR(name), valstr, > seenhv, > postav, &level, indent, pad, xpad, newapad, sep, > pair, > freezer, toaster, purity, deepcopy, quotekeys, > - bless, maxdepth, sortkeys, use_sparse_seen_hash); > + bless, maxdepth, sortkeys, use_sparse_seen_hash, > + maxrecurse); > SPAGAIN; > > if (indent >= 2 && !terse) > Index: gnu/usr.bin/perl/dist/Data-Dumper/t/recurse.t > =================================================================== > RCS file: gnu/usr.bin/perl/dist/Data-Dumper/t/recurse.t > diff -N gnu/usr.bin/perl/dist/Data-Dumper/t/recurse.t > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ gnu/usr.bin/perl/dist/Data-Dumper/t/recurse.t 24 Oct 2014 20:21:37 > -0000 > @@ -0,0 +1,45 @@ > +#!perl > + > +# Test the Maxrecurse option > + > +use strict; > +use Test::More tests => 32; > +use Data::Dumper; > + > +SKIP: { > + skip "no XS available", 16 > + if $Data::Dumper::Useperl; > + local $Data::Dumper::Useperl = 1; > + test_recursion(); > +} > + > +test_recursion(); > + > +sub test_recursion { > + my $pp = $Data::Dumper::Useperl ? "pure perl" : "XS"; > + $Data::Dumper::Purity = 1; # make sure this has no effect > + $Data::Dumper::Indent = 0; > + $Data::Dumper::Maxrecurse = 1; > + is(eval { Dumper([]) }, '$VAR1 = [];', "$pp: maxrecurse 1, []"); > + is(eval { Dumper([[]]) }, undef, "$pp: maxrecurse 1, [[]]"); > + ok($@, "exception thrown"); > + is(eval { Dumper({}) }, '$VAR1 = {};', "$pp: maxrecurse 1, {}"); > + is(eval { Dumper({ a => 1 }) }, q($VAR1 = {'a' => 1};), > + "$pp: maxrecurse 1, { a => 1 }"); > + is(eval { Dumper({ a => {} }) }, undef, "$pp: maxrecurse 1, { a => {} > }"); > + ok($@, "exception thrown"); > + is(eval { Dumper(\1) }, "\$VAR1 = \\1;", "$pp: maxrecurse 1, \\1"); > + is(eval { Dumper(\\1) }, undef, "$pp: maxrecurse 1, \\1"); > + ok($@, "exception thrown"); > + $Data::Dumper::Maxrecurse = 3; > + is(eval { Dumper(\1) }, "\$VAR1 = \\1;", "$pp: maxrecurse 3, \\1"); > + is(eval { Dumper(\(my $s = {})) }, "\$VAR1 = \\{};", "$pp: maxrecurse 3, > \\{}"); > + is(eval { Dumper(\(my $s = { a => [] })) }, "\$VAR1 = \\{'a' => []};", > + "$pp: maxrecurse 3, \\{ a => [] }"); > + is(eval { Dumper(\(my $s = { a => [{}] })) }, undef, > + "$pp: maxrecurse 3, \\{ a => [{}] }"); > + ok($@, "exception thrown"); > + $Data::Dumper::Maxrecurse = 0; > + is(eval { Dumper([[[[[]]]]]) }, q($VAR1 = [[[[[]]]]];), > + "$pp: check Maxrecurse doesn't set limit to 0 recursion"); > +}