← Back to Research Index

Jackson-core: Number Length Constraint Bypass in Async Parsers Leads to Denial of Service (DoS)

Vulnerability Research | February 18, 2026
CVSS 4.0 Score 8.7 (High)
CWE Identification CWE-770
CVSS Vector String 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
Affected Versions jackson-core 3.x (up to 3.1.0-SNAPSHOT)
Fixed In jackson-core 3.1.0

Summary

A significant security oversight was identified in the asynchronous JSON parser of jackson-core. While the library implements a maxNumberLength constraint (default: 1,000 characters) to mitigate resource exhaustion attacks, this constraint is only enforced in the synchronous parsing path. The non-blocking (async) implementation bypasses the validation layer entirely by directly finalizing number tokens without calling the necessary length verification methods. This inconsistency allows remote attackers to trigger excessive memory and CPU consumption in reactive applications.

Impact

  1. Unbounded Memory Allocation: Maliciously long numbers force the TextBuffer to grow indefinitely, leading to OutOfMemoryError.
  2. CPU Exhaustion: Processing large numbers during subsequent BigInteger conversion triggers O(n²) operations, pinning worker threads.
  3. Application Instability: The resource starvation can crash the entire JVM, affecting all concurrent users in a shared environment.

Description

The root cause is an architectural inconsistency between ParserBase and NonBlockingUtf8JsonParserBase. In the synchronous path, number parsing lifecycle always concludes with a call to resetInt() or resetFloat(), which are the authoritative points for length validation.

Number Parsing Lifecycle: Sync vs. Async

SYNC PATH (safe) digits accumulated in TextBuffer resetInt() / resetFloat() called validateIntegerLength() checks limit ✓ StreamConstraintsException thrown ASYNC PATH (vulnerable) digits accumulated in TextBuffer _valueComplete() called directly validateIntegerLength() ← SKIPPED ❌ Unbounded number accepted TextBuffer grows unbounded → OOM BigInteger conversion → O(n²) CPU
// Vulnerable Async Path Implementation
protected void _finishNumberIntegralPart(char[] outBuf, int outPtr) {
    _textBuffer.setCurrentLength(outPtr);
    // BUG: Directly finalizes token, skipping resetInt() / validateIntegerLength()
    _valueComplete(JsonToken.VALUE_NUMBER_INT);
}

By skipping the reset* lifecycle methods, the async parser never executes the code that checks the StreamReadConstraints. This means that while a sync parser will throw a StreamConstraintsException for a 5,000-digit number, the async parser will happily store it in memory, allowing for easy exploitation via large JSON payloads.

Conclusion

This vulnerability highlights that security constraints must be enforced at the lowest common denominator in the parsing pipeline. Developers using Jackson's non-blocking parser in Spring WebFlux or other reactive environments should immediately upgrade to jackson-core 3.1.0 or later. This update ensures that both synchronous and asynchronous parsing paths correctly route through the length validation logic.

References

Special thanks to the Jackson maintainers for their exceptionally quick response and effective mitigations in addressing these findings.