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](dde99215f2/regexec.c (L1153)),
	so if `stk_base != stk_alloc` we can be sure we called
	[`stack_double`](dde99215f2/regexec.c (L1210))
	and it's safe to free. It's also safe to free if we've
	[saved](dde99215f2/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`](dde99215f2/regexec.c (L1221))
This commit is contained in:
Takashi Kokubun 2024-05-29 15:52:15 -07:00
Родитель 5c06e93074
Коммит cf643fabd5
3 изменённых файлов: 14 добавлений и 2 удалений

Просмотреть файл

@ -4218,7 +4218,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end,
timeout: timeout:
xfree(xmalloc_base); xfree(xmalloc_base);
xfree(stk_base); if (stk_base != stk_alloc || IS_NOT_NULL(msa->stack_p))
xfree(stk_base);
HANDLE_REG_TIMEOUT_IN_MATCH_AT; HANDLE_REG_TIMEOUT_IN_MATCH_AT;
} }

Просмотреть файл

@ -1827,6 +1827,17 @@ class TestRegexp < Test::Unit::TestCase
end; end;
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) def per_instance_redos_test(global_timeout, per_instance_timeout, expected_timeout)
assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}") assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
global_timeout = #{ EnvUtil.apply_timeout_scale(global_timeout).inspect } global_timeout = #{ EnvUtil.apply_timeout_scale(global_timeout).inspect }

Просмотреть файл

@ -11,7 +11,7 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 1 #define RUBY_VERSION_TEENY 1
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
#define RUBY_PATCHLEVEL 71 #define RUBY_PATCHLEVEL 72
#include "ruby/version.h" #include "ruby/version.h"
#include "ruby/internal/abi.h" #include "ruby/internal/abi.h"