🛡️ CVE-2026-71847 — json
Description
Ruby JSON: JSON::ResumableParser#partial_value dereferences a freed input buffer and crashes on truncated duplicate-key streams
Summary
Ruby's JSON native C extension clears the consumed JSON::ResumableParser input buffer but leaves state.start, state.cursor, and state.end pointing into released storage.
When partial_value reconstructs an incomplete object containing duplicate keys, the duplicate-key warning path calls cursor_position, which dereferences those stale pointers. This results in a heap-use-after-free and can terminate the Ruby process.
An attacker who can supply JSON stream data to an application using JSON::ResumableParser may cause process termination when the application calls partial_value on incomplete attacker-controlled input containing duplicate object keys.
The issue was reproduced in the native C extension from the official RubyGems releases:
- JSON 2.20.0
- JSON 2.21.0
- JSON 2.21.1
The attached evidence demonstrates:
- an AddressSanitizer-confirmed heap-use-after-free;
- a native
SIGSEGVusing the official JSON 2.21.1 RubyGem; - an end-to-end loopback TCP attacker/victim reproduction;
- four differential controls;
- successful execution after applying a tested patch control.
This was originally reported privately through Ruby's HackerOne program as report #3867755. A Ruby maintainer independently confirmed reproduction of the ASan failure and requested that further coordination continue through this private advisory.
No code execution or information disclosure is claimed.
Details
The affected source is:
```text
ext/json/ext/parser/parser.c
```
The vulnerable sequence in JSON 2.21.1 is:
1. cResumableParser_parse reaches the end of the current input buffer.
2. It calls json_str_clear(parser->buffer).
3. It sets parser->buffer = Qfalse.
4. The parser-state pointers into the released buffer are not reset.
5. partial_value makes a shallow copy of the parser state.
6. Reconstructing an incomplete object containing duplicate keys reaches the duplicate-key warning path.
7. cursor_position walks through the stale input pointers and reads released memory.
Relevant source locations:
- Buffer release:
https://github.com/ruby/json/blob/fd61def38b9bb859fee7eec8e7d3143600e5b347/ext/json/ext/parser/parser.c#L2562-L2569
- Parser-state copy:
https://github.com/ruby/json/blob/fd61def38b9bb859fee7eec8e7d3143600e5b347/ext/json/ext/parser/parser.c#L2647-L2654
- Stale-pointer read in
cursor_position:
https://github.com/ruby/json/blob/fd61def38b9bb859fee7eec8e7d3143600e5b347/ext/json/ext/parser/parser.c#L590-L628
- Duplicate-key handling path:
https://github.com/ruby/json/blob/fd61def38b9bb859fee7eec8e7d3143600e5b347/ext/json/ext/parser/parser.c#L1196-L1255
When input is supplied to the resumable parser, the parser state stores direct pointers into the backing Ruby string:
```c
RSTRING_GETMEM(parser->buffer, start, len);
parser->state.start = start;
parser->state.end = start + len;
parser->state.cursor = parser->state.start + offset;
```
After the current buffer has been consumed, cResumableParser_parse clears the string and removes the parser's reference to it:
```c
if (eos(&parser->state)) {
json_str_clear(parser->buffer);
parser->buffer = Qfalse;
}
```
This path does not invalidate or replace:
```text
parser->state.start
parser->state.cursor
parser->state.end
```
JSON::ResumableParser#partial_value subsequently makes a shallow copy of the parser structure:
```c
JSON_ResumableParser *original_parser = cResumableParser_get(self);
JSON_ResumableParser parser = *original_parser;
```
When the partial object contains duplicate keys, reconstruction follows this call path:
```text
cResumableParser_partial_value_body
-> json_decode_object
-> json_on_duplicate_key
-> emit_duplicate_key_warning
-> emit_parse_warning
-> cursor_position
```
cursor_position then reads through pointers that may refer to released storage.
AddressSanitizer reports:
```text
ERROR: AddressSanitizer: heap-use-after-free
cursor_position at parser.c:604
freed by cResumableParser_parse at parser.c:2567
```
The reproducer follows the normal resumable-parser API sequence:
```ruby
parser << chunk
parser.parse
parser << next_chunk
parser.parse
parser.partial_value
```
The issue does not require:
- an application-defined callback;
- explicit garbage collection;
- parser reentrancy;
- custom parser options;
- an attacker-supplied Ruby object;
- manual modification of native parser state.
The release-build crash reproduced on JSON 2.20.0, 2.21.0, and 2.21.1.
This report covers the native C-extension implementation. The separate Java-platform implementation was not tested and is not claimed to be affected.
PoC
The complete evidence bundle is attached as:
```text
ruby-json-resumable-partial-value-uaf-evidence-20260716.zip
```
SHA-256:
```text
07bf8d47b115e45d6145d0447ab6c1c0255e4a7e9b2fb55c3c9a0e24406134ac
```
Requirements
- L
Weakness class
CVE-2026-71847 is classified as CWE-416: Use After Free. Memory is used after being released, so its contents may already belong to something else.
Affected software
CVE-2026-71847 is recorded against 2 packages.
- json (from 2.20.0 up to 2.21.2)
- unknown
Timeline and source
Published on 7 August 2026 and last revised on 8 August 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.
References
Details
Affected Packages
| Software | From version | Fixed in |
|---|---|---|
| json | 2.20.0 | 2.21.2 |
| unknown | — | — |
References
Similar Threats
- Unknown MAL-2026-10652
- Low CVE-2026-54696
- Medium CVE-2026-9673
- High CVE-2026-33210
- Unknown MAL-2025-187621
More CVE 2026 advisories
Browse all of CVE 2026 in the advisory index.
Free Vulnerability Check
Is your site affected by CVE-2026-71847?
BotEraser helps you identify potentially vulnerable plugins and themes by checking your installation against CVE-2026-71847 and other known CVE records.
Scan My Site Free →No credit card required · Results in minutes
ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.