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) {