Project

General

Profile

Actions

Bug #22260

closed

Ruby::Box: SIGSEGV when calling a method inside an isolated Proc defined at class/module body (RUBY_BOX=1)

Bug #22260: Ruby::Box: SIGSEGV when calling a method inside an isolated Proc defined at class/module body (RUBY_BOX=1)

Added by zonuexe (Kenta USAMI) 28 days ago. Updated 11 days ago.

Status:
Closed
Target version:
-
ruby -v:
ruby 4.1.0dev (2026-08-24T10:31:23Z master 042e2bfd39) +PRISM [arm64-darwin25]
[ruby-core:126481]

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_slowpathcallable_method_entry_or_negativesearch_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

    else {
        ep[VM_ENV_DATA_INDEX_SPECVAL] = VM_BLOCK_HANDLER_NONE;
    }

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.

Updated by mame (Yusuke Endoh) 13 days ago Actions #1

  • Status changed from Open to Assigned
  • Assignee set to ko1 (Koichi Sasada)

Updated by zonuexe (Kenta USAMI) 11 days ago Actions #2

  • Status changed from Assigned to Closed

Applied in changeset git|a4ad8e461a1c8bd26abef6a602e649b3905159b3.


Preserve the box of a TOP/CLASS env in isolated procs

env_copy() overwrote the SPECVAL slot of every local env with
VM_BLOCK_HANDLER_NONE. For a TOP/CLASS frame's env that slot stores
the box (VM_ENV_BOX), not a block handler, so a proc defined at a
class/module body and isolated by Ractor.make_shareable lost its box
while keeping its frame type. Any method call inside such a proc then
made rb_current_box() return NULL via current_box_on_cfp(), and the
classext lookup dereferenced the NULL box:

RUBY_BOX=1 ruby -e
'module M; L = Ractor.make_shareable(->(*a){ Rational(*a) }); end;
p M::L.call(3, 4)'

=> SIGSEGV at 0x0 in rb_vm_search_method_slowpath

Copy the SPECVAL slot through instead when the source env belongs to
a TOP/CLASS frame, so the isolated proc keeps resolving methods in
its defining box.

[Bug #22260]

Actions

Also available in: PDF Atom