summaryrefslogtreecommitdiff
path: root/scope.c
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2012-04-15 22:01:26 -0700
committerFather Chrysostomos <sprout@cpan.org>2012-04-16 10:44:50 -0700
commit3607ca02d65ff63a9322effaf09e60361984e109 (patch)
tree0bff084aed0f65e993ed4204dd4f2f639862625e /scope.c
parent8376a7bfebc88cd8955782fa2c6b6ce3a54a7cb0 (diff)
downloadperl-3607ca02d65ff63a9322effaf09e60361984e109.tar.gz
[perl #112444] Don’t leak %^H autovivified by destructor
More precisely, don’t let a hint hash autovivified by a destructor during scope exit leak to outer scopes. GvHV(PL_hintgv) (aka *^H{HASH}) must be set to null before the hash in it is freed on scope exit. Otherwise destructors will see %^H with a refcount of zero, and might try to iterate over a hash that is in the process of being freed. Bad things then happen. Commit 2653c1e3b1 took care of this. Now, if GvHV(PL_hintgv) is null when destructors are called, those destructors might end up autovivifying it. The code in scope.c that handles hints when a scope is left (SAVEt_HINTS in Perl_leave_scope) would then end up leaving that new autovivified %^H in place when the scope exited, if the outer scope did not have HINT_LOCALIZE_HH set (meaning %^H was unused). That in itself would not be so much of a problem, if it were not for the fact that %^H is magicalised by the scope-handling code, not when it is autovivified (see also bug #112458). Hence, subsequent changes to %^H would not magically set the HINT_LOCALIZE_HH hint bit, which bit is checked all over the place to see whether %^H is in use. This, in turn, would cause hints subsequently added to %^H to leak to outer scopes. This commit fixes that by repeatedly freeing GvHV(PL_hintgv). If a destructor autovivifies it again, it just causes another iteration of the while loop. This does mean a destructor could autovivify %^H and cause the new %^H itself to trigger a destructor, resulting in infi- nite loops. But then that is that own code’s fault. This originally came up because commit 2653c1e3b1 also caused des- tructors that try to add new free magic to %^H to add it to a new autovivified %^H instead of the existing %^H that was being freed. This caused the nextgen module to fail its tests, because it uses B::Hooks::EndOfScope to register a sub to be called on scope exit, and it does this from a destructor itself called during scope exit. If the autovivified %^H leaks to an outer scope, the second destructor is not called.
Diffstat (limited to 'scope.c')
-rw-r--r--scope.c4
1 files changed, 3 insertions, 1 deletions
diff --git a/scope.c b/scope.c
index 1bf79e0227..ffd0552ac3 100644
--- a/scope.c
+++ b/scope.c
@@ -1023,10 +1023,12 @@ Perl_leave_scope(pTHX_ I32 base)
PL_op = (OP*)SSPOPPTR;
break;
case SAVEt_HINTS:
- if ((PL_hints & HINT_LOCALIZE_HH) && GvHV(PL_hintgv)) {
+ if ((PL_hints & HINT_LOCALIZE_HH)) {
+ while (GvHV(PL_hintgv)) {
HV *hv = GvHV(PL_hintgv);
GvHV(PL_hintgv) = NULL;
SvREFCNT_dec(MUTABLE_SV(hv));
+ }
}
cophh_free(CopHINTHASH_get(&PL_compiling));
CopHINTHASH_set(&PL_compiling, (COPHH*)SSPOPPTR);