From 6c097c7113d178fb6559e7690d2ab1513279a82f Mon Sep 17 00:00:00 2001 From: dearblue Date: Sat, 27 Mar 2021 17:11:16 +0900 Subject: Fix `SIGSEGV` with mruby-method + mruby-catch Previously, the following code would cause a `SIGSEGV`. ```ruby mm = method(:throw) define_method(:throw, ->(*args) { mm.call(*args) }) catch { |tag| throw tag } ``` I think the reason is in the `mrb_yield_with_class()` function: - Even if a C function is called, `CI_ACC_SKIP` is used - `cipop()` is not done if globally jumping from a C function --- src/vm.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vm.c b/src/vm.c index 1009fb638..5301c53f8 100644 --- a/src/vm.c +++ b/src/vm.c @@ -780,8 +780,25 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value mrb->c->ci->stack[argc+1] = mrb_nil_value(); if (MRB_PROC_CFUNC_P(p)) { - val = MRB_PROC_CFUNC(p)(mrb, self); + struct mrb_jmpbuf *prev_jmp = mrb->jmp; + struct mrb_jmpbuf c_jmp; + int ai = mrb_gc_arena_save(mrb); + mrb_bool exc = FALSE; + + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + ci->acc = CI_ACC_DIRECT; + val = MRB_PROC_CFUNC(p)(mrb, self); + } + MRB_CATCH(&c_jmp) { + exc = TRUE; + } + MRB_END_EXC(&c_jmp); + + mrb->jmp = prev_jmp; + mrb_gc_arena_restore(mrb, ai); cipop(mrb); + if (exc) MRB_THROW(mrb->jmp); } else { val = mrb_run(mrb, p, self); -- cgit v1.2.3