mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-regexp: fix leak and UAF on compile error paths
compile_error is the chokepoint for all regex-compile errors; mrb_raisef longjmps out of re_compile, abandoning the stack-local re_compiler struct. Three connected bugs: 1. Memory leak: c->code and c->classes (grown by emit/add_class via mrb_realloc) were never freed before raising, leaking on any compile error like /[/. c->stripped was already cleaned up here for the same reason; the other two buffers were missed. 2. Use-after-free: c->src aliases c->stripped when RE_FLAG_EXTENDED is set, but the original code freed c->stripped before passing c->src to mrb_raisef's "%s" formatter. Format the message into an mrb_value first (mruby's GC-managed string survives the longjmp), then free, then raise. 3. Heap-buffer-overflow: strip_extended returns a non-NUL-terminated buffer of size len. Even with format-before-free, "%s" called strlen and read past the buffer end. Use mruby's %l directive which takes an explicit (char*, size_t) and avoids strlen. Reported by OSS-Fuzz (clusterfuzz testcase 5394267353972736). Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -37,9 +37,24 @@ static void compile_alt(re_compiler *c); /* forward */
|
||||
static void
|
||||
compile_error(re_compiler *c, const char *msg)
|
||||
{
|
||||
/* Format the message before freeing c->stripped (which may alias c->src
|
||||
in extended mode). c->src is not NUL-terminated, so use %l with the
|
||||
explicit length from c->src_end. */
|
||||
mrb_value emsg = mrb_format(c->mrb, "%s: /%l/",
|
||||
msg, c->src, (size_t)(c->src_end - c->src));
|
||||
|
||||
/* Free compile buffers before raising, since mrb_exc_raise longjmps out
|
||||
and the stack-local re_compiler is abandoned without a chance to clean
|
||||
up. mrb_free doesn't trigger GC, so emsg stays valid across these. */
|
||||
mrb_free(c->mrb, c->code);
|
||||
c->code = NULL;
|
||||
mrb_free(c->mrb, c->classes);
|
||||
c->classes = NULL;
|
||||
if (c->stripped) mrb_free(c->mrb, c->stripped);
|
||||
c->stripped = NULL;
|
||||
mrb_raisef(c->mrb, mrb_exc_get_id(c->mrb, MRB_SYM(RegexpError)), "%s: /%s/", msg, c->src);
|
||||
|
||||
mrb_exc_raise(c->mrb,
|
||||
mrb_exc_new_str(c->mrb, mrb_exc_get_id(c->mrb, MRB_SYM(RegexpError)), emsg));
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
|
||||
Reference in New Issue
Block a user