From 5bcc639b341291fe0584d11c6bdd1add29f40087 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 24 Jun 2025 12:33:27 +0200 Subject: [PATCH] Disallow forking from non-main ractor [Bug #17516] `fork(2)` only leave the calling thread alive in the child. Because of this forking from the non-main ractor can easily leave the VM in a corrupted state. It may be possible in the future to carefully allow forking from non-main Ractor, but shot term it's preferable to add this restriction. --- common.mk | 1 + process.c | 5 +++++ test/ruby/test_ractor.rb | 11 +++++++++++ 3 files changed, 17 insertions(+) diff --git a/common.mk b/common.mk index e5a4d34a0a..002f5dcef7 100644 --- a/common.mk +++ b/common.mk @@ -14057,6 +14057,7 @@ process.$(OBJEXT): {$(VPATH)}onigmo.h process.$(OBJEXT): {$(VPATH)}oniguruma.h process.$(OBJEXT): {$(VPATH)}process.c process.$(OBJEXT): {$(VPATH)}ractor.h +process.$(OBJEXT): {$(VPATH)}ractor_core.h process.$(OBJEXT): {$(VPATH)}ruby_assert.h process.$(OBJEXT): {$(VPATH)}ruby_atomic.h process.$(OBJEXT): {$(VPATH)}rubyparser.h diff --git a/process.c b/process.c index 2938411c43..da9ce74027 100644 --- a/process.c +++ b/process.c @@ -114,6 +114,7 @@ int initgroups(const char *, rb_gid_t); #include "ruby/st.h" #include "ruby/thread.h" #include "ruby/util.h" +#include "ractor_core.h" #include "vm_core.h" #include "vm_sync.h" #include "ruby/ractor.h" @@ -4120,6 +4121,10 @@ rb_fork_async_signal_safe(int *status, rb_pid_t rb_fork_ruby(int *status) { + if (UNLIKELY(!rb_ractor_main_p())) { + rb_raise(rb_eRactorIsolationError, "can not fork from non-main Ractors"); + } + struct rb_process_status child = {.status = 0}; rb_pid_t pid; int try_gc = 1, err = 0; diff --git a/test/ruby/test_ractor.rb b/test/ruby/test_ractor.rb index 3fc891da23..97af7e7413 100644 --- a/test/ruby/test_ractor.rb +++ b/test/ruby/test_ractor.rb @@ -99,6 +99,17 @@ class TestRactor < Test::Unit::TestCase RUBY end + def test_fork_raise_isolation_error + assert_ractor(<<~'RUBY') + ractor = Ractor.new do + Process.fork + rescue Ractor::IsolationError => e + e + end + assert_equal Ractor::IsolationError, ractor.value.class + RUBY + end if Process.respond_to?(:fork) + def test_require_raises_and_no_ractor_belonging_issue assert_ractor(<<~'RUBY') require "tempfile"