Use rb_current_ec_noinline in assertions

When doing a coroutine transfer from one thread to another, there's a
risk that the compiler will reuse an address from TLS before the
transfer to the new thread.

These VM assertions are all in places we would not otherwise be reading
from TLS, but using the value of `ec` or `cr` passed in. Switching these
to test against rb_current_ec_noinline() instead ensures there isn't an
optimization applied to how we read ruby_current_ec.

Currently it seems we were hitting this on LLVM 18 specifically, but I
don't know of any reason other versions wouldn't have the same issue.
This commit is contained in:
John Hawthorn 2025-04-30 14:17:48 -07:00
parent 73fdd90315
commit 7866e124a8
Notes: git 2025-04-30 22:37:13 +00:00
2 changed files with 7 additions and 5 deletions

View File

@ -39,7 +39,8 @@ static void
ASSERT_ractor_unlocking(rb_ractor_t *r)
{
#if RACTOR_CHECK_MODE > 0
if (rb_current_execution_context(false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) {
const rb_execution_context_t *ec = rb_current_ec_noinline();
if (ec != NULL && r->sync.locked_by == rb_ractor_self(rb_ec_ractor_ptr(ec))) {
rb_bug("recursive ractor locking");
}
#endif
@ -49,7 +50,8 @@ static void
ASSERT_ractor_locking(rb_ractor_t *r)
{
#if RACTOR_CHECK_MODE > 0
if (rb_current_execution_context(false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) {
const rb_execution_context_t *ec = rb_current_ec_noinline();
if (ec != NULL && r->sync.locked_by != rb_ractor_self(rb_ec_ractor_ptr(ec))) {
rp(r->sync.locked_by);
rb_bug("ractor lock is not acquired.");
}
@ -77,7 +79,7 @@ ractor_lock(rb_ractor_t *r, const char *file, int line)
static void
ractor_lock_self(rb_ractor_t *cr, const char *file, int line)
{
VM_ASSERT(cr == GET_RACTOR());
VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline()));
#if RACTOR_CHECK_MODE > 0
VM_ASSERT(cr->sync.locked_by != cr->pub.self);
#endif
@ -99,7 +101,7 @@ ractor_unlock(rb_ractor_t *r, const char *file, int line)
static void
ractor_unlock_self(rb_ractor_t *cr, const char *file, int line)
{
VM_ASSERT(cr == GET_RACTOR());
VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline()));
#if RACTOR_CHECK_MODE > 0
VM_ASSERT(cr->sync.locked_by == cr->pub.self);
#endif

View File

@ -2118,7 +2118,7 @@ rb_vm_check_ints(rb_execution_context_t *ec)
VM_ASSERT(ruby_assert_critical_section_entered == 0);
#endif
VM_ASSERT(ec == GET_EC());
VM_ASSERT(ec == rb_current_ec_noinline());
if (UNLIKELY(RUBY_VM_INTERRUPTED_ANY(ec))) {
rb_threadptr_execute_interrupts(rb_ec_thread_ptr(ec), 0);