diff options
author | Takashi Kokubun <[email protected]> | 2024-05-29 15:52:15 -0700 |
---|---|---|
committer | Takashi Kokubun <[email protected]> | 2024-05-29 15:52:15 -0700 |
commit | cf643fabd5c564c1dfeb337b50b4aa76ebaa11c1 (patch) | |
tree | 1b5355d68f908f202ba3de362e84415cbe8891b6 /test | |
parent | 5c06e930748ef6bdb4ac4751ba16b7b604da3db0 (diff) |
merge revision(s) d292a9b98ce03c76dbe13138d20b9fbf613cc02d: [Backport #20453]
[Bug #20453] segfault in Regexp timeout
https://bugs.ruby-lang.org/issues/20228 started freeing `stk_base` to
avoid a memory leak. But `stk_base` is sometimes stack allocated (using
`xalloca`), so the free only works if the regex stack has grown enough
to hit `stack_double` (which uses `xmalloc` and `xrealloc`).
To reproduce the problem on master and 3.3.1:
```ruby
Regexp.timeout = 0.001
/^(a*)x$/ =~ "a" * 1000000 + "x"'
```
Some details about this potential fix:
`stk_base == stk_alloc` on
[init](https://github.com/ruby/ruby/blob/dde99215f2bc60c22a00fc941ff7f714f011e920/regexec.c#L1153),
so if `stk_base != stk_alloc` we can be sure we called
[`stack_double`](https://github.com/ruby/ruby/blob/dde99215f2bc60c22a00fc941ff7f714f011e920/regexec.c#L1210)
and it's safe to free. It's also safe to free if we've
[saved](https://github.com/ruby/ruby/blob/dde99215f2bc60c22a00fc941ff7f714f011e920/regexec.c#L1187-L1189)
the stack to `msa->stack_p`, since we do the `stk_base != stk_alloc`
check before saving.
This matches the check we do inside
[`stack_double`](https://github.com/ruby/ruby/blob/dde99215f2bc60c22a00fc941ff7f714f011e920/regexec.c#L1221)
Diffstat (limited to 'test')
-rw-r--r-- | test/ruby/test_regexp.rb | 11 |
1 files changed, 11 insertions, 0 deletions
diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb index ec9bcc34d8..c8caca2891 100644 --- a/test/ruby/test_regexp.rb +++ b/test/ruby/test_regexp.rb @@ -1827,6 +1827,17 @@ class TestRegexp < Test::Unit::TestCase end; end + def test_bug_20453 + assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}") + begin; + Regexp.timeout = 0.001 + + assert_raise(Regexp::TimeoutError) do + /^(a*)x$/ =~ "a" * 1000000 + "x" + end + end; + end + def per_instance_redos_test(global_timeout, per_instance_timeout, expected_timeout) assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}") global_timeout = #{ EnvUtil.apply_timeout_scale(global_timeout).inspect } |