diff options
author | Father Chrysostomos <sprout@cpan.org> | 2012-04-15 22:01:26 -0700 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2012-04-16 10:44:50 -0700 |
commit | 3607ca02d65ff63a9322effaf09e60361984e109 (patch) | |
tree | 0bff084aed0f65e993ed4204dd4f2f639862625e /t | |
parent | 8376a7bfebc88cd8955782fa2c6b6ce3a54a7cb0 (diff) | |
download | perl-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 't')
-rw-r--r-- | t/comp/hints.t | 34 |
1 files changed, 32 insertions, 2 deletions
diff --git a/t/comp/hints.t b/t/comp/hints.t index d22b15e9d1..a857755a4a 100644 --- a/t/comp/hints.t +++ b/t/comp/hints.t @@ -6,7 +6,7 @@ BEGIN { @INC = qw(. ../lib); } -BEGIN { print "1..30\n"; } +BEGIN { print "1..31\n"; } BEGIN { print "not " if exists $^H{foo}; print "ok 1 - \$^H{foo} doesn't exist initially\n"; @@ -238,6 +238,36 @@ print "ok 26 - no crash when cloning a tied hint hash\n"; } +# [perl #112444] +# A destructor called while %^H is freed should not be able to stop %^H +# from being magical (due to *^H{HASH} being undef). +{ + BEGIN { + # Make sure %^H is clear and not localised, to begin with + %^H = (); + $^H = 0; + } + DESTROY { %^H } + { + { + BEGIN { + $^H{foom} = bless[]; + } + } # scope exit triggers destructor, which autovivifies a non- + # magical %^H + BEGIN { + # Here we have the %^H created by DESTROY, which is + # not localised + $^H{112444} = 'baz'; + } + } # %^H leaks on scope exit + BEGIN { @keez = keys %^H } +} +print "not " if @keez; +print "ok 30 - %^H does not leak when autovivified in destructor\n"; +print "# keys are: @keez\n" if @keez; + + # Add new tests above this require, in case it fails. require './test.pl'; @@ -247,7 +277,7 @@ my $result = runperl( stderr => 1 ); print "not " if length $result; -print "ok 30 - double-freeing hints hash\n"; +print "ok 31 - double-freeing hints hash\n"; print "# got: $result\n" if length $result; __END__ |