Skip to content

Commit f872901

Browse files
authored
MatchData: Avoid large stack allocations in MatchData (rubyGH-15872)
1 parent 16cd9da commit f872901

File tree

2 files changed

+31
-2
lines changed

2 files changed

+31
-2
lines changed

re.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,7 @@ update_char_offset(VALUE match)
10141014
char *s, *p, *q;
10151015
rb_encoding *enc;
10161016
pair_t *pairs;
1017+
VALUE pairs_obj = Qnil;
10171018

10181019
if (rm->char_offset_num_allocated)
10191020
return;
@@ -1035,7 +1036,7 @@ update_char_offset(VALUE match)
10351036
return;
10361037
}
10371038

1038-
pairs = ALLOCA_N(pair_t, num_regs*2);
1039+
pairs = RB_ALLOCV_N(pair_t, pairs_obj, num_regs * 2);
10391040
num_pos = 0;
10401041
for (i = 0; i < num_regs; i++) {
10411042
if (BEG(i) < 0)
@@ -1070,6 +1071,8 @@ update_char_offset(VALUE match)
10701071
found = bsearch(&key, pairs, num_pos, sizeof(pair_t), pair_byte_cmp);
10711072
rm->char_offset[i].end = found->char_pos;
10721073
}
1074+
1075+
RB_ALLOCV_END(pairs_obj);
10731076
}
10741077

10751078
static VALUE
@@ -2614,6 +2617,7 @@ match_inspect(VALUE match)
26142617
struct re_registers *regs = RMATCH_REGS(match);
26152618
int num_regs = regs->num_regs;
26162619
struct backref_name_tag *names;
2620+
VALUE names_obj = Qnil;
26172621
VALUE regexp = RMATCH(match)->regexp;
26182622

26192623
if (regexp == 0) {
@@ -2624,7 +2628,7 @@ match_inspect(VALUE match)
26242628
cname, rb_reg_nth_match(0, match));
26252629
}
26262630

2627-
names = ALLOCA_N(struct backref_name_tag, num_regs);
2631+
names = RB_ALLOCV_N(struct backref_name_tag, names_obj, num_regs);
26282632
MEMZERO(names, struct backref_name_tag, num_regs);
26292633

26302634
onig_foreach_name(RREGEXP_PTR(regexp),
@@ -2652,6 +2656,7 @@ match_inspect(VALUE match)
26522656
}
26532657
rb_str_buf_cat2(str, ">");
26542658

2659+
RB_ALLOCV_END(names_obj);
26552660
return str;
26562661
}
26572662

test/ruby/test_regexp.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,6 +1669,30 @@ def test_matchdata
16691669
assert_equal("hoge fuga", h["body"])
16701670
end
16711671

1672+
def test_matchdata_large_capture_groups_stack
1673+
env = {"RUBY_THREAD_MACHINE_STACK_SIZE" => (256 * 1024).to_s}
1674+
assert_separately([env], <<~'RUBY')
1675+
n = 20000
1676+
require "rbconfig/sizeof"
1677+
stack = RubyVM::DEFAULT_PARAMS[:thread_machine_stack_size]
1678+
size = RbConfig::SIZEOF["long"]
1679+
required = (n + 1) * 4 * size
1680+
if !stack || stack == 0 || stack >= required
1681+
omit "thread machine stack size not reduced (#{stack}:#{required})"
1682+
end
1683+
1684+
inspect = Thread.new do
1685+
str = "\u{3042}" * n
1686+
m = Regexp.new("(.)" * n).match(str)
1687+
assert_not_nil(m)
1688+
assert_equal([n - 1, n], m.offset(n))
1689+
m.inspect
1690+
end.value
1691+
1692+
assert_include(inspect, "MatchData")
1693+
RUBY
1694+
end
1695+
16721696
def test_regexp_popped
16731697
EnvUtil.suppress_warning do
16741698
assert_nothing_raised { eval("a = 1; /\#{ a }/; a") }

0 commit comments

Comments
 (0)