Skip to content

Commit 2653c1e

Browse files
committed
stop %^H pointing to being-freed hash; #112326
The leave_scope() action SAVEt_HINTS does the following to GvHV(PL_hintgv): first it SvREFCNT_dec()'s it, then sets it to NULL. If the current %^H contains a destructor, then that will be executed while %^H still points to the hash being freed. This can cause bad things to happen, like iterating over the hash being freed. Instead, setGvHV(PL_hintgv) to NULL first, *then* free the hash.
1 parent 629e8f5 commit 2653c1e

File tree

2 files changed

+27
-5
lines changed

2 files changed

+27
-5
lines changed

scope.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,17 +1024,18 @@ Perl_leave_scope(pTHX_ I32 base)
10241024
break;
10251025
case SAVEt_HINTS:
10261026
if ((PL_hints & HINT_LOCALIZE_HH) && GvHV(PL_hintgv)) {
1027-
SvREFCNT_dec(MUTABLE_SV(GvHV(PL_hintgv)));
1027+
HV *hv = GvHV(PL_hintgv);
10281028
GvHV(PL_hintgv) = NULL;
1029+
SvREFCNT_dec(MUTABLE_SV(hv));
10291030
}
10301031
cophh_free(CopHINTHASH_get(&PL_compiling));
10311032
CopHINTHASH_set(&PL_compiling, (COPHH*)SSPOPPTR);
10321033
*(I32*)&PL_hints = (I32)SSPOPINT;
10331034
if (PL_hints & HINT_LOCALIZE_HH) {
10341035
SvREFCNT_dec(MUTABLE_SV(GvHV(PL_hintgv)));
10351036
GvHV(PL_hintgv) = MUTABLE_HV(SSPOPPTR);
1036-
assert(GvHV(PL_hintgv));
1037-
} else if (!GvHV(PL_hintgv)) {
1037+
}
1038+
if (!GvHV(PL_hintgv)) {
10381039
/* Need to add a new one manually, else gv_fetchpv() can
10391040
add one in this code:
10401041

t/comp/hints.t

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ BEGIN {
66
@INC = qw(. ../lib);
77
}
88

9-
BEGIN { print "1..29\n"; }
9+
BEGIN { print "1..30\n"; }
1010
BEGIN {
1111
print "not " if exists $^H{foo};
1212
print "ok 1 - \$^H{foo} doesn't exist initially\n";
@@ -216,6 +216,27 @@ print "ok 26 - no crash when cloning a tied hint hash\n";
216216
"setting \${^WARNING_BITS} to its own value has no effect\n";
217217
}
218218

219+
# [perl #112326]
220+
# this code could cause a crash, due to PL_hints continuing to point to th
221+
# hints hash currently being freed
222+
223+
{
224+
package Foo;
225+
my @h = qw(a 1 b 2);
226+
BEGIN {
227+
$^H{FOO} = bless {};
228+
}
229+
sub DESTROY {
230+
@h = %^H;
231+
delete $INC{strict}; require strict; # boom!
232+
}
233+
my $h = join ':', %h;
234+
# this isn't the main point of the test; the main point is that
235+
# it doesn't crash!
236+
print "not " if $h ne '';
237+
print "ok 29 - #112326\n";
238+
}
239+
219240

220241
# Add new tests above this require, in case it fails.
221242
require './test.pl';
@@ -226,7 +247,7 @@ my $result = runperl(
226247
stderr => 1
227248
);
228249
print "not " if length $result;
229-
print "ok 29 - double-freeing hints hash\n";
250+
print "ok 30 - double-freeing hints hash\n";
230251
print "# got: $result\n" if length $result;
231252

232253
__END__

0 commit comments

Comments
 (0)