599ed107f1 (ruby_3_4): YJIT: Fix version_map use-after-free from mutable aliasing UB

0 views
Skip to first unread message

Takashi Kokubun

unread,
9:52 AM (11 hours ago) 9:52 AM
to ruby...@g.ruby-lang.org
Takashi Kokubun 2026-07-09 01:21:28 +0000 (Thu, 09 Jul 2026)

New Revision: 599ed107f1

https://github.com/ruby/ruby/commit/599ed107f1

Log:
YJIT: Fix version_map use-after-free from mutable aliasing UB

Multiple YJIT functions created overlapping `&'static mut IseqPayload`
references by calling `get_iseq_payload()` multiple times for the same
iseq. LLVM's noalias optimizations could then cache stale version_map
Vec headers, causing the version_map to point to freed backing storage.

This manifested as crashes in various YJIT operations (block lookup,
GC marking, block removal) that dereference the stale pointer.

Fix by moving `delayed_deallocation` and `get_or_create_version_list`
from free functions (which each call `get_iseq_payload()` internally)
to methods on `IseqPayload` that operate through `&mut self`. This
lets callers obtain a single payload reference and use it for all
operations without creating overlapping mutable borrows.

The three fixed call sites:

1. `rb_yjit_tracing_invalidate_all` (invariants.rs): The loop called
`delayed_deallocation()` which internally called `get_iseq_payload()`,
creating a second `&mut` overlapping with the outer `payload` reference.
Fix: call `payload.delayed_deallocation()` method instead.

2. `add_block_version` (core.rs): Called `get_or_create_version_list()`
then later `get_iseq_payload()` for pages, creating two references
Reply all
Reply to author
Forward
0 new messages