ruby-changes:39358
From: nobu <ko1@a...>
Date: Thu, 30 Jul 2015 10:42:48 +0900 (JST)
Subject: [ruby-changes:39358] nobu:r51439 (trunk): load.c: avoid segfault when 'throw' occurs in the middle of rb_load_file_str
nobu 2015-07-30 10:42:35 +0900 (Thu, 30 Jul 2015) New Revision: 51439 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=51439 Log: load.c: avoid segfault when 'throw' occurs in the middle of rb_load_file_str How can a 'throw' happen while the current thread is reading a Ruby source file from disk and parsing it? It can happen if another thread calls Thread#raise, and passes an Exception object which responds to #exception, and the custom #exception method calls Kernel#throw. In practice, this is most likely to happen if you combine the use of autoload and Timeout.timeout. An extra check is required to avoid a segfault in this case. * load.c (rb_load_internal0): extra check before returning TAG_RAISE when a non-local transfer of control happens while loading and parsing a Ruby source file. [ruby-core:70169] [Bug #11404] Modified files: trunk/ChangeLog trunk/load.c trunk/test/ruby/test_require.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 51438) +++ ChangeLog (revision 51439) @@ -1,3 +1,10 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Thu Jul 30 10:42:27 2015 Alex Dowad <alexinbeijing@g...> + + * load.c (rb_load_internal0): extra check before returning + TAG_RAISE when a non-local transfer of control happens while + loading and parsing a Ruby source file. + [ruby-core:70169] [Bug #11404] + Thu Jul 30 08:48:42 2015 Eric Wong <e@8...> * st.c (find_entry): constify st_table* Index: load.c =================================================================== --- load.c (revision 51438) +++ load.c (revision 51439) @@ -621,7 +621,7 @@ rb_load_internal0(rb_thread_t *th, VALUE https://github.com/ruby/ruby/blob/trunk/load.c#L621 th->top_self = self; th->top_wrapper = wrapper; - if (!loaded && !FIXNUM_P(th->errinfo)) { + if (!loaded && !FIXNUM_P(th->errinfo) && state != TAG_THROW) { /* an error on loading don't include INT2FIX(TAG_FATAL) see r35625 */ return TAG_RAISE; } Index: test/ruby/test_require.rb =================================================================== --- test/ruby/test_require.rb (revision 51438) +++ test/ruby/test_require.rb (revision 51439) @@ -706,4 +706,33 @@ class TestRequire < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_require.rb#L706 END } end unless /mswin|mingw/ =~ RUBY_PLATFORM + + def test_throw_while_loading + Tempfile.create(%w'bug-11404 .rb') do |f| + f.puts 'sleep' + f.close + + assert_separately(["-", f.path], <<-'end;') + path = ARGV[0] + class Error < RuntimeError + def exception(*) + begin + throw :blah + rescue UncaughtThrowError + end + self + end + end + + assert_throw(:blah) do + x = Thread.current + y = Thread.start { + sleep 0.00001 + x.raise Error.new + } + load path + end + end; + end + end end -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/