From db2845aae05eb4a624eae22892f73ebdd1ee02c3 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 19 May 2026 10:40:45 +0900 Subject: [PATCH] mruby-regexp: bounds-check group index in RE_BACKREF The RE_BACKREF execution path read `captures[group * 2]` and `captures[group * 2 + 1]` without verifying that the group index fit in the allocated captures array. A pattern like `/\1/` (no capture group, but a backreference to group 1) is accepted by the compiler and lands in execution with `ncap = 2` (only group 0 slots) and an instruction asking for group 1 -- a 4-byte read past the end of the allocation. Reported by ClusterFuzz testcase clusterfuzz-testcase-minimized-mruby_fuzzer-5474946829844480. Add `if (group * 2 + 1 >= ncap) return FALSE;` ahead of the captures access, mirroring the bounds guard already present in RE_SAVE. The compiler's permissive `\` handling stays unchanged; the runtime now treats a reference to a non-existent group as a non-match rather than UB. Co-authored-by: Claude --- mrbgems/mruby-regexp/src/re_exec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mrbgems/mruby-regexp/src/re_exec.c b/mrbgems/mruby-regexp/src/re_exec.c index 889c60c38..246398219 100644 --- a/mrbgems/mruby-regexp/src/re_exec.c +++ b/mrbgems/mruby-regexp/src/re_exec.c @@ -492,6 +492,7 @@ bt_match(const mrb_regexp_pattern *pat, const char *str, const char *str_end, case RE_BACKREF: { int group = inst.a; + if (group * 2 + 1 >= ncap) return FALSE; int gs = captures[group * 2]; int ge = captures[group * 2 + 1]; if (gs < 0 || ge < 0) return FALSE;