Oj gem CVE-2026-54901
MEDIUMSeverity by source
CVSS:4.0/AV:N/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
Trigger requires in-process configuration of Oj::Parser with array_class/hash_class and a GC race, so AV:L/AC:H/PR:H; impact is a crash only, hence C:N/I:N/A:L.
Primary rating from Vendor (https://github.com/ohler55/oj).
CVSS VectorVendor: https://github.com/ohler55/oj
CVSS:4.0/AV:N/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::Parser in usual mode does not mark array_class and hash_class references during garbage collection. If GC runs after the class is assigned but before a parse, the class object is reclaimed, leaving the parser holding a dangling VALUE. The subsequent parse call dereferences the freed object, producing a segfault.
Version
- Software: oj gem
- Affected: all versions with
ext/oj/usual.c/ext/oj/parser.c - Latest tested: 3.17.1 (confirmed present)
Details
The parser_mark function in ext/oj/parser.c is registered as the GC mark callback for the parser's TypedData. If array_class (stored as d->array_class in the Usual struct) is not passed to rb_gc_mark, the GC does not know it is referenced and may collect it.
When close_array_class (usual.c:405) later calls rb_funcallv on the collected class VALUE, it accesses freed memory, crashing at RIP: 0x7f... / 0x0000000000000000.
Crash output:
array_class finalized
about to parse
[BUG] Segmentation fault at 0x0000000000000000
close_array_class+0x194 /ext/oj/usual.c:405
parse+0x17b3 /ext/oj/parser.c:715
parser_parse+0x10b /ext/oj/parser.c:1408
RIP: 0x7fd1b46d68b7 RBP: 0x0000000000000000Reproduce
require 'oj'
p = Oj::Parser.new(:usual,
array_class: (ac = Class.new { def <<(_x); end }))
ObjectSpace.define_finalizer(ac, proc { warn 'array_class finalized' })
ac = nil
GC.start(full_mark: true, immediate_sweep: true)
# collect the class
p.parse('[1]')
# segfaultAnalysisAI
Use-after-free in the Oj Ruby JSON gem (≤ 3.17.1) crashes the host process when Oj::Parser is configured in :usual mode with a custom array_class or hash_class. Because parser_mark fails to register those VALUEs with the Ruby GC, the class object can be reclaimed and the next parse() dereferences freed memory, producing a segfault. …
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 | Requires the target Ruby application to instantiate Oj::Parser in :usual mode (Oj::Parser.new(:usual, …)) with the array_class: or hash_class: option set to a class VALUE that has no other live references, and for Ruby's GC to run a marking/sweep cycle between that assignment and the parse() call. … Additional conditions and limiting factors are described in the full assessment. |
| Risk Assessment | No CVSS or EPSS values are supplied and the CVE is not in CISA KEV, so this should be treated as a stability/availability bug rather than a high-severity security issue. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in. |
| Exploit Scenario | An attacker who can influence when Ruby triggers garbage collection between Oj::Parser configuration and a parse call - for example by submitting allocation-heavy input to a long-running web worker that uses Oj::Parser with a custom array_class - can race the GC to free the class VALUE before parse() dereferences it, crashing the worker process and causing repeated denial of service. The GHSA advisory includes a deterministic Ruby reproducer that forces GC with GC.start(full_mark: true, immediate_sweep: true) and reliably segfaults on 3.17.1, so publicly available exploit code exists for the local crash, though no remote weaponization is published. |
| Remediation | Vendor-released patch: upgrade the oj gem to 3.17.3 (or any later release), which is the fixed version per GHSA-vwm4-62gf-x745 (https://github.com/ohler55/oj/security/advisories/GHSA-vwm4-62gf-x745); update Gemfile.lock and rebuild native extensions. … Detailed patch versions, workarounds, and compensating controls in full report. |
Recommended ActionAI
Within 24 hours: Identify all Ruby applications using Oj gem via dependency scanning and code review; prioritize those with custom array_class or hash_class parser configurations. …
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-vwm4-62gf-x745