TL;DR

A bug in Ruby's Array#pack method can make the function read memory past an allocated string buffer. The issue stems from signed/unsigned handling of repeat counts and has been present in Ruby releases back to at least 2002; follow the fix in PR #15763.

What happened

A researcher revisiting integer-handling code in the MRI (Matz's Ruby Interpreter) found a memory disclosure vulnerability in Array#pack. The method interprets a template string of directives and optional repeat counts to produce binary data. Numeric repeat parsing uses an unsigned conversion but stores the result into a signed long, so very large unsigned counts become negative when stored. One pack directive, 'X', moves the output position backward; supplying a negative repeat to that directive causes the implementation to enlarge the output buffer instead of shrinking it. That unexpected growth can cause bytes beyond the original string contents to be returned, leaking memory from the process. Attempts to expand beyond the buffer capacity trigger an internal guard that aborts with a buffer-overflow bug report; by choosing input string lengths that align with the runtime's allocation rounding, it is possible to maximize leaked data without immediately hitting the guard.

Why it matters

  • The flaw allows a Ruby process to disclose memory contents beyond a string buffer, which can reveal sensitive in-process data.
  • The underlying cause is a subtle signed-vs-unsigned conversion, a class of bug that can persist across many releases and codebases.
  • The vulnerable API, Array#pack, is part of Ruby's standard library and exists in many applications and tools that use binary packing.
  • While the specific directive and argument control required limit exploitation scenarios, the bug's long presence increases the risk of unnoticed exposure in deployed systems.

Key facts

  • The vulnerability is in Ruby's Array#pack implementation (ruby/pack.c).
  • Repeat counts in the pack template are parsed with an unsigned conversion but stored into a signed long, allowing negative values.
  • The 'X' pack directive (which backs up the output position) can be given a negative repeat to enlarge the output buffer and expose adjacent memory.
  • Demonstrations showed both leaked bytes appended to output and situations that trigger an internal 'probable buffer overflow' abort.
  • The issue affects Ruby 4.0.0 and earlier, and the researcher says it likely dates back to Ruby 1.6.7 (2002).
  • A guard in rb_str_set_len prevents unbounded leakage by aborting if the requested length exceeds the buffer capacity, which is rounded to a power of two.
  • By choosing an input string length equal to a power of two, an attacker controlling the packed string can maximize the amount of memory disclosed while avoiding the guard.
  • The researcher notes Array#pack is rarely used in typical Ruby applications and that callers rarely accept attacker-controlled templates or arguments.
  • Progress on a fix is being tracked in pull request PR #15763.

What to watch next

  • Progress and status of the fix in PR #15763 (follow the linked pull request).
  • Whether a coordinated security advisory or CVE is issued for this vulnerability (not confirmed in the source).
  • Reports of exploitation in the wild or additional vectors that give attackers control over pack templates or arguments (not confirmed in the source).

Quick glossary

  • Array#pack: A Ruby instance method that converts array elements into a binary string according to a template of directives and repeat counts.
  • Directive: A character in a pack template that specifies how to encode or interpret data (for example, hex or base64) and may be followed by a repeat count.
  • Buffer overflow / memory disclosure: An error where code reads from or writes to memory outside the intended bounds of a buffer; disclosure refers to unintended reading of adjacent memory.
  • Signed vs unsigned integer: Signed integers can represent negative values, while unsigned integers represent only non-negative values; converting between them can change numeric meaning if sizes differ.

Reader FAQ

Which Ruby versions are affected?
The researcher reports Ruby 4.0.0 and earlier are affected and believes the issue goes back to Ruby 1.6.7 (2002).

Is there a fix available?
Follow the progress of a proposed fix in PR #15763; whether it has been merged or released is not confirmed in the source.

Can this be exploited remotely to compromise systems?
The source says Array#pack is rarely used and attacker control over the method's argument is uncommon, limiting exploitability, but no definitive assessment of remote exploitability is provided.

Does the bug allow arbitrary memory read or write?
The observed behavior is memory disclosure (out-of-bounds reads) and process crashes; an ability to perform arbitrary writes is not confirmed in the source.

Ruby Array Pack Bleed Luke Jahnke 28 December 2025 With the release of Ruby 4.0.0 on Christmas, I decided to revisit integer handling bugs within Ruby MRI, the canonical implementation…

Sources

Related posts

By

Leave a Reply

Your email address will not be published. Required fields are marked *