Skip to content

[pull] master from ruby:master - #1356

Merged
pull[bot] merged 5 commits into
turkdevops:masterfrom
ruby:master
Aug 28, 2026
Merged

[pull] master from ruby:master#1356
pull[bot] merged 5 commits into
turkdevops:masterfrom
ruby:master

Conversation

@pull

@pull pull Bot commented Aug 28, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

nobu and others added 5 commits August 28, 2026 10:49
Keep complete dependency rules in upstream gems while storing only source
mappings in the Ruby repository.
Keep generated rules even when manual rules cover them so source
mappings can round-trip between default gems and ruby/ruby.
test_global_variables called nonexistent assert_not_include?, raising
NoMethodError when the suite runs with RUBY_BOX=1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
coroutine_thread_terminated() leaves the Ractor's living set before handing
the scheduler slot over, then keeps using th for the rest of the function:
thread_sched_to_dead_common() reads th->nt and deregisters th from the
barrier, and the epilogue then stashes th->nt and clears th->sched.context.
Off the set th is unreachable, and once deregistered no barrier waits for
it, so a sweep in another Ractor can free it mid-epilogue.  The epilogue
reads freed memory, and co_start() dereferences the NULL it finds in
tctx->nt -- SEGV at 0xc8, the offset of dead_co in struct rb_native_thread.

The removal cannot simply move after the handoff: the GC's root scan walks
r->threads.set without the Ractor lock (relying on the barrier), so the
unlink must stay where no walker can run -- while th is still registered
and before a successor is designated.  Nor can the th accesses move before
the removal: deregistration itself writes th's running-list node.

Instead, keep the removal where it is and give the dying thread an explicit
root: r->threads.dying_th, marked by the root scan exactly like a set
member, set just before the removal and cleared inside the scheduler-lock
section after the epilogue's last use of th.  Clearing under the lock
serialises the epilogue against its successor -- even a dedicated one woken
by to_dead_common() first runs thread_sched_to_running(), which takes the
same lock -- so successive epilogues cannot overwrite each other's slot,
and the successor cannot publish threads.running_ec before the dying
thread has cleared it.  Nothing reads th after the clear: the final unlock
avoids the debug-logging wrapper (which reads th->serial), and whether the
designated successor needs the Ractor enqueued is decided while still
holding the lock, since a dedicated successor may run -- and die, freeing
itself -- the moment the lock is released.  The atfork paths reset the slot
so a fork taken mid-epilogue cannot leak a stale root into the child.  The
slot is accessed with atomic ops: the root scan may read it while another
Ractor's dying thread writes it.

Also initialise tctx->nt, which native_thread_create_shared() left holding
whatever ruby_xmalloc() returned.

Not addressed, pre-existing on master: rb_postponed_job_trigger_for_ractor()
can copy threads.running_ec and dereference it arbitrarily later (the new
root narrows this but cannot protect an already-copied pointer), and a
freshly created dedicated native thread publishes its EC via
ruby_thread_set_native() before taking the scheduler lock.

Two Ractors looping over Thread.new and GC.start crash master in about
three seconds (19 of 20 runs); an ASAN build reports the heap-use-after-free
directly, at the read of th->nt in coroutine_thread_terminated(), freed by
gc_sweep via rb_thread_free_body.  With this change ASAN is clean (0/10 vs
7/10 interleaved), as are the reproducer, s7_nested, both branches of the
epilogue under stress, a Ractor/GC soak, btest and test-all on release and
RUBY_DEBUG=1 builds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pull pull Bot locked and limited conversation to collaborators Aug 28, 2026
@pull pull Bot added the ⤵️ pull label Aug 28, 2026
@pull
pull Bot merged commit ed55eda into turkdevops:master Aug 28, 2026
1 of 3 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants