fb6d68
From f5488561bdaab57380bf07e8e66778503a41aca3 Mon Sep 17 00:00:00 2001
fb6d68
From: Father Chrysostomos <sprout@cpan.org>
fb6d68
Date: Sun, 23 Sep 2012 12:42:15 -0700
fb6d68
Subject: [PATCH] =?UTF-8?q?Don=E2=80=99t=20leak=20if=20hh=20copying=20dies?=
fb6d68
MIME-Version: 1.0
fb6d68
Content-Type: text/plain; charset=UTF-8
fb6d68
Content-Transfer-Encoding: 8bit
fb6d68
fb6d68
When %^H is copied on entering a new scope, if it happens to have been
fb6d68
tied it can die.  This was resulting in leaks, because no protections
fb6d68
were added to handle that case.
fb6d68
fb6d68
The two things that were leaking were the new hash in hv_copy_hints_hv
fb6d68
and the new value (for an element) in newSVsv.
fb6d68
fb6d68
By fixing newSVsv itself, this also fixes any potential leaks when
fb6d68
other pieces of code call newSVsv on explosive values.
fb6d68
fb6d68
Petr Pisar: Ported to 5.16.3
fb6d68
---
fb6d68
 hv.c          |  6 ++++++
fb6d68
 sv.c          |  7 ++++---
fb6d68
 t/op/svleak.t | 22 +++++++++++++++++++++-
fb6d68
 3 files changed, 31 insertions(+), 4 deletions(-)
fb6d68
fb6d68
diff --git a/hv.c b/hv.c
fb6d68
index 3c35341..29d6352 100644
fb6d68
--- a/hv.c
fb6d68
+++ b/hv.c
fb6d68
@@ -1440,6 +1440,9 @@ Perl_hv_copy_hints_hv(pTHX_ HV *const ohv)
fb6d68
 	const I32 riter = HvRITER_get(ohv);
fb6d68
 	HE * const eiter = HvEITER_get(ohv);
fb6d68
 
fb6d68
+	ENTER;
fb6d68
+	SAVEFREESV(hv);
fb6d68
+
fb6d68
 	while (hv_max && hv_max + 1 >= hv_fill * 2)
fb6d68
 	    hv_max = hv_max / 2;
fb6d68
 	HvMAX(hv) = hv_max;
fb6d68
@@ -1461,6 +1464,9 @@ Perl_hv_copy_hints_hv(pTHX_ HV *const ohv)
fb6d68
 	}
fb6d68
 	HvRITER_set(ohv, riter);
fb6d68
 	HvEITER_set(ohv, eiter);
fb6d68
+
fb6d68
+	SvREFCNT_inc_simple_void_NN(hv);
fb6d68
+	LEAVE;
fb6d68
     }
fb6d68
     hv_magic(hv, NULL, PERL_MAGIC_hints);
fb6d68
     return hv;
fb6d68
diff --git a/sv.c b/sv.c
fb6d68
index a43feac..597d71b 100644
fb6d68
--- a/sv.c
fb6d68
+++ b/sv.c
fb6d68
@@ -8764,11 +8764,12 @@ Perl_newSVsv(pTHX_ register SV *const old)
fb6d68
 	Perl_ck_warner_d(aTHX_ packWARN(WARN_INTERNAL), "semi-panic: attempt to dup freed string");
fb6d68
 	return NULL;
fb6d68
     }
fb6d68
+    /* Do this here, otherwise we leak the new SV if this croaks. */
fb6d68
+    SvGETMAGIC(old);
fb6d68
     new_SV(sv);
fb6d68
-    /* SV_GMAGIC is the default for sv_setv()
fb6d68
-       SV_NOSTEAL prevents TEMP buffers being, well, stolen, and saves games
fb6d68
+    /* SV_NOSTEAL prevents TEMP buffers being, well, stolen, and saves games
fb6d68
        with SvTEMP_off and SvTEMP_on round a call to sv_setsv.  */
fb6d68
-    sv_setsv_flags(sv, old, SV_GMAGIC | SV_NOSTEAL);
fb6d68
+    sv_setsv_flags(sv, old, SV_NOSTEAL);
fb6d68
     return sv;
fb6d68
 }
fb6d68
 
fb6d68
diff --git a/t/op/svleak.t b/t/op/svleak.t
fb6d68
index 2f09af3..011c184 100644
fb6d68
--- a/t/op/svleak.t
fb6d68
+++ b/t/op/svleak.t
fb6d68
@@ -13,7 +13,7 @@ BEGIN {
fb6d68
 	or skip_all("XS::APItest not available");
fb6d68
 }
fb6d68
 
fb6d68
-plan tests => 23;
fb6d68
+plan tests => 24;
fb6d68
 
fb6d68
 # run some code N times. If the number of SVs at the end of loop N is
fb6d68
 # greater than (N-1)*delta at the end of loop 1, we've got a leak
fb6d68
@@ -176,3 +176,23 @@ leak(2, 0, sub {
fb6d68
     each %$h;
fb6d68
     undef $h;
fb6d68
 }, 'tied hash iteration does not leak');
fb6d68
+
fb6d68
+# [perl #107000]
fb6d68
+package hhtie {
fb6d68
+    sub TIEHASH { bless [] }
fb6d68
+    sub STORE    { $_[0][0]{$_[1]} = $_[2] }
fb6d68
+    sub FETCH    { die if $explosive; $_[0][0]{$_[1]} }
fb6d68
+    sub FIRSTKEY { keys %{$_[0][0]}; each %{$_[0][0]} }
fb6d68
+    sub NEXTKEY  { each %{$_[0][0]} }
fb6d68
+}
fb6d68
+leak(2,!!$Config{mad}, sub {
fb6d68
+    eval q`
fb6d68
+    	BEGIN {
fb6d68
+	    $hhtie::explosive = 0;
fb6d68
+	    tie %^H, hhtie;
fb6d68
+	    $^H{foo} = bar;
fb6d68
+	    $hhtie::explosive = 1;
fb6d68
+    	}
fb6d68
+	{ 1; }
fb6d68
+    `;
fb6d68
+}, 'hint-hash copying does not leak');
fb6d68
-- 
fb6d68
1.8.1.4
fb6d68