Project

General

Profile

Actions

Bug #22074

closed

YJIT misaligns locals when there are > 256 local variables

Bug #22074: YJIT misaligns locals when there are > 256 local variables
1

Added by ibrahima (Ibrahim Awwal) 3 days ago. Updated about 7 hours ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
ruby 4.0.4 (2026-05-12 revision b89eb1bcbf) +PRISM [x86_64-linux]
[ruby-core:125543]

Description

Hi! We ran into this issue in production with a large Slim template in a Rails app, but we found that it's reproducible in any function with a lot of local variables. It seems like YJIT tracks local variables for register allocation in an 8-bit integer, so if there are more than 256 local variables in a function, the index overflows and maps back to the wrong variable and causes issues. We reproduced the issue with the released version of Ruby 4.0.4. We have a simple reproduction script and a proposed patch. The reproduction script generates some code with 257 locals and tests that it behaves correctly over multiple iterations (to exercise YJIT since it doesn't kick in right away).

Full disclosure, the code is AI-written, and I don't know Rust, but I do know C and integer overflows, and it seems somewhat reasonable - though I'm not sure if it's the exact right fix.

With YJIT disabled:

$ ruby yjit_many_locals_simple_repro.rb
ruby=ruby 4.0.4 (2026-05-12 revision b89eb1bcbf) +PRISM [x86_64-linux]
yjit=false
iterations=100000
YJIT is not enabled. Re-run with: ruby --yjit yjit_many_locals_simple_repro.rb
iteration=10000
iteration=20000
iteration=30000
iteration=40000
iteration=50000
iteration=60000
iteration=70000
iteration=80000
iteration=90000
iteration=100000
ok iterations=100000

With YJIT enabled

$ ruby --yjit yjit_many_locals_simple_repro.rb
ruby=ruby 4.0.4 (2026-05-12 revision b89eb1bcbf) +YJIT +PRISM [x86_64-linux]
yjit=true
iterations=100000
failed_at=30
yjit_many_locals_simple_repro.rb:259:in 'Object#many_locals': undefined method '+' for an instance of Object (NoMethodError)
        from yjit_many_locals_simple_repro.rb:42:in 'block in <main>'
        from yjit_many_locals_simple_repro.rb:41:in 'Integer#times'
        from yjit_many_locals_simple_repro.rb:41:in '<main>'
yjit_many_locals_simple_repro.rb:259:in 'Object#many_locals': undefined method '+' for an instance of Object (NoMethodError)
        from yjit_many_locals_simple_repro.rb:42:in 'block in <main>'
        from yjit_many_locals_simple_repro.rb:41:in 'Integer#times'
        from yjit_many_locals_simple_repro.rb:41:in '<main>'

Thank you for your consideration!


Files

yjit_many_locals_simple_repro.rb (1.32 KB) yjit_many_locals_simple_repro.rb Script to reproduce the locals issue ibrahima (Ibrahim Awwal), 05/19/2026 10:06 PM

Updated by k0kubun (Takashi Kokubun) about 18 hours ago Actions #2

  • Backport changed from 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN to 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: REQUIRED

Updated by Anonymous about 17 hours ago Actions #3

  • Status changed from Open to Closed

Applied in changeset git|c34cc402a378be826714aae67af5d494107d9814.


YJIT: Fix local register mapping overflow

Previously, local indices greater than 255 were truncated when
converted to RegOpnd::Local. This could make a high-index local alias
a tracked low-index local in the register mapping and produce incorrect
values after compilation.

Cap overflowing indices to MAX_CTX_LOCALS. RegMapping treats that index
as untrackable because it is just outside the tracked local range.

Fixes [Bug #22074].

Co-authored-by: Codex <199175422+chatgpt-connector[bot]@users.noreply.github.com>

Updated by k0kubun (Takashi Kokubun) about 12 hours ago Actions #4 [ruby-core:125560]

  • Backport changed from 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: REQUIRED to 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: DONE

Updated by ibrahima (Ibrahim Awwal) about 7 hours ago Actions #5 [ruby-core:125563]

By the way, we first encountered this bug on Ruby 3.4, so I think it affects at least 3.4, not sure about earlier versions. But the reproduction script should hopefully make it easy to check - I can run it on older versions later.

Updated by byroot (Jean Boussier) about 7 hours ago Actions #6 [ruby-core:125564]

  • Backport changed from 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: DONE to 3.3: DONTNEED, 3.4: REQUIRED, 4.0: DONE

The reproducer doesn't show the bug on 3.3, so marking as DONTNEED. But 3.3 is on security only maintenance anyways, so even if it was impacted, the policy would be WONTFIX.

Actions

Also available in: PDF Atom