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