diff options
author | Yves Orton <demerphq@gmail.com> | 2023-01-14 11:46:03 +0100 |
---|---|---|
committer | Yves Orton <demerphq@gmail.com> | 2023-01-15 17:20:48 +0100 |
commit | 37040543d024b3ecb0aecd78849bd5af61408d02 (patch) | |
tree | 971e23b6036477ccd688359e190b70f2858c6e31 /ext | |
parent | 3645ca4ee1a59fae1a6d6817c4582968ffd0a731 (diff) | |
download | perl-37040543d024b3ecb0aecd78849bd5af61408d02.tar.gz |
regexec.c - fix memory leak in EVAL.
EVAL was calling regcppush twice per invocation, once before executing the
callback, and once after. But not regcppop'ing twice. So each time we
would accumulate an extra "frame" of data. This is/was hidden somewhat by
the way we eventually "blow" the stack, so the extra data was just thrown
away at the end.
This removes the second set of pushes so that the save stack stays a stable
size as it unwinds from each failed eval.
We also weren't cleaning up after a (?{...}) when we failed to match to its
right. This unwinds the stack and restores the parens properly.
This adds tests to check how the save stack grows during patterns using
(?{ ... }) and (??{ ... }) and ensure that when we backtrack and re-execute
the EVAL again it cleans up the stack as it goes.
Diffstat (limited to 'ext')
-rw-r--r-- | ext/XS-APItest/APItest.xs | 9 | ||||
-rw-r--r-- | ext/XS-APItest/t/savestack.t | 37 |
2 files changed, 46 insertions, 0 deletions
diff --git a/ext/XS-APItest/APItest.xs b/ext/XS-APItest/APItest.xs index ff7667b24a..a7f1d5f011 100644 --- a/ext/XS-APItest/APItest.xs +++ b/ext/XS-APItest/APItest.xs @@ -7931,3 +7931,12 @@ newSvNV(const char * string) RETVAL = SvNV(newSVpv(string, 0)); OUTPUT: RETVAL + +MODULE = XS::APItest PACKAGE = XS::APItest::savestack + +IV +get_savestack_ix() + CODE: + RETVAL = PL_savestack_ix; + OUTPUT: + RETVAL diff --git a/ext/XS-APItest/t/savestack.t b/ext/XS-APItest/t/savestack.t new file mode 100644 index 0000000000..0e7d628e37 --- /dev/null +++ b/ext/XS-APItest/t/savestack.t @@ -0,0 +1,37 @@ +#!perl -w + +use strict; +use warnings; +use Test::More; + +use XS::APItest; + +my %ix; +sub showix { + diag join ", ", map { $ix{$_} > 1 ? "$_ x $ix{$_}" : $_ } sort { $a <=> $b } keys %ix; +} +my $len = 100; +my $str= "a" x $len; +my $pat= join "|", map { "a" x $_ } 1 .. $len; + +$str=~/^($pat)(??{ $ix{get_savestack_ix()}++; "(?!)" })/; +my $keys= 0+keys %ix; +cmp_ok($keys,">",0, "We expect at least one key in %ix for (??{ ... }) test"); +cmp_ok($keys,"<=", 2, "We expect no more than two keys in %ix if (??{ ... }) does not leak") + or showix(); + +%ix= (); +$str=~/^($pat)(?{ $ix{my $x=get_savestack_ix()}++; })(?!)/; +$keys= 0+keys %ix; +cmp_ok($keys,">",0, "We expect at least one key in %ix for (?{ ... }) test"); +cmp_ok($keys, "<=", 2, "We expect no more than two keys in %ix if (?{ ... }) does not leak") + or showix(); + +%ix= (); +$str=~/^($pat)(?(?{ $ix{my $x=get_savestack_ix()}++; })x|y)(?!)/; +$keys= 0+keys %ix; +cmp_ok($keys,">",0, "We expect at least one key in %ix for (?(?{ ... })yes|no) test"); +cmp_ok($keys, "<=", 2, "We expect no more than two keys in %ix if (?(?{ ... })yes|no) does not leak") + or showix(); + +done_testing(); |