Project

General

Profile

Backport #7825

Updated by ktsj (Kazuki Tsujimoto) over 10 years ago

=begin 
 辻本です。 

 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扱いになるのではないかと思いますが、いかがでしょうか。 
 =end 

Back