diff options
author | David Mitchell <davem@iabyn.com> | 2010-07-12 20:53:04 +0100 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2010-07-14 23:06:18 +0100 |
commit | 803f274831f937654d48f8cf0468521cbf8f5dff (patch) | |
tree | 297f701cf0a8ef3af29be3017402207f1fa62707 /gv.c | |
parent | 96bafef935f82644670a19c8ca57886c240cd969 (diff) | |
download | perl-803f274831f937654d48f8cf0468521cbf8f5dff.tar.gz |
protect CvGV weakref with backref
Each CV usually has a pointer, CvGV(cv), back to the GV that corresponds
to the CV's name (or to *foo::__ANON__ for anon CVs). This pointer wasn't
reference counted, to avoid loops. This could leave it dangling if the GV
is deleted.
We fix this by:
For named subs, adding backref magic to the GV, so that when the GV is
freed, it can trigger processing the CV's CvGV field. This processing
consists of: if it looks like the freeing of the GV is about to trigger
freeing of the CV too, set it to NULL; otherwise make it point to
*foo::__ANON__ (and set CvAONON(cv)).
For anon subs, make CvGV a strong reference, i.e. increment the refcnt of
*foo::__ANON__. This doesn't cause a loop, since in this case the
__ANON__ glob doesn't point to the CV. This also avoids dangling pointers
if someone does an explicit 'delete $foo::{__ANON__}'.
Note that there was already some partial protection for CvGV with
commit f1c32fec87699aee2eeb638f44135f21217d2127. This worked by
anonymising any corresponding CV when freeing a stash or stash entry.
This had two drawbacks. First it didn't fix CVs that were anonmous or that
weren't currently pointed to by the GV (e.g. after local *foo), and
second, it caused *all* CVs to get anonymised during cleanup, even the
ones that would have been deleted shortly afterwards anyway. This commit
effectively removes that former commit, while reusing a bit of the
actual anonymising code.
Diffstat (limited to 'gv.c')
-rw-r--r-- | gv.c | 51 |
1 files changed, 49 insertions, 2 deletions
@@ -193,6 +193,43 @@ Perl_newGP(pTHX_ GV *const gv) return gp; } +/* Assign CvGV(cv) = gv, handling weak references. + * See also S_anonymise_cv_maybe */ + +void +Perl_cvgv_set(pTHX_ CV* cv, GV* gv) +{ + GV * const oldgv = CvGV(cv); + PERL_ARGS_ASSERT_CVGV_SET; + + if (oldgv == gv) + return; + + if (oldgv) { + if (CvANON(cv)) + SvREFCNT_dec(oldgv); + else { + assert(strNE(GvNAME(oldgv),"__ANON__")); + sv_del_backref(MUTABLE_SV(oldgv), MUTABLE_SV(cv)); + } + } + + CvGV(cv) = gv; + + if (!gv) + return; + + if (CvANON(cv)) { + assert(strnEQ(GvNAME(gv),"__ANON__", 8)); + SvREFCNT_inc_simple_void_NN(gv); + } + else { + assert(strNE(GvNAME(gv),"__ANON__")); + Perl_sv_add_backref(aTHX_ MUTABLE_SV(gv), MUTABLE_SV(cv)); + } +} + + void Perl_gv_init(pTHX_ GV *gv, HV *stash, const char *name, STRLEN len, int multi) { @@ -266,7 +303,7 @@ Perl_gv_init(pTHX_ GV *gv, HV *stash, const char *name, STRLEN len, int multi) LEAVE; mro_method_changed_in(GvSTASH(gv)); /* sub Foo::bar($) { (shift) } sub ASDF::baz($); *ASDF::baz = \&Foo::bar */ - CvGV(cv) = gv; + cvgv_set(cv, gv); CvFILE_set_from_cop(cv, PL_curcop); CvSTASH(cv) = PL_curstash; if (PL_curstash) @@ -2497,12 +2534,22 @@ Perl_gv_try_downgrade(pTHX_ GV *gv) SV **gvp; PERL_ARGS_ASSERT_GV_TRY_DOWNGRADE; if (!(SvREFCNT(gv) == 1 && SvTYPE(gv) == SVt_PVGV && !SvFAKE(gv) && - !SvOBJECT(gv) && !SvMAGICAL(gv) && !SvREADONLY(gv) && + !SvOBJECT(gv) && !SvREADONLY(gv) && isGV_with_GP(gv) && GvGP(gv) && !GvINTRO(gv) && GvREFCNT(gv) == 1 && !GvSV(gv) && !GvAV(gv) && !GvHV(gv) && !GvIOp(gv) && !GvFORM(gv) && GvEGVx(gv) == gv && (stash = GvSTASH(gv)))) return; + if (SvMAGICAL(gv)) { + MAGIC *mg; + /* only backref magic is allowed */ + if (SvGMAGICAL(gv) || SvSMAGICAL(gv)) + return; + for (mg = SvMAGIC(gv); mg; mg = mg->mg_moremagic) { + if (mg->mg_type != PERL_MAGIC_backref) + return; + } + } cv = GvCV(gv); if (!cv) { HEK *gvnhek = GvNAME_HEK(gv); |