Skip to main content

Oj gem CVE-2026-54901

MEDIUM
Use After Free (CWE-416)
2026-06-19 https://github.com/ohler55/oj GHSA-vwm4-62gf-x745
6.3
CVSS 4.0 · Vendor: https://github.com/ohler55/oj
Share

Severity by source

Vendor (https://github.com/ohler55/oj) PRIMARY
6.3 MEDIUM
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
vuln.today AI
1.9 LOW

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.

3.1 AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:L
4.0 AV:L/AC:H/AT:N/PR:H/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N
Red Hat
MEDIUM
qualitative

Primary rating from Vendor (https://github.com/ohler55/oj).

CVSS VectorVendor: https://github.com/ohler55/oj

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
X

Lifecycle Timeline

3
CVSS changed
Jul 01, 2026 - 00:22 NVD
6.3 (MEDIUM)
Source Code Evidence Fetched
Jun 19, 2026 - 21:34 vuln.today
Analysis Generated
Jun 19, 2026 - 21:34 vuln.today

DescriptionCVE.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: 0x0000000000000000

Reproduce

ruby
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]')
# segfault

AnalysisAI

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. A reproducer is published in the GHSA advisory; there is no public exploit identified for remote use and the issue is not listed in CISA KEV.

Technical ContextAI

Oj is a widely deployed native-extension JSON parser for Ruby (pkg:rubygems/oj). The defect is in the C extension files ext/oj/parser.c and ext/oj/usual.c: parser_mark is registered as the TypedData GC mark callback for Oj::Parser, but it omits rb_gc_mark calls for d->array_class and d->hash_class stored in the Usual struct. This is a textbook CWE-416 use-after-free - the Ruby GC is unaware these references are live, sweeps the class, and close_array_class (usual.c:405) then invokes rb_funcallv on the dangling VALUE during parse (parser.c:715, parser.c:1408).

RemediationAI

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. If immediate upgrade is not possible, the practical workaround is to stop using Oj::Parser in :usual mode with the array_class:/hash_class: options and instead rely on Oj.load or Oj::Parser without custom class hooks, accepting the side effect that downstream code receiving Array/Hash subclasses will get plain Array/Hash and may need adjustment. Holding a long-lived strong reference (for example a constant or instance variable on the parser owner) to the class objects passed to array_class/hash_class also prevents GC from collecting them, at the cost of pinning those classes for the parser's lifetime.

Vendor StatusVendor

Share

CVE-2026-54901 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy