CVE-2026-54899: Oj: Use-After-Free in Oj::Parser Symbol Key Cache Toggle
Summary
Disabling symbolkeys on a reused Oj::Parser instance triggers a heap use-after-free. When symbolkeys is toggled from true to false, optsymbolkeysset frees the internal key cache (cachefree) but does not clear the pointer. The next parse call reads from the freed cache via cacheintern, producing a use-after-free.
Version
- Software: oj gem - Affected: all versions with ext/oj/usual.c - Latest tested: 3.17.1 (confirmed present)
Details
ext/oj/usual.c, optsymbolkeysset:
c // usual.c:1043–1051 if (symbolkeys) { d->keycache = cachecreate(...); // allocate } else { cachefree(d->keycache); // free — but d->keycache pointer not NULLed }
On the next parse call, cachekey → cacheintern reads from d->keycache which now points to freed memory.
ASAN report: ==145265==ERROR: AddressSanitizer: heap-use-after-free on address 0x50b00001a318 READ of size 8 at 0x50b00001a318 thread T0 #0 cacheintern /ext/oj/cache.c:328 #1 cachekey /ext/oj/usual.c:161 #2 closeobject /ext/oj/usual.c:285 #3 parse /ext/oj/parser.c:693 #4 parserparse /ext/oj/parser.c:1408 freed by thread T0 here: #0 free #1 cachefree /ext/oj/cache.c:277 #2 optsymbolkeysset /ext/oj/usual.c:1051 #3 option /ext/oj/usual.c:1111 #4 parsermissing /ext/oj/parser.c:1362 0x50b00001a318 is 40 bytes inside freed 112-byte region [fd]fd fd fd fd fd fd fd
Reproduce
ruby require 'oj' p = Oj::Parser.new(:usual, symbolkeys: true) p.symbolkeys = false # frees cache without nulling pointer p.parse('{"attacker":1}') # UAF: reads freed cache
Other sources
Oj (Optimized JSON) is a JSON parser and Object marshaller packaged as a Ruby gem. Prior to version 3.17.2, disabling symbolkeys on a reused Oj::Parser instance triggers a heap use-after-free. When symbolkeys is toggled from true to false, optsymbolkeysset frees the internal key cache (cachefree) but does not clear the pointer. The next parse call reads from the freed cache via cacheintern, producing a use-after-free. 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 - Configuration
If you must run a version prior to 3.17.2, avoid toggling Oj::Parser#symbol_keys from true to false on a reused instance (this triggers the heap use-after-free in ext/oj/usual.c:opt_symbol_keys_set). Create a new Oj::Parser instance with symbol_keys set to the desired value instead of reusing the same instance across the toggle.
Oj::Parser symbol_keys = false
Event History
Frequently Asked Questions
What application behavior is required to trigger this issue?
An application must reuse the same Oj::Parser instance, enable symbol_keys, then disable symbol_keys, and call parse again. The subsequent parse can read the freed internal key cache.
Is an unauthenticated remote attacker able to trigger it directly?
The CVSS vector indicates no privileges or user interaction are required and network reachability, but exploitation still depends on an application exposing a request path that performs the affected parser reuse and symbol_keys toggle sequence.
Which Oj versions should be investigated?
The issue is described as affecting all oj gem versions containing ext/oj/usual.c. It was confirmed present in version 3.17.1.
What can be done if an update cannot be applied immediately?
Avoid toggling symbol_keys from true to false on a reused Oj::Parser instance. Use a fresh parser instance after changing this setting, or keep the setting consistent for the parser's lifetime.
How can teams determine whether their code is exposed?
Review uses of Oj::Parser for parser instance reuse and calls that change symbol_keys. The vulnerable sequence is symbol_keys set to true, later set to false, followed by another parse on that same instance.