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