summaryrefslogtreecommitdiff
path: root/scope.c
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2019-03-26 11:04:07 +0000
committerDavid Mitchell <davem@iabyn.com>2019-03-26 11:04:07 +0000
commit85df897fcfe76250deecfdeb239ba1e4279d8532 (patch)
tree1fe9fab2b7e111409d71b3f052c36b6c87975302 /scope.c
parent657ed7c1c190e7fad1bac2979944d07245bbeea4 (diff)
downloadperl-85df897fcfe76250deecfdeb239ba1e4279d8532.tar.gz
avoid leak with local $h{foo}, $a[n]
When SAVEt_DELETE / SAVEt_ADELETE deletes a hash/array entry on scope exit, they also decrement the refcount of the hash/array, and for the hash, also free the saved key. However, if the call to hv_delete() or av_delete() dies (e.g. when calling a tied DELETE method) then the hash/array and key will leak because leave_scope() calls av/hv_delete(), *then* does the SvREFCNT_dec() etc. The fix is to push new FREEPV/FREESV actions just before calling av/hv_delete().
Diffstat (limited to 'scope.c')
-rw-r--r--scope.c17
1 files changed, 14 insertions, 3 deletions
diff --git a/scope.c b/scope.c
index 83a7b76893..3e4ee4344b 100644
--- a/scope.c
+++ b/scope.c
@@ -1253,15 +1253,26 @@ Perl_leave_scope(pTHX_ I32 base)
case SAVEt_DELETE:
a0 = ap[0]; a1 = ap[1]; a2 = ap[2];
+ /* hv_delete could die, so free the key and SvREFCNT_dec the
+ * hv by pushing new save actions
+ */
+ /* ap[0] is the key */
+ ap[1].any_uv = SAVEt_FREEPV; /* was len */
+ /* ap[2] is the hv */
+ ap[3].any_uv = SAVEt_FREESV; /* was SAVEt_DELETE */
+ PL_savestack_ix += 4;
(void)hv_delete(a2.any_hv, a0.any_pv, a1.any_i32, G_DISCARD);
- SvREFCNT_dec(a2.any_hv);
- Safefree(a0.any_ptr);
break;
case SAVEt_ADELETE:
a0 = ap[0]; a1 = ap[1];
+ /* av_delete could die, so SvREFCNT_dec the av by pushing a
+ * new save action
+ */
+ ap[0].any_av = a1.any_av;
+ ap[1].any_uv = SAVEt_FREESV;
+ PL_savestack_ix += 2;
(void)av_delete(a1.any_av, a0.any_iv, G_DISCARD);
- SvREFCNT_dec(a1.any_av);
break;
case SAVEt_DESTRUCTOR_X: