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