diff options
| author | Andrii Furmanets <furmanets.andriy@gmail.com> | 2026-01-16 12:52:16 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-16 19:52:16 +0900 |
| commit | f872901bb263e01768ac64e6d063377c3b633e27 (patch) | |
| tree | 15d937ce883b9a1c707a241c261842e318a63077 | |
| parent | 16cd9daa39c5488a7ee3f60ebc6f5b3cf1ee50e4 (diff) | |
MatchData: Avoid large stack allocations in MatchData (GH-15872)
| -rw-r--r-- | re.c | 9 | ||||
| -rw-r--r-- | test/ruby/test_regexp.rb | 24 |
2 files changed, 31 insertions, 2 deletions
@@ -1014,6 +1014,7 @@ update_char_offset(VALUE match) char *s, *p, *q; rb_encoding *enc; pair_t *pairs; + VALUE pairs_obj = Qnil; if (rm->char_offset_num_allocated) return; @@ -1035,7 +1036,7 @@ update_char_offset(VALUE match) return; } - pairs = ALLOCA_N(pair_t, num_regs*2); + pairs = RB_ALLOCV_N(pair_t, pairs_obj, num_regs * 2); num_pos = 0; for (i = 0; i < num_regs; i++) { if (BEG(i) < 0) @@ -1070,6 +1071,8 @@ update_char_offset(VALUE match) found = bsearch(&key, pairs, num_pos, sizeof(pair_t), pair_byte_cmp); rm->char_offset[i].end = found->char_pos; } + + RB_ALLOCV_END(pairs_obj); } static VALUE @@ -2614,6 +2617,7 @@ match_inspect(VALUE match) struct re_registers *regs = RMATCH_REGS(match); int num_regs = regs->num_regs; struct backref_name_tag *names; + VALUE names_obj = Qnil; VALUE regexp = RMATCH(match)->regexp; if (regexp == 0) { @@ -2624,7 +2628,7 @@ match_inspect(VALUE match) cname, rb_reg_nth_match(0, match)); } - names = ALLOCA_N(struct backref_name_tag, num_regs); + names = RB_ALLOCV_N(struct backref_name_tag, names_obj, num_regs); MEMZERO(names, struct backref_name_tag, num_regs); onig_foreach_name(RREGEXP_PTR(regexp), @@ -2652,6 +2656,7 @@ match_inspect(VALUE match) } rb_str_buf_cat2(str, ">"); + RB_ALLOCV_END(names_obj); return str; } diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb index 2d7a67dd54..9feababa53 100644 --- a/test/ruby/test_regexp.rb +++ b/test/ruby/test_regexp.rb @@ -1669,6 +1669,30 @@ class TestRegexp < Test::Unit::TestCase assert_equal("hoge fuga", h["body"]) end + def test_matchdata_large_capture_groups_stack + env = {"RUBY_THREAD_MACHINE_STACK_SIZE" => (256 * 1024).to_s} + assert_separately([env], <<~'RUBY') + n = 20000 + require "rbconfig/sizeof" + stack = RubyVM::DEFAULT_PARAMS[:thread_machine_stack_size] + size = RbConfig::SIZEOF["long"] + required = (n + 1) * 4 * size + if !stack || stack == 0 || stack >= required + omit "thread machine stack size not reduced (#{stack}:#{required})" + end + + inspect = Thread.new do + str = "\u{3042}" * n + m = Regexp.new("(.)" * n).match(str) + assert_not_nil(m) + assert_equal([n - 1, n], m.offset(n)) + m.inspect + end.value + + assert_include(inspect, "MatchData") + RUBY + end + def test_regexp_popped EnvUtil.suppress_warning do assert_nothing_raised { eval("a = 1; /\#{ a }/; a") } |
