Fix segmentation fault by backtrace and GC

GitHub: fix #3122

It reverts #3126. #3126 fixes the segmentation fault but generates
broken backtrace.

This change fixes the segmentation fault and generates correct
backtrace. The strategy of this change is "generating backtrace while
irep is alive".

/tmp/test.rb:
    def gen
      e0 = nil
      begin
        1.times {
          raise 'foobar'
        }
      rescue => e
        e0 = e
      end
      e0
    end

    e = gen
    GC.start
    gen
    GC.start

    puts e.backtrace.join("\n")

Run:

    % bin/mruby /tmp/test.rb
    /tmp/test.rb:5:in Object.gen
    /home/kou/work/ruby/mruby.kou/mrblib/numeric.rb:77:in Integral#times
    /tmp/test.rb:4:in Object.gen
    /tmp/test.rb:13

FYI:

    % ruby -v /tmp/test.rb
    ruby 2.3.0p0 (2015-12-25) [x86_64-linux-gnu]
    /tmp/test.rb:5:in `block in gen'
    /tmp/test.rb:4:in `times'
    /tmp/test.rb:4:in `gen'
    /tmp/test.rb:13:in `<main>'
This commit is contained in:
Kouhei Sutou
2016-03-06 11:58:54 +09:00
parent d77c72dac5
commit f1eb3aea11
2 changed files with 12 additions and 2 deletions
+7 -2
View File
@@ -255,17 +255,22 @@ mrb_exc_set(mrb_state *mrb, mrb_value exc)
{
if (!mrb->gc.out_of_memory && mrb->backtrace.n > 0) {
mrb_value target_exc = mrb_nil_value();
int ai;
ai = mrb_gc_arena_save(mrb);
if ((mrb->exc && !have_backtrace(mrb, mrb->exc))) {
target_exc = mrb_obj_value(mrb->exc);
}
else if (!mrb_nil_p(exc) && mrb_obj_ptr(exc) == mrb->backtrace.exc) {
target_exc = exc;
else if (!mrb_nil_p(exc) && mrb->backtrace.exc) {
target_exc = mrb_obj_value(mrb->backtrace.exc);
mrb_gc_protect(mrb, target_exc);
}
if (!mrb_nil_p(target_exc)) {
mrb_value backtrace;
backtrace = mrb_restore_backtrace(mrb);
set_backtrace(mrb, target_exc, backtrace);
}
mrb_gc_arena_restore(mrb, ai);
}
mrb->backtrace.n = 0;
+5
View File
@@ -695,8 +695,13 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
#endif
case MRB_TT_OBJECT:
mrb_gc_free_iv(mrb, (struct RObject*)obj);
break;
case MRB_TT_EXCEPTION:
mrb_gc_free_iv(mrb, (struct RObject*)obj);
if ((struct RObject*)obj == mrb->backtrace.exc)
mrb->backtrace.exc = 0;
break;
case MRB_TT_CLASS: