Bug #22260
openRuby::Box: SIGSEGV when calling a method inside an isolated Proc defined at class/module body (RUBY_BOX=1)
Description
Summary¶
Under RUBY_BOX=1, any method call inside a Proc isolated by Ractor.make_shareable (or Proc#isolate) and defined at a class/module body crashes the VM:
$ RUBY_BOX=1 ruby -e 'module M; L = Ractor.make_shareable(->(*a){ Rational(*a) }); end; p M::L.call(3, 4)'
-e:1: [BUG] Segmentation fault at 0x0000000000000000
$ ruby -e 'module M; L = Ractor.make_shareable(->(*a){ Rational(*a) }); end; p M::L.call(3, 4)'
(3/4)
The Proc body does not matter (a.to_s, 1.zero?, String.name all crash). A Proc defined inside a method body works. No user sub-boxes are involved.
Backtrace: rb_vm_search_method_slowpath → callable_method_entry_or_negative → search_method0, faulting in RCLASS_EXT_TABLE_LOOKUP_INTERNAL with box == NULL (box->box_object sits at offset 0 of rb_box_t, hence address 0x0).
Root cause¶
A TOP/CLASS frame's env stores its box pointer in the SPECVAL slot; that is what VM_ENV_BOX() reads and rb_current_box() returns via current_box_on_cfp(). env_copy() (vm.c, called from proc_isolate_env()) rebuilds a local env with
so the copy keeps its frame type but loses its box. The first method lookup inside the isolated Proc then dereferences the NULL box.
Proposed patch¶
Copy the SPECVAL slot through for TOP/CLASS envs (patch attached, with a regression test in test/ruby/test_box.rb):
else if (VM_ENV_BOXED_P(src_ep)) {
ep[VM_ENV_DATA_INDEX_SPECVAL] = src_ep[VM_ENV_DATA_INDEX_SPECVAL];
}
On master (042e2bfd39) the new test segfaults before the patch and passes after it; test_box.rb, test_proc.rb and test_ractor.rb stay green (288 tests, 0 failures).
Context¶
Hit by the Rigor static analyzer, which calls Ractor.make_shareabled module-scope lambdas from worker Ractors. With the patch, analyzing the whole Redmine codebase under RUBY_BOX=1 completes.
No data to display