diff options
author | David Mitchell <davem@iabyn.com> | 2015-12-11 12:06:39 +0000 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2015-12-11 12:14:57 +0000 |
commit | 2baebb988343167d06d3d536e2de40b4427dc37e (patch) | |
tree | 8085a6070450a904261fd858ad443d206081a722 /scope.c | |
parent | facc1dc2646135cef09100080f18ecafbf1df900 (diff) | |
download | perl-2baebb988343167d06d3d536e2de40b4427dc37e.tar.gz |
avoid leaks when calling mg_set() in leave_scope()
In leave_scope() in places like SAVEt_SV, it does stuff like
if (SvSMAGICAL(...))
mg_set(...)
SvREFCNT_dec_NN(ARG0_SV)
If mg_set() dies (e.g. it calls STORE() and STORE() dies), then ARG0_SV
would leak. Fix this by putting ARG0_SV back in the save stack in this
case.
A similar thing applies to SAVEt_AV and SAVEt_HV, but I couldn't
think of a simple test for those, as tied array and hashes don't have
set magic (just RMG).
Also, SAVEt_AV and SAVEt_HV share a lot of common code, so I made
SAVEt_HV goto into the SAVEt_AV code block for the common part.
Diffstat (limited to 'scope.c')
-rw-r--r-- | scope.c | 25 |
1 files changed, 18 insertions, 7 deletions
@@ -841,9 +841,18 @@ Perl_leave_scope(pTHX_ I32 base) *svp = ARG0_SV; SvREFCNT_dec(sv); if (UNLIKELY(SvSMAGICAL(ARG0_SV))) { + /* mg_set could die, skipping the freeing of ARG0_SV and + * refsv; Ensure that they're always freed in that case */ + dSS_ADD; + SS_ADD_PTR(ARG0_SV); + SS_ADD_UV(SAVEt_FREESV); + SS_ADD_PTR(refsv); + SS_ADD_UV(SAVEt_FREESV); + SS_ADD_END(4); PL_localizing = 2; mg_set(ARG0_SV); PL_localizing = 0; + break; } SvREFCNT_dec_NN(ARG0_SV); SvREFCNT_dec(refsv); @@ -898,23 +907,25 @@ Perl_leave_scope(pTHX_ I32 base) case SAVEt_AV: /* array reference */ SvREFCNT_dec(GvAV(ARG1_GV)); GvAV(ARG1_GV) = ARG0_AV; + avhv_common: if (UNLIKELY(SvSMAGICAL(ARG0_SV))) { + /* mg_set might die, so make sure ARG1 isn't leaked */ + dSS_ADD; + SS_ADD_PTR(ARG1_SV); + SS_ADD_UV(SAVEt_FREESV); + SS_ADD_END(2); PL_localizing = 2; mg_set(ARG0_SV); PL_localizing = 0; + break; } SvREFCNT_dec_NN(ARG1_GV); break; case SAVEt_HV: /* hash reference */ SvREFCNT_dec(GvHV(ARG1_GV)); GvHV(ARG1_GV) = ARG0_HV; - if (UNLIKELY(SvSMAGICAL(ARG0_SV))) { - PL_localizing = 2; - mg_set(ARG0_SV); - PL_localizing = 0; - } - SvREFCNT_dec_NN(ARG1_GV); - break; + goto avhv_common; + case SAVEt_INT_SMALL: *(int*)ARG0_PTR = (int)(uv >> SAVE_TIGHT_SHIFT); break; |