Bug #702
closed1+1+1+...+1 dumps core
Description
=begin
遠藤です。
以下で SEGV します。
$ ./ruby -e 'eval("1+" * 100000 + "1")'
セグメンテーション違反です
iseq_compile_each の再帰でスタックオーバーフローするためです。
再帰数のチェックを入れれば例外にできると思いますが、こんな
ケースのためにコンパイルを遅くするのはもったいない気もします。
どんなものでしょう。WontFix 行き?
また、MVM 版だと SystemStackError として検知できるらしいです。
ちなみに、パーサの方でスタックオーバーフローする場合は例外に
なります (bison に依存するかもしれませんが) 。
$ ./ruby -e 'eval("1+(" * 100000 + "1" + ")" * 100000)'
-e:1:in eval': (eval):1: memory exhausted (SyntaxError) ...+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1... ... ^ from -e:1:in
'
--
Yusuke ENDOH [email protected]
=end
Updated by mame (Yusuke Endoh) over 16 years ago
=begin
遠藤です。
2008/11/17 0:04 Hiro Yoshioka [email protected]:
はじめまして、よしおかと申します。
下記のバグなんですが、スタックオーバーフローでSIGSEGVの場合
リソースを使いきってしまうので、シグナルハンドラが動くリソース
すらなくなってしまって落るのではないかと思います。対処法はBINARY HACKSなどに載っているsigaltstack(2)という代替
スタックを用意してあげれば対処できるのではないかと思います。いずれにせよ、落ることにはかわりないんですが、どこで落ちて
いるかがわかる程度のことはあるかと。下記はsigaltstack(2)を使って実装した例です。
$ ./ruby -e 'p eval("1+" * 14154 + "1")'
-e:1: [BUG] Segmentation fault
ruby 1.9.0 (2008-11-01 revision 20086) [i686-linux]-- control frame ----------
c:0004 p:---- s:0011 b:0011 l:000010 d:000010 CFUNC :eval
c:0003 p:0018 s:0007 b:0006 l:000005 d:000005 TOP -e:1
c:0002 p:---- s:0004 b:0004 l:000003 d:000003 FINISH :inherited
c:0001 p:0000 s:0002 b:0002 l:000001 d:000001 TOP :17¶-e:1:in
eval': stack level too deep (SystemStackError) from -e:1:in
'どのように対処したかは日記に記しましたので、ご笑覧ください。
http://d.hatena.ne.jp/hyoshiok/20081116rubyのお作法を全然わかっていないので、変なコードになっている
とは思いますが、コメントいただけるとうれしいです。
ありがとうございます。面白いですね。
sigaltstack(2) ってどのくらいポータブルなんでしょうか。
Debian の man によると CONFORMING TO SUSv2, SVr4, POSIX.1-2001.
らしいですが、windows とかどうなんでしょう。
その辺が大丈夫そうなら、「取り込む」に一票です。
--
Yusuke ENDOH [email protected]
=end
Updated by usa (Usaku NAKAMURA) over 16 years ago
=begin
こんにちは、なかむら(う)です。
In message "[ruby-dev:37129] Re: [Bug:1.9] 1+1+1+...+1 dumps core"
on Nov.20,2008 02:54:16, [email protected] wrote:
sigaltstack(2) ってどのくらいポータブルなんでしょうか。
Debian の man によると CONFORMING TO SUSv2, SVr4, POSIX.1-2001.
らしいですが、windows とかどうなんでしょう。
Windowsにあるわきゃないんですが、ま、configureで調べてあれば
使う、でいいんじゃないでしょうか。
それでは。¶
U.Nakamura [email protected]
=end
Updated by yugui (Yuki Sonoda) over 16 years ago
=begin
Yuguiです。
U.Nakamura さんは書きました:
Windowsにあるわきゃないんですが、ま、configureで調べてあれば
使う、でいいんじゃないでしょうか。
Windowsで代替スタックを使えないのかと思って調べたらこんなのが見つかりま
した。
http://www.nminoru.jp/~nminoru/programming/stackoverflow_handling.html#winnt
もしかしてcall_cfuncで構造化例外を捕まえてSystemStackErrorに変換したりで
きます?
SEGVシグナルだときついですけど、SystemStackErrorを投げる余裕があればそれ
に越したことはないですよね。
--
Yugui [email protected]
http://yugui.jp
私は私をDumpする
=end
Updated by usa (Usaku NAKAMURA) over 16 years ago
=begin
こんにちは、なかむら(う)です。
In message "[ruby-dev:37131] Re: [Bug:1.9] 1+1+1+...+1 dumps core"
on Nov.21,2008 00:27:43, [email protected] wrote:
Windowsで代替スタックを使えないのかと思って調べたらこんなのが見つかりま
した。
http://www.nminoru.jp/~nminoru/programming/stackoverflow_handling.html#winntもしかしてcall_cfuncで構造化例外を捕まえてSystemStackErrorに変換したりで
きます?
call_cfuncじゃなくてrb_iseq_evalの中身あたりをくくるのがいい
んじゃないかと思いますが、まあそうです。
しかしこれ代替スタック領域なのかな。単に今ぶっ壊したスタック
領域をなかったことにして__try〜__except節の深さにまで巻き戻さ
れてるされてるだけのような気が...
それでは。¶
U.Nakamura [email protected]
=end
Updated by Tietew (Toru Iwase) over 16 years ago
=begin
On Fri, 21 Nov 2008 15:12:37 +0900
In article [email protected]
[[ruby-dev:37133] Re: [Bug:1.9] 1+1+1+...+1 dumps core]
"U.Nakamura" [email protected] wrote:
Windowsで代替スタックを使えないのかと思って調べたらこんなのが見つかりま
した。
http://www.nminoru.jp/~nminoru/programming/stackoverflow_handling.html#winntもしかしてcall_cfuncで構造化例外を捕まえてSystemStackErrorに変換したりで
きます?call_cfuncじゃなくてrb_iseq_evalの中身あたりをくくるのがいい
んじゃないかと思いますが、まあそうです。しかしこれ代替スタック領域なのかな。単に今ぶっ壊したスタック
領域をなかったことにして__try〜__except節の深さにまで巻き戻さ
れてるされてるだけのような気が...
SEHはCPUがトラップした例外を通知する仕組みなので、STATUS_STACK_OVERFLOW
が発生してもスタックは(まだ)ぶっ壊れていません。
__except( ... ) の括弧内を実行中は、元のスタックの続き(代替スタック)を
使っています。そうじゃないと EXCEPTION_CONTINUE_EXECUTION で再実行できな
いから。
EXCEPTION_EXECUTE_HANDLER を返すと、そこから __finally を実行しつつスタッ
クを巻き戻します。
ただ、ここで SystemStackError を生成して実行を継続できるかというと疑問で
す。CPUがトラップするので、たとえば malloc が帰ってきた次の命令がトラッ
プされるとそのメモリ領域はリークします。
abort() か、fatal を投げて終了、くらいが関の山かなあと。
--
Tietew [email protected]
Blog: http://www.tietew.jp/
PGP: 26CB 71BB B595 09C4 0153 81C4 773C 963A D51B 8CAA
=end
Updated by usa (Usaku NAKAMURA) over 16 years ago
=begin
こんにちは、なかむら(う)です。
In message "[ruby-dev:37136] Re: [Bug:1.9] 1+1+1+...+1 dumps core"
on Nov.21,2008 18:02:28, [email protected] wrote:
__except( ... ) の括弧内を実行中は、元のスタックの続き(代替スタック)を
使っています。そうじゃないと EXCEPTION_CONTINUE_EXECUTION で再実行できな
いから。
「元のスタックの続き」って具体的にどこになるんでしょうか。
元のスタックの続きが確保できない例外がSTATUS_STACK_OVERFLOWで
あると理解しているのですが...
「続き」の意味というか方向が違ったりしますか?
それでは。¶
U.Nakamura [email protected]
=end
Updated by Tietew (Toru Iwase) over 16 years ago
=begin
On Fri, 21 Nov 2008 18:48:16 +0900
In article [email protected]
[[ruby-dev:37137] Re: [Bug:1.9] 1+1+1+...+1 dumps core]
"U.Nakamura" [email protected] wrote:
こんにちは、なかむら(う)です。
In message "[ruby-dev:37136] Re: [Bug:1.9] 1+1+1+...+1 dumps core"
on Nov.21,2008 18:02:28, [email protected] wrote:__except( ... ) の括弧内を実行中は、元のスタックの続き(代替スタック)を
使っています。そうじゃないと EXCEPTION_CONTINUE_EXECUTION で再実行できな
いから。「元のスタックの続き」って具体的にどこになるんでしょうか。
元のスタックの続きが確保できない例外がSTATUS_STACK_OVERFLOWで
あると理解しているのですが...
STATUS_STACK_OVERFLOWの場合が代替スタックで、それ以外は続き、です。
ただ、代替スタックもアドレスとしては続きの位置にあったような気がします
(うろ覚え
ページとしては確保しておくが書込禁止にしてあって、トラップすると書込禁¶
止を解除するイメージ
--
Tietew [email protected]
Blog: http://www.tietew.jp/
PGP: 26CB 71BB B595 09C4 0153 81C4 773C 963A D51B 8CAA
=end
Updated by usa (Usaku NAKAMURA) over 16 years ago
=begin
こんにちは、なかむら(う)です。
In message "[ruby-dev:37138] Re: [Bug:1.9] 1+1+1+...+1 dumps core"
on Nov.21,2008 19:15:01, [email protected] wrote:
__except( ... ) の括弧内を実行中は、元のスタックの続き(代替スタック)を
使っています。そうじゃないと EXCEPTION_CONTINUE_EXECUTION で再実行できな
いから。
ああ、そうか、誤読してました。
上記の「...」を実行するときの話ですね。やっと理解できました。
__except節内のことだと思い込んでいた¶
ただ、代替スタックもアドレスとしては続きの位置にあったような気がします
(うろ覚えページとしては確保しておくが書込禁止にしてあって、トラップすると書込禁¶
止を解除するイメージ
イメージの方はあってると思います。
代替スタックの方は、続きの位置にあるなら、つまりその他の例外
の場合と何も変わらない気が...
要するにEXCEPTION_STACK_OVERFLOWが起きてもあと1ページだけスタ
ック使えるよっていうそれだけのことですよね。
それでは。¶
U.Nakamura [email protected]
=end
Updated by usa (Usaku NAKAMURA) over 16 years ago
=begin
こんにちは、なかむら(う)です。
というわけで、疑問は解消したので、
In message "[ruby-dev:37133] Re: [Bug:1.9] 1+1+1+...+1 dumps core"
on Nov.21,2008 15:12:37, [email protected] wrote:
もしかしてcall_cfuncで構造化例外を捕まえてSystemStackErrorに変換したりで
きます?call_cfuncじゃなくてrb_iseq_evalの中身あたりをくくるのがいい
んじゃないかと思いますが、まあそうです。
という方針でパッチ書いてみました。
なんかびみょーにcontrol frameの中身が違いますね。¶
C:>miniruby -e "eval('1+'*100000+'1')"
-e:1: [BUG] Segmentation fault
ruby 1.9.0 (2008-11-19 revision 20257) [i386-mswin32]
-- control frame ----------
c:0004 p:---- s:0010 b:0010 l:000009 d:000009 CFUNC :eval
c:0003 p:0017 s:0006 b:0006 l:000005 d:000005 TOP -e:1
c:0002 p:---- s:0004 b:0004 l:000003 d:000003 FINISH
c:0001 p:0000 s:0002 b:0002 l:000001 d:000001 TOP¶
-e:1:in eval': stack level too deep (SystemStackError) from -e:1:in
'
Index: vm.c¶
--- vm.c (revision 20257)
+++ vm.c (working copy)
@@ -1231,6 +1231,16 @@ vm_exec(rb_thread_t *th)
/* misc */
+#ifdef _WIN32
+static void
+w32_check_stack_overflow(DWORD code)
+{
- if (code == EXCEPTION_STACK_OVERFLOW) {
- raise(SIGSEGV);
- }
+}
+#endif
VALUE
rb_iseq_eval(VALUE iseqval)
{
@@ -1243,7 +1253,17 @@ rb_iseq_eval(VALUE iseqval)
if (!rb_const_defined(rb_cObject, rb_intern("TOPLEVEL_BINDING"))) {
rb_define_global_const("TOPLEVEL_BINDING", rb_binding_new());
}
+#ifdef _WIN32
- __try {
+#endif
val = vm_exec(th);
+#ifdef _WIN32 - }
- __except(w32_check_stack_overflow(GetExceptionCode()),
-
EXCEPTION_CONTINUE_SEARCH) {
- /* never comes here */
- }
+#endif
tmp = iseqval; /* prohibit tail call optimization */
return val;
}
それでは。
--
U.Nakamura [email protected]
=end
Updated by matz (Yukihiro Matsumoto) over 16 years ago
- Status changed from Open to Closed
=begin
fixed by r20293 (on linux).
=end
Updated by nobu (Nobuyoshi Nakada) over 16 years ago
=begin
なかだです。
At Mon, 17 Nov 2008 00:04:12 +0900,
Hiro Yoshioka wrote in [ruby-dev:37102]:
下記はsigaltstack(2)を使って実装した例です。
$ ./ruby -e 'p eval("1+" * 14154 + "1")'
-e:1: [BUG] Segmentation fault
ruby 1.9.0 (2008-11-01 revision 20086) [i686-linux]-- control frame ----------
c:0004 p:---- s:0011 b:0011 l:000010 d:000010 CFUNC :eval
c:0003 p:0018 s:0007 b:0006 l:000005 d:000005 TOP -e:1
c:0002 p:---- s:0004 b:0004 l:000003 d:000003 FINISH :inherited
c:0001 p:0000 s:0002 b:0002 l:000001 d:000001 TOP :17¶-e:1:in
eval': stack level too deep (SystemStackError) from -e:1:in
'
なぜとくにチェックもしていないのにSystemStackErrorになっているの
か疑問だったのですが、代替スタック上でメソッドを呼び出している際
にスタックチェックしているので、これではすべてのrb_bugが常に
SystemStackErrorになってしまいます。
$ .i686-linux/miniruby -e Exception.abort!
-e:1: [BUG] Segmentation fault
ruby 1.9.0 (2008-11-26 revision 20367) [i686-linux]
-- control frame ----------
c:0004 p:---- s:0009 b:0009 l:000008 d:000008 CFUNC :abort!
c:0003 p:0015 s:0006 b:0006 l:000005 d:000005 TOP -e:1
c:0002 p:---- s:0004 b:0004 l:000003 d:000003 FINISH
c:0001 p:0000 s:0002 b:0002 l:000001 d:000001 TOP :17¶
-e:1:in abort!': stack level too deep (SystemStackError) from -e:1:in
'
しかも、代替スタックでもさらにオーバーフローしてヒープを壊してい
ます。
代替スタックを使うことだけなら極簡単な話で、問題はスタックオーバー
フローかどうかをきちんとポータブルに判断する方法です。
--
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
中田 伸悦
=end