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");
> +}

Reply via email to