[pull] master from ruby:master - #1318
Merged
Merged
Conversation
coroutine_transfer() restores target->from after the switch, that is,
after this coroutine is resumed -- by then it may have been resumed by
someone else and target may be freed.
With M:N threads it usually is: a Ruby thread parks by transferring to
its shared native thread's context (thread_sched_wait_running_turn), and
that thread, finding the ready queue empty, retires and frees nt and
nt->nt_context. Over 40 retires in the reproducer below, the number of
threads still parked on the retiring context was a median of 19 (max 61,
zero in four cases); each writes to it when it is next resumed.
sizeof(ucontext_t) is 968, so the write lands in a freed 1008-byte
region, and once that is reused it corrupts the new owner:
free(): invalid next size (fast)
ruby_xfree_sized ... st_free_entries -> rb_st_free_table
ractor_free_all_ports -> ractor_notify_exit -> ractor_atexit
thread_start_func_2 -> co_start -> coroutine_trampoline
AddressSanitizer: heap-use-after-free
WRITE of size 8 in coroutine_transfer (Context.h:97), thread T3
968 bytes inside a 1008-byte region freed in nt_start ->
native_thread_destroy_self, allocated in native_thread_alloc
from is read only by the trampoline, when target starts, so the restore
is dead by the time the switch returns. Skip it under
COROUTINE_TARGET_MAY_BE_FREED, which thread_pthread.c defines; cont.c
and other embedders keep the original behaviour. Only ucontext runs
M:N threads today, but the lifetime rule is not specific to them, so
guard every backend that has the restore.
Fibers never hit this: a suspended fiber still references its target, so
it cannot be collected. A native thread's context is not a Ruby object
and is freed by hand.
Reproducer: clang-20 -O1 --enable-shared --with-coroutine=ucontext,
bootstraptest/test_ractor.rb (the case at line 2603 spawns 2000 Ractors)
four times in parallel on four CPUs: 21 of 48 runs died before, 0 of 48
after. Under ASAN, the isolated case eight at a time: 120 of 120
reported before, 0 of 120 after.
CI: https://github.com/ruby/ruby/actions/runs/31942932752/job/95154613098
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 : )