Bug #2398

race condition of /#{ foo }/o

Added by mame (Yusuke Endoh) over 2 years ago. Updated about 1 year ago.

[ruby-dev:39768]
Status:Closed Start date:
Priority:Low Due date:
Assignee:mame (Yusuke Endoh) % Done:

100%

Category:-
Target version:1.9.2
ruby -v:-

Description

遠藤です。

以下のようにすると SEGV します。


$ cat race.rb
  f = proc {|s| /#{ sleep 1; s }/o }
  [ Thread.new {            f.call("foo"); nil },
    Thread.new { sleep 0.5; f.call("bar"); nil },
  ].each {|t| t.join }
  GC.start
  p f.call

$ ./ruby race.rb
race.rb:6: [BUG] Segmentation fault
ruby 1.9.2dev (2009-11-24 trunk 25908) [i686-linux]

-- control frame ----------
c:0004 p:---- s:0011 b:0011 l:000010 d:000010 CFUNC  :p
c:0003 p:0083 s:0007 b:0007 l:001d8c d:0022e8 EVAL   race.rb:6
c:0002 p:---- s:0004 b:0004 l:000003 d:000003 FINISH
c:0001 p:0000 s:0002 b:0002 l:001d8c d:001d8c TOP
---------------------------
race.rb:6:in `<main>'
race.rb:6:in `p'

セグメンテーション違反です


onceinlinecache を通ってから setinlinecache に着く前までの間
(= /#{ sleep 1; s }/o の評価が始まってから終わるまでの間) に
複数のスレッドが突入すると、インラインキャッシュのアクセスが
race condition になっていて適切にマークされなくなるようです。

解決方法としては、当該範囲がクリティカルセクションになるよう
同期を行うか、または何かをする必要があると思います。

once フラグのある正規表現を複数スレッドで同時に評価した場合の
意味はどうなるでしょうか。

-- 
Yusuke ENDOH <mame@tsg.ne.jp>

Associated revisions

Revision 27515
Added by mame (Yusuke Endoh) about 2 years ago

* insns.def (onceinlinecache): add exclusion control for a region between onceinlinecache and setinlinecache. [ruby-dev:39768]

History

Updated by ujihisa (ujihisa .) over 2 years ago

  • Status changed from Open to Assigned
  • Assignee set to nobu (Nobuyoshi Nakada)

Updated by mame (Yusuke Endoh) about 2 years ago

  • Assignee changed from nobu (Nobuyoshi Nakada) to mame (Yusuke Endoh)
  • Target version set to 1.9.2
  • ruby -v set to -
遠藤です。

とりあえず、最初に once 区間に突入した (onceinlinecache を実行した)
スレッドが once 区間を脱出する (setinlinecache を実行する) まで、
2 番目以降に同じ区間に突入するスレッドをブロックするパッチを書きま
した。

最初のスレッドが正常に出口に到達しなかった場合、2 番目以降に突入した
スレッドたちは永久にブロックしてしまいますが、SEGV よりましですし、
Ctrl+C で止めることもできるので、いいかなと思います。
そもそもそういう状況になるようなプログラムに問題があると言えるかも
しれません。

すぐにコミットしてしまいますが、反対があったら言ってください。


diff --git a/insns.def b/insns.def
index 9541465..ba6f1d6 100644
--- a/insns.def
+++ b/insns.def
@@ -1199,10 +1199,17 @@ onceinlinecache
 ()
 (VALUE val)
 {
+  retry:
     if (ic->ic_vmstat) {
 	val = ic->ic_value.value;
 	JUMP(dst);
     }
+    else if (ic->ic_value.value == Qundef)
+    {
+    	RUBY_VM_CHECK_INTS();
+    	rb_thread_schedule();
+	goto retry;
+    }
     else {
 	/* none */
 	ic->ic_value.value = Qundef;

-- 
Yusuke Endoh <mame@tsg.ne.jp>

Updated by mame (Yusuke Endoh) about 2 years ago

  • Status changed from Assigned to Closed
  • % Done changed from 0 to 100
This issue was solved with changeset r27515.
Yusuke, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.

Also available in: Atom PDF