CVE-2026-54902: Oj: Use-After-Free in Oj::Parser SAJ Long Key Callback
Summary
Oj::Parser in SAJ mode does not protect cached object keys (≥ 35 bytes) from garbage collection. A Ruby callback that triggers GC inside hashend can cause the key string to be reclaimed while the C parser still holds a pointer to it. The subsequent access to the freed string VALUE results in a segfault, confirmed by an RIP pointing to address 0x4242 (a canary-style pattern suggesting control over the freed memory's content).
Version
- Software: oj gem - Affected: all versions with ext/oj/saj2.c / ext/oj/parser.c - Latest tested: 3.17.1 (confirmed present)
Details
Short keys (≤ 34 bytes) are stored inline on the C stack and are safe. Long keys (≥ 35 bytes) are stored as heap-allocated Ruby String objects passed to rbfuncall as the key argument. Between the key being resolved and the callback completing, a GC triggered inside the callback (e.g. GC.start) can collect the key String, leaving a dangling VALUE.
Crash output: longkeytrigger [BUG] Segmentation fault at 0x0000000000004242 closeobject+0x260 /ext/oj/usual.c:405 (calls rbfuncall with freed key) parse+0x11ff /ext/oj/parser.c:693 parserparse+0x145 /ext/oj/parser.c:1408
RIP: 0x7fd1b46d68b7 RDI: 0x0000000000004242 (freed key VALUE) R12: 0x0000000000004242
The freed VALUE 0x4242 shows the attacker-controlled content of the key string was loaded as a pointer — a classic use-after-free indicator.
Reproduce
ruby require 'oj'
class H < Oj::Saj def addvalue(value, key) GC.start(fullmark: true, immediatesweep: true) if key == 'x' end def hashstart(key); end def hashend(key); end end
p = Oj::Parser.new(:saj) p.handler = H.new p.parse('{"' + 'A' 35 + '":{"x":1}}') # long outer key, GC fires on inner key
Other sources
Oj (Optimized JSON) is a JSON parser and Object marshaller packaged as a Ruby gem. Prior to version 3.17.2, is vulnerable to Use-After-Free when in SAJ mode. The Oj::Parser does not protect cached object keys (≥ 35 bytes) from garbage collection, and a Ruby callback that triggers GC inside hashend can cause the key string to be reclaimed while the C parser still holds a pointer to it. The subsequent access to the freed string VALUE results in a segfault, confirmed by an RIP pointing to address 0x4242 (a canary-style pattern suggesting control over the freed memory's content). This issue has been fixed in version 3.17.2.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
rubygems/ojto a version that resolves this vulnerability.Fixed in 3.17.3 - Upgrade
Upgrade
oj gemto a version that resolves this vulnerability.Fixed in 3.17.2
Event History
Frequently Asked Questions
What is the severity of CVE-2026-54902?
CVE-2026-54902 has a risk score of 33, indicating a moderate severity level.
How do I fix CVE-2026-54902?
To fix CVE-2026-54902, update the 'oj' gem to the latest version where the vulnerability has been patched.
What systems are affected by CVE-2026-54902?
CVE-2026-54902 affects applications using the 'oj' gem in SAJ mode that don't adequately handle garbage collection.
What type of vulnerability is CVE-2026-54902?
CVE-2026-54902 is classified as a Use After Free vulnerability.
What are the potential impacts of CVE-2026-54902?
Exploiting CVE-2026-54902 may lead to application crashes or the execution of arbitrary code due to accessing freed memory.