JAVA-2557: Accept any negative length when decoding elements of tuples and UDTs (#1368)


diff --git a/changelog/README.md b/changelog/README.md
index 805ce20..e4d26b3 100644
--- a/changelog/README.md
+++ b/changelog/README.md
@@ -2,8 +2,9 @@
 
 <!-- Note: contrary to 3.x, insert new entries *first* in their section -->
 
-### 4.4.0 (in progress)
+### 4.3.1 (in progress)
 
+- [bug] JAVA-2557: Accept any negative length when decoding elements of tuples and UDTs
 
 ### 4.3.0
 
diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/type/codec/TupleCodec.java b/core/src/main/java/com/datastax/oss/driver/internal/core/type/codec/TupleCodec.java
index 2a900ce..ad008cd 100644
--- a/core/src/main/java/com/datastax/oss/driver/internal/core/type/codec/TupleCodec.java
+++ b/core/src/main/java/com/datastax/oss/driver/internal/core/type/codec/TupleCodec.java
@@ -109,7 +109,7 @@
         }
         int elementSize = input.getInt();
         ByteBuffer element;
-        if (elementSize == -1) {
+        if (elementSize < 0) {
           element = null;
         } else {
           element = input.slice();
diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/type/codec/UdtCodec.java b/core/src/main/java/com/datastax/oss/driver/internal/core/type/codec/UdtCodec.java
index 2e2df95..f515c6b 100644
--- a/core/src/main/java/com/datastax/oss/driver/internal/core/type/codec/UdtCodec.java
+++ b/core/src/main/java/com/datastax/oss/driver/internal/core/type/codec/UdtCodec.java
@@ -112,7 +112,7 @@
         }
         int elementSize = input.getInt();
         ByteBuffer element;
-        if (elementSize == -1) {
+        if (elementSize < 0) {
           element = null;
         } else {
           element = input.slice();
diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/type/codec/TupleCodecTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/type/codec/TupleCodecTest.java
index f7d609e..ab95dd4 100644
--- a/core/src/test/java/com/datastax/oss/driver/internal/core/type/codec/TupleCodecTest.java
+++ b/core/src/test/java/com/datastax/oss/driver/internal/core/type/codec/TupleCodecTest.java
@@ -122,6 +122,26 @@
     verify(textCodec).decode(Bytes.fromHexString("0x61"), ProtocolVersion.DEFAULT);
   }
 
+  /** Test for JAVA-2557. Ensures that the codec can decode null fields with any negative length. */
+  @Test
+  public void should_decode_negative_element_length_as_null_field() {
+    TupleValue tuple =
+        decode(
+            "0x"
+                + "ffffffff" // field1 has length -1
+                + "fffffffe" // field2 has length -2
+                + "80000000" // field3 has length Integer.MIN_VALUE (-2147483648)
+            );
+
+    assertThat(tuple.isNull(0)).isTrue();
+    assertThat(tuple.isNull(1)).isTrue();
+    assertThat(tuple.isNull(2)).isTrue();
+
+    verifyZeroInteractions(intCodec);
+    verifyZeroInteractions(doubleCodec);
+    verifyZeroInteractions(textCodec);
+  }
+
   @Test
   public void should_format_null_tuple() {
     assertThat(format(null)).isEqualTo("NULL");
diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/type/codec/UdtCodecTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/type/codec/UdtCodecTest.java
index 5947cff..a3bd4fe 100644
--- a/core/src/test/java/com/datastax/oss/driver/internal/core/type/codec/UdtCodecTest.java
+++ b/core/src/test/java/com/datastax/oss/driver/internal/core/type/codec/UdtCodecTest.java
@@ -131,6 +131,39 @@
     verify(textCodec).decode(Bytes.fromHexString("0x61"), ProtocolVersion.DEFAULT);
   }
 
+  /** Test for JAVA-2557. Ensures that the codec can decode null fields with any negative length. */
+  @Test
+  public void should_decode_negative_element_length_as_null_field() {
+    UdtValue udt =
+        decode(
+            "0x"
+                + "ffffffff" // field1 has length -1
+                + "fffffffe" // field2 has length -2
+                + "80000000" // field3 has length Integer.MIN_VALUE (-2147483648)
+            );
+
+    assertThat(udt.isNull(0)).isTrue();
+    assertThat(udt.isNull(1)).isTrue();
+    assertThat(udt.isNull(2)).isTrue();
+
+    verifyZeroInteractions(intCodec);
+    verifyZeroInteractions(doubleCodec);
+    verifyZeroInteractions(textCodec);
+  }
+
+  @Test
+  public void should_decode_absent_element_as_null_field() {
+    UdtValue udt = decode("0x");
+
+    assertThat(udt.isNull(0)).isTrue();
+    assertThat(udt.isNull(1)).isTrue();
+    assertThat(udt.isNull(2)).isTrue();
+
+    verifyZeroInteractions(intCodec);
+    verifyZeroInteractions(doubleCodec);
+    verifyZeroInteractions(textCodec);
+  }
+
   @Test
   public void should_format_null_udt() {
     assertThat(format(null)).isEqualTo("NULL");