From: "akr (Akira Tanaka)" Date: 2013-07-18T19:49:13+09:00 Subject: [ruby-dev:47527] [ruby-trunk - Bug #8644] valgrind error in a readline test Issue #8644 has been updated by akr (Akira Tanaka). 調べてみると、テストにある、Readline.output に設定した IO オブジェクトを close した場合だけでなく、 Readline.input に設定した IO オブジェクトを close した場合にも Invalid read が起こるようです。 以下のようにして確認できます。 % valgrind ./ruby -rreadline -e ' r, w = IO.pipe Readline.input = r Readline.output = w r.close Readline.readline' ==10001== Memcheck, a memory error detector ==10001== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al. ==10001== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==10001== Command: ./ruby -rreadline -e ==10001== r,\ w\ =\ IO.pipe ==10001== Readline.input\ =\ r ==10001== Readline.output\ =\ w ==10001== r.close ==10001== Readline.readline ==10001== ==10001== Invalid read of size 4 ==10001== at 0x59706A0: fileno (fileno.c:37) ==10001== by 0x6EDB699: readline_readline (readline.c:394) ==10001== by 0x258AFE: call_cfunc_m1 (vm_insnhelper.c:1348) ==10001== by 0x2596EE: vm_call_cfunc_with_frame (vm_insnhelper.c:1492) ==10001== by 0x259828: vm_call_cfunc (vm_insnhelper.c:1582) ==10001== by 0x25A368: vm_call_method (vm_insnhelper.c:1774) ==10001== by 0x25AB02: vm_call_general (vm_insnhelper.c:1925) ==10001== by 0x25E4D0: vm_exec_core (insns.def:1017) ==10001== by 0x26D8C8: vm_exec (vm.c:1198) ==10001== by 0x26E53C: rb_iseq_eval_main (vm.c:1448) ==10001== by 0x12D3CC: ruby_exec_internal (eval.c:252) ==10001== by 0x12D4DF: ruby_exec_node (eval.c:317) ==10001== Address 0x6e5f970 is 0 bytes inside a block of size 568 free'd ==10001== at 0x4C240FD: free (vg_replace_malloc.c:366) ==10001== by 0x596CD5C: fclose@@GLIBC_2.2.5 (iofclose.c:88) ==10001== by 0x156E76: nogvl_fclose (io.c:4048) ==10001== by 0x27AB3D: call_without_gvl (thread.c:1244) ==10001== by 0x27AC6F: rb_thread_call_without_gvl (thread.c:1354) ==10001== by 0x156EBC: maygvl_fclose (io.c:4057) ==10001== by 0x157014: fptr_finalize (io.c:4099) ==10001== by 0x15712B: rb_io_fptr_cleanup (io.c:4130) ==10001== by 0x15740C: rb_io_close (io.c:4221) ==10001== by 0x157464: rb_io_close_m (io.c:4250) ==10001== by 0x258B2E: call_cfunc_0 (vm_insnhelper.c:1354) ==10001== by 0x2596EE: vm_call_cfunc_with_frame (vm_insnhelper.c:1492) ==10001== -e:6:in `readline': closed stdin (IOError) from -e:6:in `
' ==10001== ==10001== HEAP SUMMARY: ==10001== in use at exit: 1,503,865 bytes in 23,682 blocks ==10001== total heap usage: 41,787 allocs, 18,105 frees, 8,853,637 bytes allocated ==10001== ==10001== LEAK SUMMARY: ==10001== definitely lost: 453,958 bytes in 4,422 blocks ==10001== indirectly lost: 696,055 bytes in 12,524 blocks ==10001== possibly lost: 121 bytes in 1 blocks ==10001== still reachable: 353,731 bytes in 6,735 blocks ==10001== suppressed: 0 bytes in 0 blocks ==10001== Rerun with --leak-check=full to see details of leaked memory ==10001== ==10001== For counts of detected and suppressed errors, rerun with: -v ==10001== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4) zsh: exit 1 valgrind ./ruby -rreadline -e ついでに、Readline.output のほうも test でなく単独で確認するには以下のようにできます。 % valgrind ./ruby -rreadline -e ' r, w = IO.pipe Readline.input = r Readline.output = w w.close Readline.readline' ==9994== Memcheck, a memory error detector ==9994== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al. ==9994== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==9994== Command: ./ruby -rreadline -e ==9994== r,\ w\ =\ IO.pipe ==9994== Readline.input\ =\ r ==9994== Readline.output\ =\ w ==9994== w.close ==9994== Readline.readline ==9994== ==9994== Invalid read of size 4 ==9994== at 0x59706A0: fileno (fileno.c:37) ==9994== by 0x6EDB6EF: readline_readline (readline.c:397) ==9994== by 0x258AFE: call_cfunc_m1 (vm_insnhelper.c:1348) ==9994== by 0x2596EE: vm_call_cfunc_with_frame (vm_insnhelper.c:1492) ==9994== by 0x259828: vm_call_cfunc (vm_insnhelper.c:1582) ==9994== by 0x25A368: vm_call_method (vm_insnhelper.c:1774) ==9994== by 0x25AB02: vm_call_general (vm_insnhelper.c:1925) ==9994== by 0x25E4D0: vm_exec_core (insns.def:1017) ==9994== by 0x26D8C8: vm_exec (vm.c:1198) ==9994== by 0x26E53C: rb_iseq_eval_main (vm.c:1448) ==9994== by 0x12D3CC: ruby_exec_internal (eval.c:252) ==9994== by 0x12D4DF: ruby_exec_node (eval.c:317) ==9994== Address 0x6e5fbf0 is 0 bytes inside a block of size 568 free'd ==9994== at 0x4C240FD: free (vg_replace_malloc.c:366) ==9994== by 0x596CD5C: fclose@@GLIBC_2.2.5 (iofclose.c:88) ==9994== by 0x156E76: nogvl_fclose (io.c:4048) ==9994== by 0x27AB3D: call_without_gvl (thread.c:1244) ==9994== by 0x27AC6F: rb_thread_call_without_gvl (thread.c:1354) ==9994== by 0x156EBC: maygvl_fclose (io.c:4057) ==9994== by 0x157014: fptr_finalize (io.c:4099) ==9994== by 0x15712B: rb_io_fptr_cleanup (io.c:4130) ==9994== by 0x15740C: rb_io_close (io.c:4221) ==9994== by 0x157464: rb_io_close_m (io.c:4250) ==9994== by 0x258B2E: call_cfunc_0 (vm_insnhelper.c:1354) ==9994== by 0x2596EE: vm_call_cfunc_with_frame (vm_insnhelper.c:1492) ==9994== -e:6:in `readline': closed stdout (IOError) from -e:6:in `
' ==9994== ==9994== HEAP SUMMARY: ==9994== in use at exit: 1,503,865 bytes in 23,682 blocks ==9994== total heap usage: 41,787 allocs, 18,105 frees, 8,853,637 bytes allocated ==9994== ==9994== LEAK SUMMARY: ==9994== definitely lost: 453,958 bytes in 4,422 blocks ==9994== indirectly lost: 696,055 bytes in 12,524 blocks ==9994== possibly lost: 121 bytes in 1 blocks ==9994== still reachable: 353,731 bytes in 6,735 blocks ==9994== suppressed: 0 bytes in 0 blocks ==9994== Rerun with --leak-check=full to see details of leaked memory ==9994== ==9994== For counts of detected and suppressed errors, rerun with: -v ==9994== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4) zsh: exit 1 valgrind ./ruby -rreadline -e % ./ruby -v ruby 2.1.0dev (2013-07-18 trunk 42040) [x86_64-linux] 問題は、readline_s_set_input や readline_s_set_output で、 IO オブジェクトから rb_io_stdio_file で FILE * を入手し、 rl_instream や rl_outstream へ代入していることにあります。 IO#close では rl_instream や rl_outstream に残っていることは気にせず、 FILE * を fclose し、ファイル構造体は開放されるので、 その後で (readline_readline で) fileno(rl_instream) や fileno(rl_outstream) をすると Invalid read になります。 もし、ファイル構造体のあったメモリが OS に返却されていれば SEGV になってもおかしくないでしょう。 rb_io_stdio_file で入手した FILE * はいつ消えてもおかしくないので、 rl_instream や rl_outstream へ代入してとっておくのはよろしくないのではないでしょうか。 ファイル構造体をどうしてもとっておく必要があるなら、おそらく、fd を dup して fdopen で ファイル構造体を新しく作って代入するような方向性がいいんじゃないでしょうか。 もちろん、そうやって dup/fdopen で資源 (新しい fd, ファイル構造体) を確保するなら 開放する手立ても必要でしょう。 readline_readline で errno == EBADF で検査する代わりにもともとの IO オブジェクトが クローズされているかどうかを調べてクローズされていたら開放するとか、 readline_s_set_input や readline_s_set_output で nil を受け付けるとか、 いくつか考えないといけないとは思います。 ---------------------------------------- Bug #8644: valgrind error in a readline test https://bugs.ruby-lang.org/issues/8644#change-40567 Author: akr (Akira Tanaka) Status: Open Priority: Normal Assignee: Category: Target version: ruby -v: ruby 2.1.0dev (2013-07-16 trunk 42006) [x86_64-linux] Backport: 1.9.3: UNKNOWN, 2.0.0: UNKNOWN test-all が readline のところで SEGV することがあったので、 とりあえず valgrind をかけてみたところ、ひとつ問題を指摘されました。 % ./ruby -v ruby 2.1.0dev (2013-07-16 trunk 42006) [x86_64-linux] % valgrind ./ruby -I.ext/x86_64-linux ../../ruby/test/runner.rb ../../ruby/test/readline -n test_closed_outstream ==27651== Memcheck, a memory error detector ==27651== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. ==27651== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info ==27651== Command: ./ruby -I.ext/x86_64-linux ../../ruby/test/runner.rb ../../ruby/test/readline -n test_closed_outstream ==27651== Run options: -n test_closed_outstream # Running tests: [1/1] TestReadline#test_closed_outstream==27651== Invalid read of size 4 ==27651== at 0x597B610: fileno (fileno.c:37) ==27651== by 0x74A3EA6: readline_readline (readline.c:397) ==27651== by 0x390B39: call_cfunc_m1 (vm_insnhelper.c:1348) ==27651== by 0x396943: vm_call_cfunc_with_frame (vm_insnhelper.c:1492) ==27651== by 0x395144: vm_call_cfunc (vm_insnhelper.c:1582) ==27651== by 0x394915: vm_call_method (vm_insnhelper.c:1774) ==27651== by 0x396A84: vm_call_general (vm_insnhelper.c:1925) ==27651== by 0x3724DC: vm_exec_core (insns.def:1017) ==27651== by 0x383DDF: vm_exec (vm.c:1198) ==27651== by 0x389DBD: invoke_block_from_c (vm.c:646) ==27651== by 0x38E299: vm_yield (vm.c:677) ==27651== by 0x380012: rb_yield_0 (vm_eval.c:937) ==27651== Address 0x847a1a0 is 0 bytes inside a block of size 568 free'd ==27651== at 0x4C27D4E: free (vg_replace_malloc.c:427) ==27651== by 0x5977CCC: fclose@@GLIBC_2.2.5 (iofclose.c:88) ==27651== by 0x190F5C: nogvl_fclose (io.c:4048) ==27651== by 0x3A33FC: call_without_gvl (thread.c:1244) ==27651== by 0x3A3532: rb_thread_call_without_gvl (thread.c:1354) ==27651== by 0x190E7C: maygvl_fclose (io.c:4057) ==27651== by 0x190620: fptr_finalize (io.c:4099) ==27651== by 0x16C922: rb_io_fptr_cleanup (io.c:4130) ==27651== by 0x16CAE7: rb_io_close (io.c:4221) ==27651== by 0x17A2F1: rb_io_close_m (io.c:4250) ==27651== by 0x390B62: call_cfunc_0 (vm_insnhelper.c:1354) ==27651== by 0x396943: vm_call_cfunc_with_frame (vm_insnhelper.c:1492) ==27651== Finished tests in 0.521803s, 1.9164 tests/s, 5.7493 assertions/s. 1 tests, 3 assertions, 0 failures, 0 errors, 0 skips ruby -v: ruby 2.1.0dev (2013-07-16 trunk 42006) [x86_64-linux] ==27651== ==27651== HEAP SUMMARY: ==27651== in use at exit: 1,905,554 bytes in 30,452 blocks ==27651== total heap usage: 94,021 allocs, 63,569 frees, 19,535,963 bytes allocated ==27651== ==27651== LEAK SUMMARY: ==27651== definitely lost: 447,671 bytes in 3,643 blocks ==27651== indirectly lost: 979,312 bytes in 17,524 blocks ==27651== possibly lost: 0 bytes in 0 blocks ==27651== still reachable: 478,571 bytes in 9,285 blocks ==27651== suppressed: 0 bytes in 0 blocks ==27651== Rerun with --leak-check=full to see details of leaked memory ==27651== ==27651== For counts of detected and suppressed errors, rerun with: -v ==27651== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4) ちょっとソースを見たところ、fclose した後に fileno しているようですが、 これはよろしくないんじゃないでしょうか。 valgrind で指摘されたのが原因かどうかはわかりませんが、 SEGV したときのログもつけておきます。 (長すぎるので消しました。test_closed_outstream-segv.txt をみてください。) -- http://bugs.ruby-lang.org/