diff options
author | Father Chrysostomos <sprout@cpan.org> | 2012-11-17 22:49:57 -0800 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2012-11-18 14:02:35 -0800 |
commit | 1cdc9186cabf19ce52d3cfa09e46c4f4a28e59e1 (patch) | |
tree | ab2b0aac7939b0a946cc519be5200726a3870195 | |
parent | c9ac52160c0ba46c0a6482d85efb06a330ad4541 (diff) | |
download | perl-1cdc9186cabf19ce52d3cfa09e46c4f4a28e59e1.tar.gz |
Stop local $_[0] from leaking
local $_[0] puts the current $_[0] on to the savestack and gives the
array a brand new SV. If the array is not marked REAL, it holds no
reference counts on its elements. @_ is surreal by default.
The localisation code was making @_ hold a reference count on its new
element. The restore code was assuming it had a reference count, so
everything worked out if $_[0] was not modified after localisation.
But if the array is surreal, then modifications to it will assume that
it does *not* hold a reference count on $_[0]. So doing shift, or
@_=undef would cause the new element to leak.
Also, taking a reference to the array (\@_) will trigger, making
the reference count of all elements increeas, likwies leaking the
new element.
Since there is only one REAL flag, which indicates that all elements
of the array are reference-counted, we cannot have some elements ref-
erence-counted and some not (which local $_[0] does), and have every-
thing behave correctly.
So the only solution is to reify arrays before localising
their elements.
-rw-r--r-- | scope.c | 5 | ||||
-rw-r--r-- | t/op/svleak.t | 8 |
2 files changed, 10 insertions, 3 deletions
@@ -627,9 +627,10 @@ Perl_save_aelem_flags(pTHX_ AV *av, I32 idx, SV **sptr, const U32 flags) SvGETMAGIC(*sptr); save_pushptri32ptr(SvREFCNT_inc_simple(av), idx, SvREFCNT_inc(*sptr), SAVEt_AELEM); - /* if it gets reified later, the restore will have the wrong refcnt */ + /* The array needs to hold a reference count on its new element, so it + must be AvREAL. */ if (!AvREAL(av) && AvREIFY(av)) - SvREFCNT_inc_void(*sptr); + av_reify(av); save_scalar_at(sptr, flags); /* XXX - FIXME - see #60360 */ if (flags & SAVEf_KEEPOLDELEM) return; diff --git a/t/op/svleak.t b/t/op/svleak.t index d38c92d11f..7b530925fa 100644 --- a/t/op/svleak.t +++ b/t/op/svleak.t @@ -15,7 +15,7 @@ BEGIN { use Config; -plan tests => 69; +plan tests => 71; # run some code N times. If the number of SVs at the end of loop N is # greater than (N-1)*delta at the end of loop 1, we've got a leak @@ -197,6 +197,12 @@ eleak(2, 0, 'no warnings; sub {1 1}', 'anon sub with syntax error'); eleak(2, 0, 'no warnings; use feature ":all"; my sub a{1 1}', 'my sub with syntax error'); +# Reification (or lack thereof) +leak(2, 0, sub { sub { local $_[0]; shift }->(1) }, + 'local $_[0] on surreal @_, followed by shift'); +leak(2, 0, sub { sub { local $_[0]; \@_ }->(1) }, + 'local $_[0] on surreal @_, followed by reification'); + # Syntax errors eleak(2, 0, '"${<<END}" ', 'unterminated here-doc in quotes in multiline eval'); |