From: Yusuke ENDOH Date: 2007-08-10T23:59:37+09:00 Subject: [ruby-dev:31372] jump in rescue clause in eval dumps core 遠藤と申します。 1.9 で後置 rescue 節の中で break や next や redo をするコードを eval すると落ちます。 $ ./ruby -ve 'eval "0 rescue break"' ruby 1.9.0 (2007-08-10 patchlevel 0) [i686-linux] -e:1: -- control frame ---------- c:0004 p:---- s:0009 b:0009 l:000008 d:000008 CFUNC :eval c:0003 p:0009 s:0005 b:0005 l:000004 d:000004 TOP -e:1 c:0002 p:---- s:0003 b:0003 l:000002 d:000002 FINISH :inherited c:0001 p:---- s:0001 b:-001 l:000000 d:000000 ------ --------------------------- DBG> : "-e:1:in `
'" -- backtrace of native function call (Use addr2line) -- 0x80dac55 0x80f4093 0x80f415b 0x80b2250 0xffffe420 0x8104206 0x810b14c 0x80cf0f2 0x81028ae 0x81051fd 0x810b11e 0x80cf0f2 0x80cf6c7 0x80cf7f8 0x805b69d 0x805bb15 0x80d156e 0x80d3269 0x80d5de5 0x80d9554 0x80d988b 0x8059229 0x805d79d 0x8056df2 0xb7d6dea8 0x8056d21 ------------------------------------------------------- [BUG] Segmentation fault ruby 1.9.0 (2007-08-10) [i686-linux] アボートしました (core dumped) $ ./ruby -ve 'eval "0 rescue next"' ruby 1.9.0 (2007-08-10 patchlevel 0) [i686-linux] -e:1: -- control frame ---------- c:0004 p:---- s:0009 b:0009 l:000008 d:000008 CFUNC :eval c:0003 p:0009 s:0005 b:0005 l:000004 d:000004 TOP -e:1 c:0002 p:---- s:0003 b:0003 l:000002 d:000002 FINISH :inherited c:0001 p:---- s:0001 b:-001 l:000000 d:000000 ------ --------------------------- DBG> : "-e:1:in `
'" -- backtrace of native function call (Use addr2line) -- 0x80dac55 (snip) 0x8056d21 ------------------------------------------------------- [BUG] Segmentation fault ruby 1.9.0 (2007-08-10) [i686-linux] アボートしました (core dumped) $ ./ruby -ve 'eval "0 rescue redo"' ruby 1.9.0 (2007-08-10 patchlevel 0) [i686-linux] -e:1: -- control frame ---------- c:0004 p:---- s:0009 b:0009 l:000008 d:000008 CFUNC :eval c:0003 p:0009 s:0005 b:0005 l:000004 d:000004 TOP -e:1 c:0002 p:---- s:0003 b:0003 l:000002 d:000002 FINISH :inherited c:0001 p:---- s:0001 b:-001 l:000000 d:000000 ------ --------------------------- DBG> : "-e:1:in `
'" -- backtrace of native function call (Use addr2line) -- 0x80dac55 (snip) 0x8056d21 ------------------------------------------------------- [BUG] Segmentation fault ruby 1.9.0 (2007-08-10) [i686-linux] アボートしました (core dumped) NODE_BREAK や NODE_NEXT や NODE_REDO のコンパイルで parent_iseq を たどるとき、iseq が ISEQ_TYPE_EVAL かどうかのチェックがなかったり、 COMPILE_ERROR が while の中にあるせいで while 直後の余計なコードが 実行されていたりするためのようです。 # 今後後者のようなミスを防ぐために、COMPILE_ERROR マクロの break を # goto に変えておくのも手かも? Index: compile.c =================================================================== --- compile.c (revision 12914) +++ compile.c (working copy) @@ -2903,9 +2903,14 @@ } else { rb_iseq_t *ip = iseq->parent_iseq; + int error_type_eval = 0; while (ip) { level++; - if (ip->compile_data->redo_label != 0) { + if (ip->type == ISEQ_TYPE_EVAL) { + error_type_eval = 1; + break; + } + else if (ip->compile_data->redo_label != 0) { level = 0x8000; if (ip->compile_data->loopval_popped == 0) { /* need value */ @@ -2919,7 +2924,12 @@ } ip = ip->parent_iseq; } - COMPILE_ERROR((ERROR_ARGS "Illegal break")); + if (error_type_eval) { + COMPILE_ERROR((ERROR_ARGS "Can't escape from eval with break")); + } + else { + COMPILE_ERROR((ERROR_ARGS "Illegal break")); + } } break; } @@ -2942,18 +2952,26 @@ } else { rb_iseq_t *ip = iseq->parent_iseq; + int error_type_eval = 0; while (ip) { level = 0x8000; if (ip->type == ISEQ_TYPE_BLOCK) { level |= 0x4000; break; } + else if (ip->type == ISEQ_TYPE_EVAL) { + error_type_eval = 1; + break; + } else if (ip->compile_data->redo_label != 0) { break; } ip = ip->parent_iseq; } - if (ip != 0) { + if (error_type_eval) { + COMPILE_ERROR((ERROR_ARGS "Can't escape from eval with next")); + } + else if (ip != 0) { COMPILE(ret, "next val", node->nd_stts); add_ensure_iseq(ret, iseq); ADD_INSN1(ret, nd_line(node), throw, @@ -2987,19 +3005,24 @@ else { rb_iseq_t *ip = iseq->parent_iseq; unsigned long level = 0x8000 | 0x4000; + int error_type_eval = 0; while (ip) { if (ip->type == ISEQ_TYPE_BLOCK) { break; } else if (ip->type == ISEQ_TYPE_EVAL) { - COMPILE_ERROR((ERROR_ARGS "Can't escape from eval with redo")); + error_type_eval = 1; + break; } else if (ip->compile_data->redo_label != 0) { break; } ip = ip->parent_iseq; } - if (ip != 0) { + if (error_type_eval) { + COMPILE_ERROR((ERROR_ARGS "Can't escape from eval with redo")); + } + else if (ip != 0) { add_ensure_iseq(ret, iseq); ADD_INSN1(ret, nd_line(node), throw, INT2FIX(level | 0x05) /* TAG_REDO */ ); -- Yusuke ENDOH