CVE-2026-54502: Verified Reproduction
CVE-2026-54502: Oj Ruby gem stack buffer overflow via large :indent value
CVE-2026-54502 is verified against oj · Ruby. Affected versions: < 3.17.2 (per user); GitHub advisory lists affected < 3.17.2, patched 3.17.3. Fixed in 3.17.3. Vulnerability class: Buffer Overflow. This medium reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00209.
What Is CVE-2026-54502?
CVE-2026-54502 is a medium-severity stack-based buffer overflow in the Oj Ruby gem's Oj.dump, triggered when a caller passes an oversized :indent option. Pruva reproduced it (reproduction REPRO-2026-00209).
CVE-2026-54502 Severity & CVSS Score
CVE-2026-54502 is rated medium severity, with a CVSS base score of 6.3 out of 10.
Medium — meaningful risk under specific conditions. Schedule a fix in the normal cycle.
Affected oj Versions
oj · Ruby versions < 3.17.2 (per user); GitHub advisory lists affected < 3.17.2, patched 3.17.3 are affected.
How to Reproduce CVE-2026-54502
pruva-verify REPRO-2026-00209 curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00209/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-54502
- reached the target end-to-end
- crash observed
- high confidence
- the upstream fix blocks the same trigger
indent: 2147483647
- Oj.dump({a: 1}, indent: INT_MAX)
reproduction_steps.sh How the agent worked
Root Cause and Exploit Chain for CVE-2026-54502
The Oj Ruby gem (ohler55/oj) is vulnerable to a stack-based buffer overflow in versions prior to 3.17.2. When Oj.dump is called with a large :indent option (e.g., INT_MAX), the native fill_indent helper in ext/oj/dump.h multiplies the indentation count by out->indent and calls memset(out->cur, ' ', cnt) without validating that the destination buffer can hold the requested bytes. The stack-allocated output buffer is only a few kilobytes, so a 2 GB memset corrupts the stack and crashes the Ruby interpreter with a SIGSEGV. Commit ec368db ("Fix stack limits (#1014)", released as 3.17.2) mitigates the issue by rejecting :indent values greater than 16 at the option-parsing layer.
- Package/component affected:
ohler55/oj(Optimized JSON gem for Ruby), specifically the C extensionext/oj/dump.cand the inlinefill_indenthelper inext/oj/dump.h. - Affected versions: Prior to 3.17.2 (vulnerable parent commit
4587e87; fix commitec368db). - Risk level and consequences: Medium severity. A developer-controlled
:indentvalue of2147483647causes a deterministic native crash (SIGSEGV) due to stack corruption. In processes that expose JSON serialization to untrusted input, this could be used for denial of service or, with further research, potentially memory corruption exploitation.
Impact Parity
- Disclosed/claimed maximum impact: memory corruption (stack buffer overflow) / crash.
- Reproduced impact from this run: Native SIGSEGV crash in
Oj.dumpon the vulnerable version; the same call is cleanly rejected with anArgumentErroron the fixed version. - Parity:
full— the reproduced crash directly matches the claimed memory-corruption impact. - Not demonstrated: Full arbitrary code execution was not attempted; only the crash/memory-corruption symptom was proven.
Root Cause
ext/oj/dump.h defines an inline function:
inline static void fill_indent(Out out, int cnt) {
if (0 < out->indent) {
cnt *= out->indent;
*out->cur++ = '\n';
memset(out->cur, ' ', cnt);
out->cur += cnt;
}
}
out->indent is populated from the Ruby :indent option in ext/oj/oj.c (parse_options_cb). In the vulnerable code there is no upper bound on the value, so passing indent: 2147483647 makes cnt equal to INT_MAX and memset attempts to write ~2 GB of spaces into the stack-allocated output buffer, causing a stack overflow and SIGSEGV.
Fix commit ec368db ("Fix stack limits (#1014)") introduces MAX_INDENT 16 and raises rb_raise(rb_eArgError, "indent is limited to %d characters.", MAX_INDENT) when the provided indent exceeds that limit. This validation is performed before the value reaches fill_indent, preventing the overflow.
- Fix commit:
ec368dbe936ef0104b782e4b0f67b17d6c7276f7 - Vulnerable commit:
4587e87e23adc9a4163834dc8c9ba9d7206c6501(parent of fix, matches v3.17.1)
Reproduction Steps
- Run
bundle/repro/reproduction_steps.sh. - The script reads
bundle/project_cache_context.jsonand clones the Oj repository from the project cache intobundle/artifacts/oj-vulnandbundle/artifacts/oj-fixed. - It checks out the vulnerable commit (
4587e87) in one copy, builds the C extension, and runs:
This produces a SIGSEGV (exit code 139) and the Ruby interpreter prints a segmentation-fault backtrace.Oj.dump({a: 1}, indent: 2147483647) - It checks out the fixed commit (
ec368db) in the second copy, builds the C extension, and runs the same Ruby call. The fixed version raises anArgumentError:indent is limited to 16 characters. - The script compares the two outcomes and writes
bundle/repro/runtime_manifest.jsonandbundle/repro/validation_verdict.json.
Expected evidence of reproduction
bundle/logs/vulnerable.log: contains[BUG] Segmentation fault at ...and the Ruby/C backtrace.bundle/logs/fixed.log: containsArgumentError: indent is limited to 16 characters.bundle/logs/reproduction_steps.log: contains the full build/test output and the finalCONFIRMEDline.
Evidence
Environment
- Ruby 3.3.8 (x86_64-linux-gnu)
- Oj vulnerable commit
4587e87(VERSION 3.17.1) - Oj fixed commit
ec368db(VERSION 3.17.2) - C extension built directly with
extconf.rb+makein each checkout
Key excerpts
Vulnerable run (bundle/logs/vulnerable.log):
-e:1: [BUG] Segmentation fault at 0x00007ffc5049e000
ruby 3.3.8 (2025-04-09 revision b200bad6cd) [x86_64-linux-gnu]
-- Control frame information -----------------------------------------------
c:0003 p:---- s:0012 e:000011 CFUNC :dump
...
-- Machine register context ------------------------------------------------
...
RDX: 0x000000007fffffff
...
The RDX register holds 0x7fffffff (INT_MAX), matching the requested indent size.
Fixed run (bundle/logs/fixed.log):
-e:1:in `dump': indent is limited to 16 characters. (ArgumentError)
require 'oj'; puts Oj::VERSION; Oj.dump({a: 1}, indent: 2147483647); puts 'no crash'
^^^^^^^^^^^^^^^^^^^^^^^^^^
from -e:1:in `<main>'
3.17.2
Driver log (bundle/logs/reproduction_steps.log):
VULN_RESULT=0
FIXED_RESULT=1
CONFIRMED: vulnerable version crashes with SIGSEGV, fixed version does not.
Recommendations / Next Steps
- Suggested fix: Apply the upstream patch from
ec368dband enforce a maximum:indentvalue (currently 16) at the option-parsing layer, before any native buffer operation. Any location that accepts user-provided indentation settings should validate the value. - Upgrade guidance: Upgrade to Oj 3.17.2 or later. The vulnerable behavior is fixed by the upstream validation.
- Testing recommendations: Add regression tests that call
Oj.dumpwithindent: 2147483647and expect anArgumentError. Also test with a variety of nested objects/arrays and negative/edge-case indent values to ensure no other path reachesfill_indentwith an unbounded size.
Additional Notes
- Idempotency: The script was executed twice successfully from a clean state and from a state where the artifact clones already existed. Both runs produced the same SIGSEGV on the vulnerable build and
ArgumentErroron the fixed build, then exited with code 0 and wrote the required runtime manifest and verdict. - Edge cases / limitations: The reproduction uses the exact Ruby API call named in the ticket (
Oj.dump(..., indent: INT_MAX)). The crash is a native SIGSEGV, not a sanitizer report; no ASAN/UBSAN build was used, so the primary oracle is the process exit status and the Ruby interpreter's segmentation-fault backtrace.
CVE-2026-54502 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Artifacts and Evidence for CVE-2026-54502
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-54502
Upgrade oj · Ruby to 3.17.3 or later.
FAQ: CVE-2026-54502
How does the Oj :indent stack overflow crash occur?
Which versions of Oj are affected by CVE-2026-54502, and where is it fixed?
How severe is CVE-2026-54502?
How can I reproduce CVE-2026-54502?
References for CVE-2026-54502
Authoritative sources for CVE-2026-54502 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.