rubyのバグ、gdb豆知識(その3)
以下でSEGVする
$ ./ruby -e 'eval("1+" * 100000 + "1")' Segmentation fault 参照: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/37007
さっそくgdbでおってみる。
(gdb) run -e 'eval("1+" * 100000 + "1")' Starting program: /home/hyoshiok/work/ruby_trunk/ruby/ruby -e 'eval("1+" * 100000 + "1")' [Thread debugging using libthread_db enabled] [New Thread 0xb7d3c6b0 (LWP 8295)] [New Thread 0xb7f10b90 (LWP 8296)] Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0xb7d3c6b0 (LWP 8295)] iseq_compile_each (iseq=0x824ab18, ret=0xbf2253a0, node=0x871d27c, poped=0) at compile.c:2883
となる。
どこで呼ばれているかというと、
(gdb) bt 5 #0 iseq_compile_each (iseq=0x824ab18, ret=0xbf2253a0, node=0x871d27c, poped=0) at compile.c:2883 #1 0x08108088 in iseq_compile_each (iseq=0x824ab18, ret=0xbf2255f0, node=0x871d240, poped=0) at compile.c:3954 #2 0x08108088 in iseq_compile_each (iseq=0x824ab18, ret=0xbf225840, node=0x871d204, poped=0) at compile.c:3954 #3 0x08108088 in iseq_compile_each (iseq=0x824ab18, ret=0xbf225a90, node=0x871d1c8, poped=0) at compile.c:3954 #4 0x08108088 in iseq_compile_each (iseq=0x824ab18, ret=0xbf225ce0, node=0x871d18c, poped=0) at compile.c:3954 (More stack frames follow...)
スタックフレームを一つあがって(up)、ソースを見る(list)。
(gdb) up #1 0x08108088 in iseq_compile_each (iseq=0x824ab18, ret=0xbf2255f0, node=0x871d240, poped=0) at compile.c:3954 (gdb) list 3949 } 3950 } 3951 #endif 3952 /* reciever */ 3953 if (type == NODE_CALL) { 3954 COMPILE(recv, "recv", node->nd_recv); 3955 } 3956 else if (type == NODE_FCALL || type == NODE_VCALL) { 3957 ADD_CALL_RECEIVER(recv, nd_line(node)); 3958 }
3954行のCOMPILE(recv, "recv", node->nd_recv);というところから呼ばれているのがわかる。
COMPILEというのはマクロで定義は下記のとおりである。
/* compile node */ #define COMPILE(anchor, desc, node) \ (debug_compile("== " desc "\n", \ iseq_compile_each(iseq, anchor, node, 0)))
さて、SEGVしたのは、再帰的にiseq_compile_each()をずんずん呼んでいって、スタックオーバフローしたからである。さきほどのコードで確実に再現するので(わたしの環境はUbuntu 8.04)、gdbを利用していろいろ調べることが容易にできる。
gdbの場合、signalを掴まえると、設定によって、その場所で停止する。info signalでその設定をみることができる。
(gdb) info signal Signal Stop Print Pass to program Description SIGHUP Yes Yes Yes Hangup SIGINT Yes Yes No Interrupt SIGQUIT Yes Yes Yes Quit SIGILL Yes Yes Yes Illegal instruction SIGTRAP Yes Yes No Trace/breakpoint trap SIGABRT Yes Yes Yes Aborted SIGEMT Yes Yes Yes Emulation trap SIGFPE Yes Yes Yes Arithmetic exception SIGKILL Yes Yes Yes Killed SIGBUS Yes Yes Yes Bus error SIGSEGV Yes Yes Yes Segmentation fault SIGSYS Yes Yes Yes Bad system call SIGPIPE Yes Yes Yes Broken pipe SIGALRM No No Yes Alarm clock SIGTERM Yes Yes Yes Terminated SIGURG No No Yes Urgent I/O condition SIGSTOP Yes Yes Yes Stopped (signal) SIGTSTP Yes Yes Yes Stopped (user) SIGCONT Yes Yes Yes Continued SIGCHLD No No Yes Child status changed ...
SIGSEGVは停止して表示する設定になっている。通常SEGVは不正なアクセスや、スタックオーバフローなどで発生するので、ソースコードをみて対策をとればいい。
一般的にいってsignalを捕獲したら、それようのシグナルハンドラを用意して、何がしかの作業をすればいい。rubyではスタックオーバフローには対処していないようなので、新規にそれを実装するのは、ちょっとめんどうかもしれない。
BINARY HACKSのHACK #76(pp. 291-300)、"sigaltstackでスタックオーバフローに対処する"が参考になりそうだ。
Binary Hacks ―ハッカー秘伝のテクニック100選
- 作者: 高林哲,鵜飼文敏,佐藤祐介,浜地慎一郎,首藤一幸
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2006/11/14
- メディア: 単行本(ソフトカバー)
- 購入: 23人 クリック: 383回
- この商品を含むブログ (223件) を見る