diff options
author | Father Chrysostomos <sprout@cpan.org> | 2012-11-21 08:48:41 -0800 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2012-11-21 08:48:41 -0800 |
commit | f6f93f805953c20dfcbe8940e84850dfe58ce0ef (patch) | |
tree | 520dc5c981992931c90d9f5bfa66d578536f81cb /sv.c | |
parent | 4cc783efe0ed9495ebcbefd8494083a4b6160e6f (diff) | |
download | perl-f6f93f805953c20dfcbe8940e84850dfe58ce0ef.tar.gz |
Fix double free with stashes blessed into each other
When I added that extra pass in 5.16 that curses any remaining blessed
objects during global destruction, I caused this bug:
$ ./miniperl -e 'warn bless \%foo::, bar::; warn bless \%bar::, foo::'
bar=HASH(0x827260) at -e line 1.
foo=HASH(0x8272b0) at -e line 1.
Attempt to free unreferenced scalar: SV 0x8272b0, Perl interpreter: 0x800000 during global destruction.
By creating a circularity between stashes, with no RVs remaining, we
cause one of the two stashes, say foo, to be cursed during global
destruction. That causes it to lower the remaining reference count
on bar, which, when freed, lowers its reference count on foo, which
then tries to lower its reference count on bar, which has already
been freed.
The solution here is to turn off the object flag before decrementing
the stash’s reference count. So a recursive call won’t try to curse
the already accursed object.
Turning off the flag makes SvSTASH into a DESTROY cache. That won’t
work if the SvREFCNT_dec call tries to access that cache. So we have
to null the field before calling SvREFCNT_dec (which we should be
doing anyway).
Diffstat (limited to 'sv.c')
-rw-r--r-- | sv.c | 6 |
1 files changed, 5 insertions, 1 deletions
@@ -6399,8 +6399,12 @@ S_curse(pTHX_ SV * const sv, const bool check_refcnt) { } if (SvOBJECT(sv)) { - SvREFCNT_dec(SvSTASH(sv)); /* possibly of changed persuasion */ + HV * const stash = SvSTASH(sv); + /* Curse before freeing the stash, as freeing the stash could cause + a recursive call into S_curse. */ SvOBJECT_off(sv); /* Curse the object. */ + SvSTASH_set(sv,0); /* SvREFCNT_dec may try to read this */ + SvREFCNT_dec(stash); /* possibly of changed persuasion */ if (SvTYPE(sv) != SVt_PVIO) --PL_sv_objcount;/* XXX Might want something more general */ } |