From 4617263030d8f9acb558829f4da6134509755fa3 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 7 Feb 2026 19:10:15 +0900 Subject: [PATCH] mruby-compiler: fix JMPNOT-to-MATCHERR rewriting in pattern match codegen The MATCHERR optimization replaced JMPNOT (BS, 4 bytes) with MATCHERR (B, 2 bytes) and rewound s->pc by 2. When pattern alternation (e.g. a|B) dispatched a success jump to s->pc before the optimization, the rewind shifted subsequent instructions and the jump landed in the middle of the next instruction, causing out-of-bounds access at runtime. Replace JMPNOT in-place with MATCHERR+NOP+NOP to keep the same 4-byte size, so s->pc does not change and jump targets stay valid. Co-authored-by: Claude --- mrbgems/mruby-compiler/core/codegen.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index a77f9f17a..db7b3af02 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -6701,11 +6701,12 @@ codegen(codegen_scope *s, node *tree, int val) fail_pos + 2 == s->pc && s->iseq[fail_pos - 2] == OP_JMPNOT) { if (mp->raise_on_fail) { - /* Single failure point with raise - replace JMPNOT with MATCHERR */ - int reg = s->iseq[fail_pos - 1]; /* Register from JMPNOT */ - s->pc = fail_pos - 2; /* Rewind past JMPNOT */ - s->lastpc = s->pc; - genop_1(s, OP_MATCHERR, reg); /* Emit MATCHERR with the register */ + /* Replace JMPNOT(BS,4bytes) with MATCHERR(B,2bytes)+NOP+NOP; + * keep the same size so that any jump targeting s->pc stays valid */ + s->iseq[fail_pos - 2] = OP_MATCHERR; + /* fail_pos-1 already holds the register operand */ + s->iseq[fail_pos] = OP_NOP; + s->iseq[fail_pos + 1] = OP_NOP; s->sp = saved_sp - 1; if (val) push(); break; /* Pattern matching complete */