Forum: Ruby-dev [ruby-trunk - Bug #7825][Assigned] VM/envのマーク漏れによるSEGV

Posted by ktsj (Kazuki Tsujimoto) (Guest)
on 2013-02-11 02:26
(Received via mailing list)
Issue #7825 has been reported by ktsj (Kazuki Tsujimoto).

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

Author: ktsj (Kazuki Tsujimoto)
Status: Assigned
Priority: High
Assignee: mame (Yusuke Endoh)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2013-02-10 trunk 39197) [x86_64-linux]


=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
Posted by mame (Yusuke Endoh) (Guest)
on 2013-02-11 04:13
(Received via mailing list)
Issue #7825 has been updated by mame (Yusuke Endoh).

Assignee changed from mame (Yusuke Endoh) to ko1 (Koichi Sasada)

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

そうですね。
ktsj さんのパッチなので大丈夫だとは思いますが、一応 ko1 さんに確認してもらって、よさそうならコミットしてください。

--
Yusuke Endoh <mame@tsg.ne.jp>
----------------------------------------
Bug #7825: VM/envのマーク漏れによるSEGV
https://bugs.ruby-lang.org/issues/7825#change-36139

Author: ktsj (Kazuki Tsujimoto)
Status: Assigned
Priority: High
Assignee: ko1 (Koichi Sasada)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2013-02-10 trunk 39197) [x86_64-linux]


=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
Posted by ko1 (Koichi Sasada) (Guest)
on 2013-02-16 08:48
(Received via mailing list)
Issue #7825 has been updated by ko1 (Koichi Sasada).

Assignee changed from ko1 (Koichi Sasada) to mame (Yusuke Endoh)

これは SEGV 起こせるバグなので、 r39276 を(検証の上)
バックポート頂けると助かります。

原因を簡単に説明すると、
- define_method で定義したメソッド foo を実行中に
- foo を別のメソッドで上書きすると
- foo の実体の Proc が解放されて大惨事
(正確には、Proc から参照される Env が解放されて大惨事)

という話でした。
最初のパッチでは Env を mark しようとしていますが、
最終的なパッチは method_entry から辿れる Proc を mark するようになっています。

PS.
ただし、この辺もっと綺麗になると思うので、2.0.0 が出た後でなんとかしたいと思っています。

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

Author: ktsj (Kazuki Tsujimoto)
Status: Closed
Priority: High
Assignee: mame (Yusuke Endoh)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2013-02-10 trunk 39197) [x86_64-linux]


=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
Posted by ko1 (Koichi Sasada) (Guest)
on 2013-02-16 08:53
(Received via mailing list)
Issue #7825 has been updated by ko1 (Koichi Sasada).

Status changed from Closed to Assigned


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

Author: ktsj (Kazuki Tsujimoto)
Status: Assigned
Priority: High
Assignee: mame (Yusuke Endoh)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2013-02-10 trunk 39197) [x86_64-linux]


=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
Posted by mame (Yusuke Endoh) (Guest)
on 2013-02-16 08:59
(Received via mailing list)
Issue #7825 has been updated by mame (Yusuke Endoh).

Assignee changed from mame (Yusuke Endoh) to ktsj (Kazuki Tsujimoto)

ktsj さんに確認してもらって、よさそうならバックポートしてもらうというのでどうでしょうか。

--
Yusuke Endoh <mame@tsg.ne.jp>
----------------------------------------
Bug #7825: VM/envのマーク漏れによるSEGV
https://bugs.ruby-lang.org/issues/7825#change-36339

Author: ktsj (Kazuki Tsujimoto)
Status: Assigned
Priority: High
Assignee: ktsj (Kazuki Tsujimoto)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2013-02-10 trunk 39197) [x86_64-linux]


=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
Posted by ktsj (Kazuki Tsujimoto) (Guest)
on 2013-02-16 12:50
(Received via mailing list)
Issue #7825 has been updated by ktsj (Kazuki Tsujimoto).

Status changed from Assigned to Closed

> ささださん
レビューありがとうございました。

問題なさそうなので、 r39279 でバックポートしました。
----------------------------------------
Bug #7825: VM/envのマーク漏れによるSEGV
https://bugs.ruby-lang.org/issues/7825#change-36344

Author: ktsj (Kazuki Tsujimoto)
Status: Closed
Priority: High
Assignee: ktsj (Kazuki Tsujimoto)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2013-02-10 trunk 39197) [x86_64-linux]


=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
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.