diff options
author | David Mitchell <davem@iabyn.com> | 2022-11-15 19:31:14 +0000 |
---|---|---|
committer | Yves Orton <demerphq@gmail.com> | 2023-02-28 20:53:51 +0800 |
commit | ad5fedb002a4e7befcb1bd42bd788878f37a0d6e (patch) | |
tree | f5f4698d0773ca1556749001038e125249a86202 /t | |
parent | 7460e4ea20e79a71632e499d2af24ad90ee1c58e (diff) | |
download | perl-ad5fedb002a4e7befcb1bd42bd788878f37a0d6e.tar.gz |
for loops: protect GV/LVREF from premature free
In something like
for $package_var (....) { ... }
or more experimentally,
for \$lvref (....) { ... }
when entering the loop in pp_enteriter, perl would pop the GV/LVREF off
the stack, but didn't bump its refcount. Thus it was possible (if
unlikely) that it could be freed during the loop. In particular this
crashed:
$f = "foo";
for ${*$f} (1,2) {
delete $main::{$f}; # delete the glob *foo
...;
}
This will become more serious when the stack becomes refcounted, as
popping something off the stack will trigger a refcount decrement on it
and thus a possible immediate free of the GV.
This commit future-proofs for loops against this by ensuring that
the refcount of the SV referred to by cx->blk_loop.itervar_u.svp is
appropriately bumped up / down on entering / exiting the loop.
Diffstat (limited to 't')
-rw-r--r-- | t/op/for.t | 16 |
1 files changed, 16 insertions, 0 deletions
diff --git a/t/op/for.t b/t/op/for.t index a4d80664d8..b931a66a6c 100644 --- a/t/op/for.t +++ b/t/op/for.t @@ -756,4 +756,20 @@ is(fscope(), 1, 'return via loop in sub'); } } +# the GV of the loop variable didn't have its refcount bumped while being +# used by the loop, so it was possible to free it mid-loop. This used to +# assert/SEGV + +{ + my $f = "a_low_refcnt_package_var"; + my $i = 0; + no strict 'refs'; + for ${*$f} (5,11,33) { + delete $main::{$f}; + $i++; + } + is($i, 3, "deleting glob is safe"); +} + + done_testing(); |