This is an automated email from the ASF dual-hosted git repository.

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new d0fef0a04a [common] Overflow-safe URI length check in 
VideoFrameDescriptor (#9618)
d0fef0a04a is described below

commit d0fef0a04ad741ce1ecbd103e65a7c542310ae31
Author: YangJie <[email protected]>
AuthorDate: Thu Sep 10 02:52:29 2026 -0400

    [common] Overflow-safe URI length check in VideoFrameDescriptor (#9618)
---
 .../apache/paimon/data/VideoFrameDescriptor.java   | 12 ++++++++--
 .../paimon/data/VideoFrameDescriptorTest.java      | 26 ++++++++++++++++++++++
 2 files changed, 36 insertions(+), 2 deletions(-)

diff --git 
a/paimon-common/src/main/java/org/apache/paimon/data/VideoFrameDescriptor.java 
b/paimon-common/src/main/java/org/apache/paimon/data/VideoFrameDescriptor.java
index bd4fedd732..45b995db97 100644
--- 
a/paimon-common/src/main/java/org/apache/paimon/data/VideoFrameDescriptor.java
+++ 
b/paimon-common/src/main/java/org/apache/paimon/data/VideoFrameDescriptor.java
@@ -109,8 +109,16 @@ public class VideoFrameDescriptor extends BlobDescriptor {
             throw invalidPayload("missing magic header");
         }
         int uriLength = buffer.getInt();
-        if (uriLength < 0 || buffer.remaining() < uriLength + 3 * Long.BYTES) {
-            throw invalidPayload("invalid URI length: " + uriLength);
+        // checked by comparison and subtraction: uriLength + 3 * Long.BYTES 
wraps negative
+        // for a uriLength near Integer.MAX_VALUE
+        if (uriLength < 0) {
+            throw invalidPayload("negative URI length: " + uriLength);
+        }
+        if (uriLength > buffer.remaining()) {
+            throw invalidPayload("URI length exceeds data size");
+        }
+        if (buffer.remaining() - uriLength < 3 * Long.BYTES) {
+            throw invalidPayload("missing offset/length/frame index");
         }
 
         byte[] uriBytes = new byte[uriLength];
diff --git 
a/paimon-common/src/test/java/org/apache/paimon/data/VideoFrameDescriptorTest.java
 
b/paimon-common/src/test/java/org/apache/paimon/data/VideoFrameDescriptorTest.java
index 8ab737510b..ea452559f1 100644
--- 
a/paimon-common/src/test/java/org/apache/paimon/data/VideoFrameDescriptorTest.java
+++ 
b/paimon-common/src/test/java/org/apache/paimon/data/VideoFrameDescriptorTest.java
@@ -22,6 +22,8 @@ import org.apache.paimon.utils.IOUtils;
 
 import org.junit.jupiter.api.Test;
 
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 
@@ -89,6 +91,30 @@ public class VideoFrameDescriptorTest {
         assertThatThrownBy(() -> new VideoFrameDescriptor("file:/video.mp4", 
0, 9, -1))
                 .isInstanceOf(IllegalArgumentException.class)
                 .hasMessageContaining("non-negative");
+
+        // The old check let this through because uriLength + 3 * Long.BYTES 
wrapped
+        // negative, and deserialize went on to allocate ~2GB.
+        byte[] hostileUriLength = descriptor.serialize();
+        ByteBuffer.wrap(hostileUriLength)
+                .order(ByteOrder.LITTLE_ENDIAN)
+                .putInt(Byte.BYTES + Long.BYTES, Integer.MAX_VALUE - 23);
+        assertThatThrownBy(() -> 
VideoFrameDescriptor.deserialize(hostileUriLength))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("URI length exceeds data size");
+
+        byte[] negativeUriLength = descriptor.serialize();
+        ByteBuffer.wrap(negativeUriLength)
+                .order(ByteOrder.LITTLE_ENDIAN)
+                .putInt(Byte.BYTES + Long.BYTES, -1);
+        assertThatThrownBy(() -> 
VideoFrameDescriptor.deserialize(negativeUriLength))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("negative URI length");
+
+        byte[] truncated =
+                Arrays.copyOf(descriptor.serialize(), 
descriptor.serialize().length - Long.BYTES);
+        assertThatThrownBy(() -> VideoFrameDescriptor.deserialize(truncated))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("missing offset/length/frame index");
     }
 
     private static byte[] fromHex(String hex) {

Reply via email to