From be2a2eea8db2f8c48067711216cd347d029dda06 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Wed, 15 Jul 2026 04:23:56 +0900 Subject: [PATCH] Relax opt_getconstant_path IC assertion under multi-ractor rb_vm_opt_getconstant_path() asserts on an inline constant cache hit that the cached value equals a freshly recomputed constant chain: VM_ASSERT(val == vm_get_ev_const_chain(ec, segments)); On a non-main ractor an IC hit is only allowed for shareable values (IMEMO_CONST_CACHE_SHAREABLE). Reassigning a shareable constant is legal from a non-main ractor, and it invalidates ICs asynchronously via rb_clear_constant_cache_for_id() (ic->entry = NULL) without locking the reader side. So the hit path can load a still-valid, previously-assigned shareable value while another ractor concurrently reassigns the constant; the subsequent recomputation then observes the newer value and the two legitimately differ (a benign TOCTOU). Both values were validly assigned to the constant at some point, so no correctness or memory-safety issue results -- only this debug-only assertion trips. Guard the equality check with rb_multi_ractor_p(), mirroring the existing workaround in vm_cc_invalidate() (vm_callinfo.h). Reproducible with several ractors reassigning the same frozen constant in a loop; NDEBUG builds are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) --- vm_insnhelper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 1abf7dd122a..f7b007d814e 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -6548,7 +6548,11 @@ rb_vm_opt_getconstant_path(rb_execution_context_t *ec, rb_control_frame_t *const if (ice && vm_ic_hit_p(ice, GET_EP())) { val = ice->value; - VM_ASSERT(val == vm_get_ev_const_chain(ec, segments)); + // On a non-main ractor another ractor can concurrently reassign a + // shareable constant (which invalidates ICs asynchronously), so the + // cached-but-still-shareable value may legitimately differ from a + // freshly recomputed chain. Only assert equality when single-ractor. + VM_ASSERT(val == vm_get_ev_const_chain(ec, segments) || rb_multi_ractor_p()); } else { ruby_vm_constant_cache_misses++;