Project

General

Profile

Bug #11404 » test.rb

Here is code for minimal repro - alexdowad (Alex Dowad), 07/29/2015 08:25 AM

 
# OK things are starting to make sense...
# 1. Call thread.raise on a Thread, passing an exception class which defines 'exception'
# 2. The 'exception' method must 'throw'
# 3. That changes th->errinfo to a special 'object' with NULL class
# It's not really an object, but is used to pass data back to rb_catch through
# a longjmp
# 4. Now, the thread must have been in the middle of rb_require_internal at the time
# 5. rb_require_internal sets a 'tag' -- a target for a longjmp() if an error condition
# occurs
# 6. And furthermore, if th->errinfo is not a fixnum, it assumes that it is valid
# exception data and passes it to rb_exc_raise!
# 7. This only happens if the exception is raised while LOADING AND PARSING the file,
# because before it is actually executed, the 'loading' flag is set!

# The reason Timeout::Error.exception uses 'throw' is to skip back to self.catch
# bypassing any rescue blocks which might catch ANY exception at all

$: << Dir.pwd

class Error < RuntimeError
def exception(*)
begin
throw :blah
rescue UncaughtThrowError
end
self
end
end

catch :blah do
x = Thread.current
y = Thread.start {
sleep 0.00001
x.raise Error.new
}
require 'blah'
end
(2-2/4)