critical

CVE

CVE-2026-110879, CVE-2026-54512, CVE-2026-104016, CVE-2026-110747, CVE-2026-104895, CVE-2026-102554, CVE-2026-102479

CWE

CWE-502, CWE-444, CWE-190, CWE-770, CWE-789

Affected Surface

  • Maven `com.fasterxml.jackson.core:jackson-databind` (`2.10.0..2.18.11`, `2.19.0..2.21.7`, `2.22.0..2.22.3`) and `tools.jackson.core:jackson-databind` (`3.0.0..3.1.7`, `3.2.0..3.2.3`) for `CVE-2026-110879` (`GHSA-m8xf-2cr7-77ph`)
  • Maven `com.fasterxml.jackson.dataformat:jackson-dataformat-protobuf` and `tools.jackson.dataformat:jackson-dataformat-protobuf` (`2.6.0..2.18.11`, `2.19.0..2.21.7`, `2.22.0..2.22.3`, `3.0.0..3.1.7`, `3.2.0..3.2.3`) for `CVE-2026-104016` (`GHSA-c2rq-mcfv-jw29`) and `CVE-2026-110747` (`GHSA-fv7v-rrmh-4qcv`)
  • Maven `com.fasterxml.jackson.dataformat:jackson-dataformat-smile` and `tools.jackson.dataformat:jackson-dataformat-smile` (`2.9.0..2.18.11`, `2.19.0..2.21.7`, `2.22.0..2.22.3`, `3.0.0..3.1.7`, `3.2.0..3.2.3`) for `CVE-2026-104895` (`GHSA-h3c2-2m49-r49w`)
  • Maven `com.google.guava:guava` versions `4.0` through `33.7.1-jre` / `33.7.1-android` (`CVE-2026-102554` / `GHSA-xxph-c9ww-hj94`)
  • Maven `io.vertx:vertx-core` versions `5.0.0` through `5.0.12`, `5.1.0` through `5.1.9`, and `5.2.0` (`CVE-2026-102479` / `GHSA-ch7p-g5pm-3gqx`), including downstream Quarkus, `apache/camel-quarkus`, and `strimzi/strimzi-kafka-bridge 0.33.0` deployments behind HTTP/1.1 reverse proxies

TL;DR

On 27 and 28 October 2026, six vulnerabilities across three core Java libraries (FasterXML Jackson, Google Guava, and Eclipse Vert.x) were published to NVD, GitHub Advisory Database, and oss-security:

  1. jackson-databind PolymorphicTypeValidator generic and array element bypass (CVE-2026-110879 / GHSA-m8xf-2cr7-77ph, CVSS 9.8 Critical): An incomplete fix for June’s CVE-2026-54512 (GHSA-j3rv-43j4-c7qm) left DatabindContext._resolveAndValidateGeneric() validating generic type arguments against the outer base type rather than Object.class, and replacing the caller’s PolymorphicTypeValidator with ApprovedBaseTypeValidator / LaissezFaireSubTypeValidator once the outer container type was approved by allowIfBaseType(...). A property declared as Map<String, String> accepted HashMap<FileSystemXmlApplicationContext, String>, invoking Spring’s one-string XML constructor during map-key discovery to reach ProcessBuilder.start() RCE. Array element types in [Lpkg.Gadget; and [[Lpkg.Gadget; bypassed validation in the same way.
  2. Eclipse Vert.x 5.x Content-Xength HTTP/1.x request smuggling (“Byte Debt”, CVE-2026-102479 / GHSA-ch7p-g5pm-3gqx, CVSS v4.0 8.7 High): To avoid string allocations when interning common HTTP headers, VertxHttpRequestDecoder.isContentLength() checked bytes 1..7 ("ontent-") and bytes 9..13 ("ength"), completely skipping index 8 ('L'). Any 14-byte header matching Content-*ength (such as Content-Xength: 50) was treated by Vert.x as Content-Length while reverse proxies (Nginx, HAProxy, Envoy, Traefik, Caddy, Apache httpd, nghttpx) forwarded it as an unknown custom header. On 28 October, JFrog Security Research published a “dangling-byte” double-desync chain achieving cross-user session fixation across all seven proxies.
  3. jackson-dataformat-protobuf and jackson-dataformat-smile 8-to-33-byte OutOfMemoryError crashes (CVE-2026-104016, CVE-2026-110747, and CVE-2026-104895, CVSS 7.5 High): In ProtobufParser, checking (_inputPtr + len) <= _inputEnd with 32-bit signed integers wrapped to a negative number when a varint length was near Integer.MAX_VALUE, bypassing StreamReadConstraints and crashing the JVM in Arrays.copyOf() on a 33-byte message (CVE-2026-104016) or an 8-byte map<string, V> entry (CVE-2026-110747). In jackson-dataformat-smile’s reactive NonBlockingByteArrayParser, raw-binary token 0xFD omitted the (value >>> 25) != 0 VInt overflow guard and allocated new byte[len] (~2 GB) from a 10-byte feed (CVE-2026-104895).
  4. Google Guava collection deserialization OutOfMemoryError (CVE-2026-102554 / GHSA-xxph-c9ww-hj94, CVSS v4.0 8.2 High): In com.google.guava:guava 4.0 through 33.7.1, CompactHashMap, CompactHashSet, and MapMakerInternalMap read an unvalidated 32-bit elementCount / size from ObjectInputStream and eagerly allocated backing arrays for up to 2^30 - 1 (~1.07 billion) entries on a 72-byte serialized stream.

What to do now: Upgrade jackson-databind, jackson-dataformat-protobuf, and jackson-dataformat-smile to 2.18.12, 2.21.8, 2.22.4, 3.1.8, or 3.2.4; upgrade com.google.guava:guava to 33.7.2-jre (or 33.7.2-android); and upgrade io.vertx:vertx-core on the 5.x line to 5.1.10 or 5.2.1+ (5.2.2 is current).

Affected packages and projects

EcosystemMaven coordinatesCVE / AdvisorySeverityAffected versionsFixed versions
Mavencom.fasterxml.jackson.core:jackson-databind, tools.jackson.core:jackson-databindCVE-2026-110879 (GHSA-m8xf-2cr7-77ph)CVSS 9.8 Critical2.10.0..2.18.11, 2.19.0..2.21.7, 2.22.0..2.22.3, 3.0.0..3.1.7, 3.2.0..3.2.32.18.12, 2.21.8, 2.22.4, 3.1.8, 3.2.4
Mavencom.fasterxml.jackson.dataformat:jackson-dataformat-protobuf, tools.jackson.dataformat:jackson-dataformat-protobufCVE-2026-104016 (GHSA-c2rq-mcfv-jw29)CVSS 7.5 High2.6.0..2.18.11, 2.19.0..2.21.7, 2.22.0..2.22.3, 3.0.0..3.1.7, 3.2.0..3.2.32.18.12, 2.21.8, 2.22.4, 3.1.8, 3.2.4
Mavencom.fasterxml.jackson.dataformat:jackson-dataformat-protobuf, tools.jackson.dataformat:jackson-dataformat-protobufCVE-2026-110747 (GHSA-fv7v-rrmh-4qcv)CVSS 7.5 High2.21.6..2.21.7, 2.22.0..2.22.3, 3.1.6..3.1.7, 3.2.2..3.2.32.21.8, 2.22.4, 3.1.8, 3.2.4
Mavencom.fasterxml.jackson.dataformat:jackson-dataformat-smile, tools.jackson.dataformat:jackson-dataformat-smileCVE-2026-104895 (GHSA-h3c2-2m49-r49w)CVSS 7.5 High2.9.0..2.18.11, 2.19.0..2.21.7, 2.22.0..2.22.3, 3.0.0..3.1.7, 3.2.0..3.2.32.18.12, 2.21.8, 2.22.4, 3.1.8, 3.2.4
Mavencom.google.guava:guavaCVE-2026-102554 (GHSA-xxph-c9ww-hj94)CVSS v4.0 8.2 High4.0 through 33.7.1-jre / 33.7.1-android33.7.2-jre, 33.7.2-android
Mavenio.vertx:vertx-coreCVE-2026-102479 (GHSA-ch7p-g5pm-3gqx)CVSS v4.0 8.7 High5.0.0..5.0.12, 5.1.0..5.1.9, 5.2.05.1.10, 5.2.1 (5.2.2 current)

Technical analysis

1. CVE-2026-110879: how generic type arguments and array elements bypassed PolymorphicTypeValidator in jackson-databind

When jackson-databind patched CVE-2026-54512 (GHSA-j3rv-43j4-c7qm), it added _validateTypeParameter() inside DatabindContext._resolveAndValidateGeneric() so that a type ID like java.util.ArrayList<EvilGadget> would have its type arguments checked.

However, as Adam Korcz and François Martin reported, three flaws in _resolveAndValidateGeneric() and _validateTypeParameter() allowed attacker-chosen classes inside generic parameters or array descriptors ([L...;, [[L...;) to bypass validation anyway:

  1. Validating type arguments against the outer baseType instead of Object.class: _validateTypeParameter(baseType, subType.containedType(i), ptv, config) passed the outer property’s baseType to ptv.validateSubClassName(config, baseType, rawName) and ptv.validateSubType(config, baseType, param). Under Jackson’s default DefaultBaseTypeLimitingValidator (used with @JsonTypeInfo(use = Id.CLASS) without a custom validator), validateBaseType() and validateSubType() assumed the subtype relationship had already been checked, so any class nested inside a generic argument was approved even though selecting that same class directly as the outer type ID failed with "Not a subtype".
  2. allowIfBaseType(...) substituted a permissive validator for embedded arguments: When a BasicPolymorphicTypeValidator rule approved the outer container via allowIfBaseType(...) (for example permitting Map), Jackson wrapped or substituted the validator so that embedded generic arguments were no longer checked against the configured subtype rules. Declaring a field as Map<String, String> allowed the JSON payload to specify "@class": "java.util.HashMap<org.springframework.context.support.FileSystemXmlApplicationContext, java.lang.String>". During map-key deserialization, Jackson passed the attacker-controlled JSON property name to FileSystemXmlApplicationContext(String), loading an XML bean definition and executing ProcessBuilder.start().
  3. Multi-dimensional and Serializable[] array descriptors (PR #6289): When an array type ID such as [[Lcom.example.Gadget; was validated against a rule like allowIfSubType(Object[][].class) or allowIfSubType(Serializable[].class), _isSpecificallyApprovedArrayType() only probed Object[] (1D), concluded the rule was a narrow array approval, and skipped validating the element type Gadget.

PR #6287 and PR #6289 fix all three paths in DatabindContext.java by unwrapping ApprovedBaseTypeValidator back to the caller’s configured PolymorphicTypeValidator, validating every class named in a type parameter or array element as if it were a direct type ID for base type Object.class, and probing array rules across Object, Serializable, and Cloneable at the array’s exact dimension depth:

--- a/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java
@@ -288,14 +291,13 @@ private JavaType _resolveAndValidateGeneric(JavaType baseType, String subClass,
-        for (int i = 0, n = subType.containedTypeCount(); i < n; ++i) {
-            _validateTypeParameter(baseType, subType.containedType(i), ptv, config);
-        }
-        if (subType.isArrayType()) {
-            _validateTypeParameter(baseType, subType.getContentType(), ptv, config);
+        // [CVE-2026-110879]: ... and validated with the configured validator (not one
+        // that approves all subtypes of the base type) and as if named as a direct
+        // type id for base type `Object` -- type parameter may bind to anything.
+        if (ptv instanceof ApprovedBaseTypeValidator) {
+            ptv = ((ApprovedBaseTypeValidator) ptv).getTypeParameterValidator();
         }
+        _validateTypeParameters(baseType, subClass, subType, ptv, config);

2. CVE-2026-102479: Eclipse Vert.x 5.x Content-Xength header canonicalization and the “dangling-byte” smuggling chain

In Eclipse Vert.x 5.0.0 through 5.0.12, 5.1.0 through 5.1.9, and 5.2.0, VertxHttpRequestDecoder.splitHeaderName() optimized HTTP/1.x header parsing by packing header name bytes into 64-bit long and 32-bit int values and comparing them against precomputed constants before allocating a new AsciiString.

For 12-byte (Content-Type) and 14-byte (Content-Length) headers starting with 'C' or 'c', isContentType() and isContentLength() packed bytes 1..7 ("ontent-") into a long and packed the trailing bytes ("ype" at offsets 9..11 or "ength" at offsets 9..13) into an int, leaving byte offset 8 ('T' or 'L') completely unchecked:

--- a/vertx-core/src/main/java/io/vertx/core/http/impl/http1/VertxHttpRequestDecoder.java
+++ b/vertx-core/src/main/java/io/vertx/core/http/impl/http1/VertxHttpRequestDecoder.java
@@ -90,12 +110,22 @@ protected AsciiString splitHeaderName(byte[] sb, int start, int length) {
       } else if (length == 12) {
-        if (isContentType(sb, start)) {
-          return firstChar == 'C' ? _Content_Type : HttpHeaderNames.CONTENT_TYPE;
+        if (mightBeContentType(sb, start)) {
+          byte maybeT = sb[start + 8];
+          if (firstChar == 'C' && maybeT == 'T') {
+            return _Content_Type;
+          } else if (firstChar == 'c' && maybeT == 't') {
+            return HttpHeaderNames.CONTENT_TYPE;
+          }
         }
       } else if (length == 14) {
-        if (isContentLength(sb, start)) {
-          return firstChar == 'C' ? _Content_Length : HttpHeaderNames.CONTENT_LENGTH;
+        if (mightBeContentLength(sb, start)) {
+          byte maybeL = sb[start + 8];
+          if (firstChar == 'C' && maybeL == 'L') {
+            return _Content_Length;
+          } else if (firstChar == 'c' && maybeL == 'l') {
+            return HttpHeaderNames.CONTENT_LENGTH;
+          }
         }
       }

As JFrog Security Research detailed on 28 October (Byte Debt), sending both Content-Length and Content-Xength in the same request is rejected by Netty’s duplicate Content-Length check (400 Bad Request). Instead, an attacker sends a single request A carrying Content-Xength: <N> and no body to an endpoint that does not read the request body:

  1. The reverse proxy (Nginx, HAProxy, Envoy, Traefik, Caddy, Apache httpd, or nghttpx) treats Content-Xength as a harmless custom header, forwards request A with no body, receives Vert.x’s immediate HTTP response, and returns the backend TCP connection to its keep-alive pool while Vert.x is still waiting to read <N> body bytes on that socket (“byte debt”).
  2. On a second request B that reuses the pooled connection, the attacker sends a body containing two spliced HTTP requests C and D, where D (for example POST /login with the attacker’s credentials) declares a body length 1 byte longer than the bytes actually sent.
  3. Vert.x consumes B’s headers as A’s leftover body, replies to C (which the proxy returns as the response to B), and pauses while waiting for the single “dangling byte” of D. Because D has not emitted a response yet, the proxy’s idle-socket check sees an empty read buffer and returns the poisoned connection to the pool.
  4. When the next victim user sends a request on that connection, the very first byte of their HTTP request (G in GET /...) satisfies D’s 1-byte debt, and Vert.x immediately returns the attacker’s POST /login 303 Set-Cookie response to the victim’s browser.

Commit 317c778d7f (shipped in 5.1.10 and 5.2.1) checks sb[start + 8] explicitly for both Content-Type and Content-Length.

3. CVE-2026-104016, CVE-2026-110747, and CVE-2026-104895: Jackson Protobuf and Smile OutOfMemoryError crashes

Three High-severity advisories in FasterXML/jackson-dataformats-binary allow tiny payloads (8 to 33 bytes) to crash the JVM or throw uncaught NegativeArraySizeException before StreamReadConstraints can intervene:

  • Protobuf signed-int _inputPtr + len overflow (CVE-2026-104016 and CVE-2026-110747, commit 671db43487 and commit a4d05bd166): In ProtobufParser.java, nextTextValue(), getText(), and _readStringKey() (used for map<string, V> keys) checked whether the entire string was already buffered in memory using 32-bit signed arithmetic:
    -        if ((_inputPtr + len) <= _inputEnd) {
    +        // Compare against remaining input: `_inputPtr + len` may overflow
    +        if (len <= (_inputEnd - _inputPtr)) {
                 return _finishShortText(len);
             }
    When an attacker supplied a 5-byte varint decoding to Integer.MAX_VALUE (0x7fffffff) while _inputPtr >= 1, _inputPtr + len wrapped to a negative int, satisfying <= _inputEnd and calling _finishShortText(Integer.MAX_VALUE) -> TextBuffer.expandCurrentSegment(len) -> Arrays.copyOf(curr, len), which immediately threw OutOfMemoryError: Requested array size exceeds VM limit.
  • Smile async NonBlockingByteArrayParser raw-binary eager allocation (CVE-2026-104895, commit 60e58bbb24): On a Smile raw-binary token (0xFD), the blocking SmileParser._readUnsignedVInt() guarded against 32-bit shift overflow with if ((value >>> 25) != 0), but the reactive/non-blocking NonBlockingByteArrayParser._decodeVInt() omitted the check and passed the decoded len directly into _binaryValue = new byte[len] inside _startRawBinary() before any payload bytes arrived. A 10-byte Smile frame forced an immediate ~2 GB byte[] allocation (or NegativeArraySizeException on overflow) in Spring WebFlux, Vert.x, or Micronaut pipelines.

4. CVE-2026-102554: Google Guava CompactHashMap, CompactHashSet, and MapMakerInternalMap deserialization OOM

In com.google.guava:guava 4.0 through 33.7.1, CompactHashMap.readObject(), CompactHashSet.readObject(), and MapMakerInternalMap.readMapMaker() read a 32-bit elementCount or size integer from ObjectInputStream and passed it directly into init(elementCount) or .initialCapacity(size).

Because init(elementCount) only clamped the count to CompactHashing.MAX_SIZE (2^30 - 1, or ~1.07 billion elements), a 72-byte Java serialization stream declaring elementCount = 1_073_741_823 followed by a single element caused the first put() / add() call to allocate new int[2^30], new Object[2^30], and new Object[2^30], crashing the JVM with OutOfMemoryError. Commit b931fe9d6d (released in Guava 33.7.2-jre and 33.7.2-android) caps the initial capacity allocated during deserialization at 256 (init(min(elementCount, 256))), allowing larger collections to grow naturally as real elements are read from the stream:

--- a/guava/src/com/google/common/collect/CompactHashMap.java
+++ b/guava/src/com/google/common/collect/CompactHashMap.java
@@ -1129,7 +1129,7 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo
     if (elementCount < 0) {
       throw new InvalidObjectException("Invalid size: " + elementCount);
     }
-    init(elementCount);
+    init(min(elementCount, 256));
     for (int i = 0; i < elementCount; i++) {

Detection and remediation

# 1. Check Maven and Gradle manifests and lockfiles for vulnerable Jackson, Guava, and Vert.x versions
rg -n '(jackson-databind|jackson-bom|jackson-dataformat-protobuf|jackson-dataformat-smile|com\.google\.guava:guava|io\.vertx:vertx-core)' \
  pom.xml '**/pom.xml' build.gradle* '**/build.gradle*' gradle.lockfile '**/gradle.lockfile' gradle/libs.versions.toml 2>/dev/null

# 2. Audit Java/Kotlin codebases for class-name polymorphic deserialization and non-blocking Smile parsers
rg -n 'activateDefaultTyping|enableDefaultTyping|JsonTypeInfo\.Id\.(MINIMAL_)?CLASS|createNonBlockingByteArrayParser' \
  src/ '**/src/' 2>/dev/null
  1. Upgrade Jackson (2.18.12, 2.21.8, 2.22.4, 3.1.8, or 3.2.4): Bump jackson-bom or individual jackson-databind, jackson-dataformat-protobuf, and jackson-dataformat-smile coordinates. Note that after upgrading to 2.18.12+ / 2.22.4+ / 3.2.4+, any PolymorphicTypeValidator configuration that previously relied on an approved outer generic container (List, Map) or array type (Object[][], Serializable[]) to implicitly allow unlisted type arguments or element classes will now reject those payloads unless the element classes themselves are allowlisted.
  2. Upgrade Eclipse Vert.x (5.1.10 or 5.2.1+): Any service on the Vert.x 5.0.x, 5.1.x < 5.1.10, or 5.2.0 branch (including Quarkus, camel-quarkus, or Strimzi Kafka Bridge 0.33.0 containers) should upgrade io.vertx:vertx-core to 5.1.10 or 5.2.1+ (5.2.2 is current). If you cannot upgrade immediately, configure your front-end reverse proxy or WAF to drop requests containing any header matching ^Content-[^Ll]ength$ or ^Content-[^Tt]ype$ (case-insensitive).
  3. Upgrade Google Guava (33.7.2-jre / 33.7.2-android): Upgrade com.google.guava:guava to 33.7.2-jre (or 33.7.2-android) in any application that deserializes Java ObjectInputStream data, and configure an ObjectInputFilter (jdk.serialFilter) that bounds maxarray.

Timeline

  • 2026-09-23: Julien Viet commits 317c778d7f to eclipse-vertx/vert.x to verify byte offset 8 in VertxHttpRequestDecoder.splitHeaderName().
  • 2026-09-29: Google commits b931fe9d6d and releases com.google.guava:guava 33.7.2-jre and 33.7.2-android (GHSA-xxph-c9ww-hj94).
  • 2026-09-30 to 2026-10-02: FasterXML merges commits 671db43487, a4d05bd166, and 60e58bbb24 in jackson-dataformats-binary.
  • 2026-10-15 to 2026-10-18: FasterXML merges PRs #6287 and #6289 in jackson-databind and publishes 2.18.12, 2.21.8, 2.22.4, 3.1.8, and 3.2.4 across jackson-databind and jackson-dataformats-binary to Maven Central.
  • 2026-10-27 to 2026-10-28: GitHub Advisory Database and NVD publish CVE-2026-102554, CVE-2026-104016, CVE-2026-104895, CVE-2026-110747, and CVE-2026-110879, while JFrog Security Research and oss-security (2026/10/28/19) publish the Byte Debt session-fixation analysis for Eclipse Vert.x CVE-2026-102479.

From research to remediation

Check whether this pattern exists in your codebase

Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.

References