忘れたときに備えた記録
2009-05-06(Wednesday)
amazon.rbがSEGVする件
うちではestraier-register.rbを使っていないんですが、amazon.rbでSEGVする問題が発生していました。
ずっと気にはなっていたんてすが、今日ようやく時間ができたので調べてみたのです。
まず根本的な原因なんですが、Ruby-1.8.7にバグがあって、次のコードで落ちます。
#!/usr/bin/ruby
C = nil
o = ""
o.instance_eval("def m; C; end")
o.m
puts 1
o.clone.m
puts 2
~/opt/ruby187/bin/ruby -v ./test ruby 1.8.7 (2009-04-08 patchlevel 160) [x86_64-linux] 1 (eval):1: [BUG] Segmentation fault ruby 1.8.7 (2009-04-08 patchlevel 160) [x86_64-linux] Aborted
ただし、1.8の開発版では、もう直っています
~/opt/ruby18/bin/ruby -v ./test ruby 1.8.8dev (2009-05-06 revision 23350) [x86_64-linux] 1 2
また、1.9でも発生しないようです。
~/opt/ruby19/bin/ruby -v ./test ruby 1.9.1p5000 (2009-01-28 trunk 21811) [x86_64-linux] 1 2
で、と。とにかく現在の1.8.7では、
- オブジェクトに対してinstance_evalで、メソッドを追加する
- メソッドの中では定数を参照する
- そのオブジェクトのクローンを作る
- クローンに対して追加したメソッドの呼び出しを行う
という手順でSEGVするわけです。
僕の環境では、estraier-register.rbは使っていないんですが、squeeze.rbを使っています。 この中で
add_update_proc do
conf = @conf.clone
として@confのcloneを作り
def execute
(中略)
File::open(filename, 'w'){|f| f.write(eval_rhtml)}
@confのcloneを使ってeval_rhtml(中でプラグインを実行)するようになっています。
で、amazon.rbの中では conf.to_native を使っています。
さて、TDiary::Config#to_nativeですが、これは tdiary/lang/ja.rb の中で
def to_native( str, charset = nil )
begin
Iconv.conv('utf-8', charset || 'utf-8', str)
といった風に定数 Iconv を呼び出すメソッドを定義して、これをTDiary::Config#initializeが呼び出す #loadでロードしています。この時に、instance_evalを使っています。
def load
(中略)
@lang = 'ja' unless @lang
begin
instance_eval( File::open( "#{TDiary::PATH}/tdiary/lang/#{@lang}.rb" ){|f| f.read }.untaint, "(tdiary/lang/#{@lang}.rb)", 1 )
というわけで、
- conf#to_nativeはinstance_evalで定義されるメソッド
- to_nativeの中では定数 Iconv を呼び出し
- squeeze.rbでconfをclone
- squeeze.rbのeval_rhtmlの中のamazon.rbが、cloneしたconfのto_nativeを呼び出し
のコンボが成立してSEGVするというわけです。
早い話が、squeeze.rbの中で conf=@conf.clone して、クローンのto_nativeを使ったのが原因です。
なので、to_nativeを使っていればamazon.rb以外でもSEGVするし、conf.cloneしていればestraier-register.rbやsqueeze.rb以外でもSEGVするはずです。
また、さっきも書きましたが1.8系列の開発版や1.9系列ではバグが直っているのでそもそも起こりません。
どっとはらいヽ(´ー`)丿
おまけ
あたなたのRubyがamazon.rbでSEGVするか調べるワンライナー
ruby -ve "C=0; o=''; o.instance_eval('def m; C; end'); o.clone.m"
うちで使っているUbuntu 8.10のRubyはダメでした
ruby -ve "C=0; o=''; o.instance_eval('def m; C; end'); o.clone.m"
ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]
(eval):1: [BUG] Segmentation fault
ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]
Aborted
某所のVine 4.2のは大丈夫でした。
ruby -ve "C=0; o=''; o.instance_eval('def m; C; end'); o.clone.m"
ruby 1.8.5 (2006-08-25) [i386-linux]
うちのRuby1.8.7p12ではSEGVしませんでした。<br>ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-linux]<br><br>64bit環境だと問題があるとかでしょうか?
コメントありがとうございます!<br>上にあげたのは64bit版Ubuntu 8.10のRubyだったんですが、32bitのUbuntu 9.04の<br>ruby 1.8.7 (2008-08-11 patchlevel 72) [i486-linux]<br>でも、SEGVしました(ワンライナー調べ)。<br><br>i486とi686で違ったりするものなんでしょうか?(汗
今気づきましたが、Debian系列のRuby1.8.7-p72は、すっぴんのp72にその後のパッチをいくつか取り込んでるっぽいので、その辺が原因かもです。<br><br>つまり、p-73からp-160のどこかで入ったのかも
FreeBSD 7.2上でも落ちました。<br>ruby 1.8.7 (2009-04-08 patchlevel 160) [amd64-freebsd7]<br>(eval):1: [BUG] Segmentation fault<br>ruby 1.8.7 (2009-04-08 patchlevel 160) [amd64-freebsd7]<br><br>アボート<br>最近パッケージのバージョンを上げてから落ちるようになったので、p72からp160の間にバグが入ったようですね。