c811f5
From d9c4b4ae5a1a17347ff5e3ecbf8e1d9da481f476 Mon Sep 17 00:00:00 2001
c811f5
From: David Mitchell <davem@iabyn.com>
c811f5
Date: Wed, 3 Apr 2019 13:23:24 +0100
c811f5
Subject: [PATCH] Data::Dumper - avoid leak on croak
c811f5
MIME-Version: 1.0
c811f5
Content-Type: text/plain; charset=UTF-8
c811f5
Content-Transfer-Encoding: 8bit
c811f5
c811f5
v5.21.3-742-g19be3be696 added a facility to Dumper.xs to croak if the
c811f5
recursion level became too deep (1000 by default).
c811f5
c811f5
The trouble with this is that various parts of DD_dump() allocate
c811f5
temporary SVs and buffers, which will leak if DD_dump() unceremoniously
c811f5
just croaks().
c811f5
c811f5
This currently manifests as dist/Data-Dumper/t/recurse.t failing under
c811f5
Address Sanitiser.
c811f5
c811f5
This commit makes the depth checking code just set a sticky 'too deep'
c811f5
boolean flag, and
c811f5
a) on entry, DD_dump() just returns immediately if the flag is set;
c811f5
b) the flag is checked by the top-level called of DD_dump() and croaks
c811f5
if set.
c811f5
c811f5
So the net effect is to defer croaking until the dump is complete,
c811f5
and avoid any further recursion once the flag is set.
c811f5
c811f5
This is a bit of a quick fix. More long-term solutions would be to
c811f5
convert DD_dump() to be iterative rather than recursive, and/or make
c811f5
sure all temporary SVs and buffers are suitably anchored somewhere so
c811f5
that they get cleaned up on croak.
c811f5
c811f5
Petr Písař: Ported from 6d65cb5d847ac93680949c4fa02111808207fbdc in
c811f5
perl git tree.
c811f5
c811f5
Signed-off-by: Petr Písař <ppisar@redhat.com>
c811f5
---
c811f5
 Dumper.pm |  6 +++---
c811f5
 Dumper.xs | 27 ++++++++++++++++++++-------
c811f5
 2 files changed, 23 insertions(+), 10 deletions(-)
c811f5
c811f5
diff --git a/Dumper.pm b/Dumper.pm
c811f5
index 40aeb7d..06af4c4 100644
c811f5
--- a/Dumper.pm
c811f5
+++ b/Dumper.pm
c811f5
@@ -10,7 +10,7 @@
c811f5
 package Data::Dumper;
c811f5
 
c811f5
 BEGIN {
c811f5
-    $VERSION = '2.173'; # Don't forget to set version and release
c811f5
+    $VERSION = '2.174'; # Don't forget to set version and release
c811f5
 }               # date in POD below!
c811f5
 
c811f5
 #$| = 1;
c811f5
@@ -1461,13 +1461,13 @@ be to use the C<Sortkeys> filter of Data::Dumper.
c811f5
 
c811f5
 Gurusamy Sarathy        gsar@activestate.com
c811f5
 
c811f5
-Copyright (c) 1996-2017 Gurusamy Sarathy. All rights reserved.
c811f5
+Copyright (c) 1996-2019 Gurusamy Sarathy. All rights reserved.
c811f5
 This program is free software; you can redistribute it and/or
c811f5
 modify it under the same terms as Perl itself.
c811f5
 
c811f5
 =head1 VERSION
c811f5
 
c811f5
-Version 2.173
c811f5
+Version 2.174
c811f5
 
c811f5
 =head1 SEE ALSO
c811f5
 
c811f5
diff --git a/Dumper.xs b/Dumper.xs
c811f5
index 7f0b027..a324cb6 100644
c811f5
--- a/Dumper.xs
c811f5
+++ b/Dumper.xs
c811f5
@@ -61,9 +61,10 @@
c811f5
 #endif
c811f5
 
c811f5
 /* This struct contains almost all the user's desired configuration, and it
c811f5
- * is treated as constant by the recursive function. This arrangement has
c811f5
- * the advantage of needing less memory than passing all of them on the
c811f5
- * stack all the time (as was the case in an earlier implementation). */
c811f5
+ * is treated as mostly constant (except for maxrecursed) by the recursive
c811f5
+ * function.  This arrangement has the advantage of needing less memory
c811f5
+ * than passing all of them on the stack all the time (as was the case in
c811f5
+ * an earlier implementation). */
c811f5
 typedef struct {
c811f5
     SV *pad;
c811f5
     SV *xpad;
c811f5
@@ -74,6 +75,7 @@ typedef struct {
c811f5
     SV *toaster;
c811f5
     SV *bless;
c811f5
     IV maxrecurse;
c811f5
+    bool maxrecursed; /* at some point we exceeded the maximum recursion level */
c811f5
     I32 indent;
c811f5
     I32 purity;
c811f5
     I32 deepcopy;
c811f5
@@ -97,7 +99,7 @@ static bool safe_decimal_number(const char *p, STRLEN len);
c811f5
 static SV *sv_x (pTHX_ SV *sv, const char *str, STRLEN len, I32 n);
c811f5
 static I32 DD_dump (pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval,
c811f5
                     HV *seenhv, AV *postav, const I32 level, SV *apad,
c811f5
-                    const Style *style);
c811f5
+                    Style *style);
c811f5
 
c811f5
 #ifndef HvNAME_get
c811f5
 #define HvNAME_get HvNAME
c811f5
@@ -615,7 +617,7 @@ deparsed_output(pTHX_ SV *val)
c811f5
  */
c811f5
 static I32
c811f5
 DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
c811f5
-	AV *postav, const I32 level, SV *apad, const Style *style)
c811f5
+	AV *postav, const I32 level, SV *apad, Style *style)
c811f5
 {
c811f5
     char tmpbuf[128];
c811f5
     Size_t i;
c811f5
@@ -642,6 +644,9 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
c811f5
     if (!val)
c811f5
 	return 0;
c811f5
 
c811f5
+    if (style->maxrecursed)
c811f5
+        return 0;
c811f5
+
c811f5
     /* If the output buffer has less than some arbitrary amount of space
c811f5
        remaining, then enlarge it. For the test case (25M of output),
c811f5
        *1.1 was slower, *2.0 was the same, so the first guess of 1.5 is
c811f5
@@ -793,7 +798,7 @@ DD_dump(pTHX_ SV *val, const char *name, STRLEN namelen, SV *retval, HV *seenhv,
c811f5
 	}
c811f5
 
c811f5
         if (style->maxrecurse > 0 && level >= style->maxrecurse) {
c811f5
-            croak("Recursion limit of %" IVdf " exceeded", style->maxrecurse);
c811f5
+            style->maxrecursed = TRUE;
c811f5
 	}
c811f5
 
c811f5
 	if (realpack && !no_bless) {				/* we have a blessed ref */
c811f5
@@ -1528,6 +1533,7 @@ Data_Dumper_Dumpxs(href, ...)
c811f5
             style.indent = 2;
c811f5
             style.quotekeys = 1;
c811f5
             style.maxrecurse = 1000;
c811f5
+            style.maxrecursed = FALSE;
c811f5
             style.purity = style.deepcopy = style.useqq = style.maxdepth
c811f5
                 = style.use_sparse_seen_hash = style.trailingcomma = 0;
c811f5
             style.pad = style.xpad = style.sep = style.pair = style.sortkeys
c811f5
@@ -1675,7 +1681,7 @@ Data_Dumper_Dumpxs(href, ...)
c811f5
 		    DD_dump(aTHX_ val, SvPVX_const(name), SvCUR(name), valstr, seenhv,
c811f5
                             postav, 0, newapad, &style);
c811f5
 		    SPAGAIN;
c811f5
-		
c811f5
+
c811f5
                     if (style.indent >= 2 && !terse)
c811f5
 			SvREFCNT_dec(newapad);
c811f5
 
c811f5
@@ -1715,6 +1721,13 @@ Data_Dumper_Dumpxs(href, ...)
c811f5
 		}
c811f5
 		SvREFCNT_dec(postav);
c811f5
 		SvREFCNT_dec(valstr);
c811f5
+
c811f5
+                /* we defer croaking until here so that temporary SVs and
c811f5
+                 * buffers won't be leaked */
c811f5
+                if (style.maxrecursed)
c811f5
+                    croak("Recursion limit of %" IVdf " exceeded",
c811f5
+                            style.maxrecurse);
c811f5
+		
c811f5
 	    }
c811f5
 	    else
c811f5
 		croak("Call to new() method failed to return HASH ref");
c811f5
-- 
c811f5
2.20.1
c811f5