Project

General

Profile

Actions

Bug #22335

closed

Stack corruption with >31 keyword args and refined Integer#==

Bug #22335: Stack corruption with >31 keyword args and refined Integer#==

Added by eapache_opslevel (Evan Huus) 2 days ago. Updated 1 day ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:126779]

Description

This is a messy one that originally surfaced as a flaky test in our Rails app, and required a lot of AI-assisted debugging to track down. This bug appears in the latest 4.0.7, though all of the original investigation was done against 3.3.11.

If you have:

  • a method that takes only keyword args
  • has >31 keyword args
  • has two call-time-evaluated default values (e.g. {}) at argument index >=31
  • running in a Ruby process that has refined or redefined Integer#== at any point

Then you get inconsistent stack corruption depending on your process's hash salt.

The mechanism is nasty:

  • In a method with only keyword args, vm_callee_setup_arg() and vm_call_iseq_setup_kwparm_kwarg() call args_setup_kw_parameters() with klocals = argv + kw_param->bits_start - kw_param->num, i.e. above cfp->sp, without first extending cfp->sp over those locals
  • args_setup_kw_parameters() uses a full hash, keyed by integer, for arguments beyond index 31
  • When args_setup_kw_parameters() sets a key in that hash, and that key collides with another in the ar_hint that small hashes use for a linear scan (not uncommon for a tiny hash, but still salt-dependent), it triggers an integer comparison
  • The refinement on Integer#== prevents that comparison from taking the fast path and turns it into a real C frame
  • Which gets pushed onto the stack, when the stack pointer hasn't been extended
  • Thus overwriting the other args and producing garbage

The net effect is that the called method sees garbage values for the overwritten arguments.

I've attached a reproducing script, which is unfortunately complex, as it needs to do some work to intentionally hit the correct hash collision depending on the process's salt. A simpler static version doesn't reproduce reliably enough to be useful. Even this version will occasionally get a hash salt with no collisions and fail to reproduce, though that seems pretty rare on my machine.


Files

repro.rb (1.51 KB) repro.rb Reproduction script eapache_opslevel (Evan Huus), 09/18/2026 03:02 PM
Actions

Also available in: PDF Atom