[ruby-core:125543] [Ruby Bug#22074] YJIT misaligns locals when there are > 256 local variables
Issue #22074 has been reported by ibrahima (Ibrahim Awwal). ---------------------------------------- Bug #22074: YJIT misaligns locals when there are > 256 local variables https://bugs.ruby-lang.org/issues/22074 * Author: ibrahima (Ibrahim Awwal) * Status: Open * ruby -v: ruby 4.0.4 (2026-05-12 revision b89eb1bcbf) +PRISM [x86_64-linux] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- 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) -- https://bugs.ruby-lang.org/
Issue #22074 has been updated by Eregon (Benoit Daloze). Link to the PR: https://github.com/ruby/ruby/pull/17043 ---------------------------------------- Bug #22074: YJIT misaligns locals when there are > 256 local variables https://bugs.ruby-lang.org/issues/22074#change-117377 * Author: ibrahima (Ibrahim Awwal) * Status: Open * ruby -v: ruby 4.0.4 (2026-05-12 revision b89eb1bcbf) +PRISM [x86_64-linux] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- 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) -- https://bugs.ruby-lang.org/
Issue #22074 has been updated by k0kubun (Takashi Kokubun). Backport changed from 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: REQUIRED to 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: DONE ruby_4_0 commit:cc058251160135a76cfbec8bbd83ae114001228f. ---------------------------------------- Bug #22074: YJIT misaligns locals when there are > 256 local variables https://bugs.ruby-lang.org/issues/22074#change-117391 * Author: ibrahima (Ibrahim Awwal) * Status: Closed * ruby -v: ruby 4.0.4 (2026-05-12 revision b89eb1bcbf) +PRISM [x86_64-linux] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: DONE ---------------------------------------- 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) -- https://bugs.ruby-lang.org/
Issue #22074 has been updated by ibrahima (Ibrahim Awwal). 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. ---------------------------------------- Bug #22074: YJIT misaligns locals when there are > 256 local variables https://bugs.ruby-lang.org/issues/22074#change-117393 * Author: ibrahima (Ibrahim Awwal) * Status: Closed * ruby -v: ruby 4.0.4 (2026-05-12 revision b89eb1bcbf) +PRISM [x86_64-linux] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: DONE ---------------------------------------- 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) -- https://bugs.ruby-lang.org/
Issue #22074 has been updated by byroot (Jean Boussier). 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. ---------------------------------------- Bug #22074: YJIT misaligns locals when there are > 256 local variables https://bugs.ruby-lang.org/issues/22074#change-117394 * Author: ibrahima (Ibrahim Awwal) * Status: Closed * ruby -v: ruby 4.0.4 (2026-05-12 revision b89eb1bcbf) +PRISM [x86_64-linux] * Backport: 3.3: DONTNEED, 3.4: REQUIRED, 4.0: DONE ---------------------------------------- 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) -- https://bugs.ruby-lang.org/
participants (4)
-
byroot (Jean Boussier) -
Eregon (Benoit Daloze) -
ibrahima (Ibrahim Awwal) -
k0kubun (Takashi Kokubun)