[ruby-dev:47885] [Backport93 - Backport #7825] VM/envのマーク漏れによるSEGV

From: Kazuki@...
Date: 2014-01-13 11:28:35 UTC
List: ruby-dev #47885
チケット #7825 が Kazuki Tsujimoto によって更新されました。

説明 を更新

----------------------------------------
Backport #7825: VM/envのマーク漏れによるSEGV
https://bugs.ruby-lang.org/issues/7825#change-44264

* 作成者: Kazuki Tsujimoto
* ステータス: Assigned
* 優先度: Normal
* 担当者: Usaku NAKAMURA
* カテゴリ: 
* 対象バージョン: 
* ruby -v: ruby 2.0.0dev (2013-02-10 trunk 39197) [x86_64-linux]
----------------------------------------
辻本です。

trunk/rc2で次のコードでSEGVします(Ubuntu 12.04 x86_64)。

    require 'irb'
    IRB::Irb.module_eval do
      define_method(:eval_input) do
        IRB::Irb.module_eval { alias_method :eval_input, :to_s }
        # (A)
        GC.start
        Kernel
      end
    end
    IRB.start

以下がGC前(A)のフレームの構造です。
(0x555555e078c8は2行目のmodule_evalのBLOCKフレームに対応するep)

    c:0006 (0x7ffff6b08e30) p:0046 s:0018 e:000017 LAMBDA test.rb:5 [FINISH]
      -- prev ep(s) --
      ep: 0x7ffff6a09098
      ep: 0x555555e078c8
      ep: 0x555555b74360
      ep: 0x555555b26a88
    c:0002 (0x7ffff6b08f70) p:0044 s:0005 E:0012d0 EVAL   test.rb:10 [FINISH]
      -- prev ep(s) --
      ep: 0x555555b74360
      ep: 0x555555b26a88
    c:0001 (0x7ffff6b08fc0) p:0000 s:0002 E:001bf8 TOP    [FINISH]
      -- prev ep(s) --
      ep: 0x555555b26a88
 
これがGC後に次のようになり、epが辿れなくなっています。
 
    c:0006 (0x7ffff6b08e30) p:0062 s:0018 e:000017 LAMBDA test.rb:7 [FINISH]
      -- prev ep(s) --
      ep: 0x7ffff6a09098
      ep: 0x555555e078c8
      ep: 0x555555e0da20
      ep: (nil)

epがスタックを指している場合に、上位(PREV)のepがヒープにあると
そのennvalがマークされずにGCされるということのようです。

ちょっと自信がありませんが、以下のパッチで直ります。

    diff --git a/vm.c b/vm.c
    index 36def2c..fea4a57 100644
    --- a/vm.c
    +++ b/vm.c
    @@ -1775,10 +1775,22 @@ rb_thread_mark(void *ptr)
     	    rb_gc_mark_locations(p, p + th->mark_stack_len);
     
     	    while (cfp != limit_cfp) {
    +		VALUE *ep = cfp->ep;
    +		VALUE *lep = VM_CF_LEP(cfp);
     		rb_iseq_t *iseq = cfp->iseq;
     		rb_gc_mark(cfp->proc);
     		rb_gc_mark(cfp->self);
     		rb_gc_mark(cfp->klass);
    +		while (1) {
    +		    if (ENV_IN_HEAP_P(th, ep)) {
    +			rb_gc_mark(ep[1]); /* envval */
    +			break;
    +		    }
    +		    if (ep == lep) {
    +			break;
    +		    }
    +		    ep = VM_EP_PREV_EP(ep);
    +		}
     		if (iseq) {
     		    rb_gc_mark(RUBY_VM_NORMAL_ISEQ_P(iseq) ? iseq->self : (VALUE)iseq);
     		}

showstopper扱いになるのではないかと思いますが、いかがでしょうか。

---ファイル---------------------------------
backtrace.txt (4.244 KB)


-- 
http://bugs.ruby-lang.org/

In This Thread