|
|
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 |
|