Oj gem CVE-2026-54897
LOWSeverity by source
CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Requires in-process Ruby code that calls the vulnerable iterator with a close-invoking block (AV:L, PR:L, AC:H); reliable memory corruption is hard, crash impact is high (A:H), info/integrity limited (C:L/I:L).
Primary rating from Vendor (https://github.com/ohler55/oj).
CVSS VectorVendor: https://github.com/ohler55/oj
CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Lifecycle Timeline
3DescriptionCVE.org
Summary
Oj::Doc iterators (each_value, each_child, each_leaf) are vulnerable to a heap use-after-free. When a Ruby block yielded during iteration calls doc.close or d.close, the document's heap memory is freed while the C iterator is still running. When control returns from the block, the iterator reads from the freed region, producing a use-after-free accessible from pure Ruby.
Version
- Software: oj gem
- Affected: all versions with
ext/oj/fast.c - Latest tested: 3.17.1 (confirmed present)
Details
The iterators in ext/oj/fast.c follow the pattern:
// fast.c:1505 (doc_each_child)
static VALUE doc_each_child(VALUE self, ...) {
...
while (cur != NULL) {
rb_yield(...); // ← Ruby block executes here
cur = cur->next; // ← cur is now freed if block called close()
}
}rb_yield can invoke arbitrary Ruby code, including calling close() on the Doc or any child node, which calls ruby_sized_xfree on the backing buffer. On return, the C code reads cur->next from the freed region. All three iterators are affected.
ASAN report (each_child variant):
==253632==ERROR: AddressSanitizer: heap-use-after-free on address 0x5210000bd080
READ of size 8 at 0x5210000bd080 thread T0
#0 doc_each_child /ext/oj/fast.c:1505
0x5210000bd080 is located 896 bytes inside of 4064-byte region [0x5210000bcd00, 0x5210000bdce0)
freed by thread T0 here:
#0 free
#1 ruby_sized_xfree (libruby-3.3.so.3.3)All three iterators trigger the same freed region (fd shadow bytes):
0x5210000bd080:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fdReproduce
require 'oj'
# each_child
Oj::Doc.open('[1,2]') { |doc| doc.each_child { |d| d.close } }
# each_value
Oj::Doc.open('[1,2]') { |doc| doc.each_value { |v| doc.close } }
# each_leaf
Oj::Doc.open('[1,[2]]') { |doc| doc.each_leaf { |d| d.close } }AnalysisAI
Heap use-after-free in the Oj Ruby gem's Oj::Doc iterators (each_value, each_child, each_leaf) allows a Ruby block executed during iteration to free the underlying document buffer via doc.close, after which the native C iterator in ext/oj/fast.c dereferences the freed region. The flaw is reachable from pure Ruby and confirmed by an AddressSanitizer report against version 3.17.1, with no public exploit identified at time of analysis but a clear reproducer published in the GHSA advisory. …
Unlock full vulnerability intelligence
- Risk assessment & exploitation conditions
- Attack chain visualization
- Remediation with exact patch versions
- Threat intelligence from 22 sources
- Personal watchlist & email alerts
Free forever · No credit card required
Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata
Vulnerability AssessmentAI
| Exploitation | Exploitation requires that the target Ruby process load the Oj gem (versions before 3.17.3) and invoke one of `Oj::Doc#each_value`, `each_child`, or `each_leaf`, AND that the block passed to the iterator calls `close` on the document or a yielded child node during iteration. … Additional conditions and limiting factors are described in the full assessment. |
| Risk Assessment | No CVSS vector is provided by NVD or the vendor, and the issue is not listed in CISA KEV; EPSS data is not supplied. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in. |
| Exploit Scenario | An application uses `Oj::Doc.open` to stream-parse JSON and exposes the resulting iterator to a callback supplied by a plugin, integration script, or user-defined transformation. That callback - possibly through a generic resource-cleanup helper - invokes `close` on the document or a child, freeing the parse buffer; when the C iterator resumes and dereferences the freed `cur->next`, it crashes or, with heap-grooming primitives reachable from the same Ruby process, could be coaxed into reading attacker-controlled data. … |
| Remediation | Vendor-released patch: upgrade the Oj gem to version 3.17.3 or later (the GHSA advisory at https://github.com/ohler55/oj/security/advisories/GHSA-9ppp-w3g4-fh4q lists `< 3.17.2` as vulnerable and 3.17.3 as fixed). … Detailed patch versions, workarounds, and compensating controls in full report. |
Recommended ActionAI
Within 24 hours: Inventory all Ruby applications using Oj gem ≤3.17.1 and identify those invoking Oj::Doc iteration methods, especially in request-handling paths. …
Sign in for detailed remediation steps and compensating controls.
Threat intelligence, references, and detailed analysis are available after sign-in.
Same weakness CWE-416 – Use After Free
View allSame technique Use After Free
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-9ppp-w3g4-fh4q