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

Issue #7825 has been reported by ktsj (Kazuki Tsujimoto).


Bug #7825: VM/envのマーク漏れによるSEGV

Author: ktsj (Kazuki Tsujimoto)
Status: Assigned
Priority: High
Assignee: mame (Yusuke E.)
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

Issue #7825 has been updated by mame (Yusuke E.).

Assignee changed from mame (Yusuke E.) to ko1 (Koichi Sasada)

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

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


Yusuke E. [email protected]

Bug #7825: VM/envのマーク漏れによるSEGV

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

Issue #7825 has been updated by ko1 (Koichi Sasada).

Assignee changed from ko1 (Koichi Sasada) to mame (Yusuke E.)

これは 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

Author: ktsj (Kazuki Tsujimoto)
Status: Closed
Priority: High
Assignee: mame (Yusuke E.)
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

Issue #7825 has been updated by mame (Yusuke E.).

Assignee changed from mame (Yusuke E.) to ktsj (Kazuki Tsujimoto)

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


Yusuke E. [email protected]

Bug #7825: VM/envのマーク漏れによるSEGV

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

Issue #7825 has been updated by ko1 (Koichi Sasada).

Status changed from Closed to Assigned


Bug #7825: VM/envのマーク漏れによるSEGV

Author: ktsj (Kazuki Tsujimoto)
Status: Assigned
Priority: High
Assignee: mame (Yusuke E.)
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

Issue #7825 has been updated by ktsj (Kazuki Tsujimoto).

Status changed from Assigned to Closed

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

問題なさそうなので、 r39279 でバックポートしました。

Bug #7825: VM/envのマーク漏れによるSEGV

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

チケット #7825 が Kazuki Tsujimoto によって更新されました。

説明 を更新


Backport #7825: VM/envのマーク漏れによるSEGV

  • 作成者: 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)

チケット #7825 が Kazuki Tsujimoto によって更新されました。

トラッカー を Bug から Backport に変更
プロジェクト を ruby-trunk から Backport93 に変更
カテゴリ を削除 (core)
ステータス を Closed から Assigned に変更
担当者 を Kazuki Tsujimoto から Usaku NAKAMURA に変更
優先度 を High から Normal に変更
対象バージョン を削除 (2.0.0)

1.9.3にも同じ問題があるので、バックポートをお願いします。


Backport #7825: VM/envのマーク漏れによるSEGV

  • 作成者: Kazuki Tsujimoto
  • ステータス: Assigned
  • 優先度: Normal
  • 担当者: Usaku NAKAMURA
  • カテゴリ:
  • 対象バージョン:
  • 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

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

Issue #7825 has been updated by Usaku NAKAMURA.

Status changed from Assigned to Closed

Applied in changeset r44736.


merge revision(s) 39276: [Backport #7825]

  • vm.c (rb_thread_mark): mark a working Proc of bmethod
    (a method defined by define_method) even if the method was removed.
    We could not trace working Proc object which represents the body
    of bmethod if the method was removed (alias/undef/overridden).
    Simply, it was mark miss.
    This patch by Kazuki Tsujimoto. [Bug #7825]
    NOTE: We can brush up this marking because we do not need to mark
    me' on each living control frame. We need to mark me’s
    only if `me’ was free’ed. This is future work after Ruby 2.0.0.

  • test/ruby/test_method.rb: add a test.


Backport #7825: VM/envのマーク漏れによるSEGV

  • Author: Kazuki Tsujimoto
  • Status: Closed
  • Priority: Normal
  • Assignee: Usaku NAKAMURA
  • Category:
  • Target version:
  • 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扱いになるのではないかと思いますが、いかがでしょうか。

—Files--------------------------------
backtrace.txt (4.24 KB)