Bug #7802
closedRuby crashes when detect is called while executing the ensure callback given to rb_ensure
Description
Conditions:
- rb_ensure(cb, cb_args, ensure, ensure_args) is used from C API
- cb raises an exception
- ensure calls Enumerable#detect and detect finds an element
Sample code:
ensured.c¶
#include "ruby.h"
static VALUE
rubyEnsuredBegin(VALUE object) {
return rb_funcall(object, rb_intern("try_method"), 0);
}
static VALUE
rubyEnsure(VALUE object) {
return rb_funcall(object, rb_intern("ensured_method"), 0);
}
static VALUE
rubyEnsured(VALUE module, VALUE object) {
VALUE result = rb_ensure(
rubyEnsuredBegin, object,
rubyEnsure, object
);
return result;
}
void Init_ensured() {
rb_define_method(rb_mKernel, "ensured", rubyEnsured, 1);
}
test_ensured.rb¶
require_relative 'ensured'
class TestObject
def try_method
raise "error raised in rb_ensure"
end
def ensured_method
# Returning true in the detect block will
# call rb_iter_break() which will cause
# Ruby to crash when the exception resumes
# unwinding the stack
[1].detect { |i| ARGV[0] == 'crash' }
# This line still gets executed
puts 'after detect'
end
end
begin
ensured(TestObject.new)
rescue
puts $!
end
END
$ ruby test_ensured.rb
after detect
error raised in rb_ensure
$ ruby test_ensured.rb crash
after detect
test_ensured.rb: [BUG] Segmentation fault
ruby 1.9.3p385 (2013-02-06 revision 39114) [x86_64-darwin10.8.0]
-- Control frame information -----------------------------------------------
c:0004 p:0030 s:0009 b:0009 l:000398 d:000008 BLOCK
c:0003 p:0047 s:0006 b:0006 l:000398 d:000b18 EVAL test_ensured.rb:20
c:0002 p:---- s:0004 b:0004 l:000003 d:000003 FINISH
c:0001 p:0000 s:0002 b:0002 l:000398 d:000398 TOP
Reproduced on the following system:
$ uname -a
Linux lion 2.6.32-5-686 #1 SMP Sun Sep 23 09:49:36 UTC 2012 i686 GNU/Linux
$ ruby -v
ruby 1.9.3p327 (2012-11-10 revision 37606) [i686-linux]
AND
$ uname -v
Darwin Kernel Version 10.8.0: Tue Jun 7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/RELEASE_I386
$ ruby -v
ruby 1.9.3p385 (2013-02-06 revision 39114) [x86_64-darwin10.8.0]
The use case for the above example is testing a C extension with Mocha (or other mocking library) that uses rb_ensure and does regular rb_funcalls to the Ruby stdlib.