From 84a27934cf53ee36a19c7450622bb0266ffc0e93 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 18 Jul 2026 10:18:24 +1200 Subject: [PATCH] Guard fiber target across `fiber_store`. (#17957) [Bug #22196] --- cont.c | 8 ++++++++ test/ruby/test_fiber.rb | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/cont.c b/cont.c index 252e2ec199..1145a9f64a 100644 --- a/cont.c +++ b/cont.c @@ -2869,6 +2869,13 @@ fiber_switch(rb_fiber_t *fiber, int argc, const VALUE *argv, int kw_splat, rb_fi VM_ASSERT(FIBER_RUNNABLE_P(fiber)); + /* + * Keep the target fiber object alive across fiber_store. The raw + * rb_fiber_t pointer is used after the coroutine switch, and GC may run + * while this C frame is suspended. + */ + VALUE fiber_value = fiber->cont.self; + rb_fiber_t *current_fiber = fiber_current(); VM_ASSERT(!current_fiber->resuming_fiber); @@ -2902,6 +2909,7 @@ fiber_switch(rb_fiber_t *fiber, int argc, const VALUE *argv, int kw_splat, rb_fi } } #endif + RB_GC_GUARD(fiber_value); if (fiber_current()->blocking) { th->blocking += 1; diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb index 6976bd9742..ec82a3a347 100644 --- a/test/ruby/test_fiber.rb +++ b/test/ruby/test_fiber.rb @@ -497,6 +497,40 @@ class TestFiber < Test::Unit::TestCase assert_equal :ok, ret, '[Bug #14642]' end + def test_gc_during_nested_resume_yield + assert_normal_exit <<-'RUBY', '[Bug #22196]', timeout: 10 + parent = child1 = child2 = nil + + parent = Fiber.new do + child1 = Fiber.new do + parent.resume + end + + child2 = Fiber.new do + GC.start + parent.resume + end + + Fiber.yield + + child1 = nil + + Fiber.yield + end + + parent.resume + + child = child1 + child1 = nil + child.resume + child = nil + + child = child2 + child2 = nil + child.resume + RUBY + end + def test_machine_stack_gc assert_normal_exit <<-RUBY, '[Bug #14561]', timeout: 60 enum = Enumerator.new { |y| y << 1 }