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 1d5cc316db [format] Fail fast when skipping DELTA_LENGTH_BYTE_ARRAY 
data past end of page (#9571)
1d5cc316db is described below

commit 1d5cc316db12acec719bae6130b81c17b8a50dca
Author: YangJie <[email protected]>
AuthorDate: Fri Sep 4 03:24:32 2026 -0400

    [format] Fail fast when skipping DELTA_LENGTH_BYTE_ARRAY data past end of 
page (#9571)
---
 .../VectorizedDeltaLengthByteArrayReader.java      | 13 ++++++--
 .../reader/DeltaLengthByteArrayEncodingTest.java   | 35 ++++++++++++++++++++++
 2 files changed, 45 insertions(+), 3 deletions(-)

diff --git 
a/paimon-format/src/main/java/org/apache/paimon/format/parquet/reader/VectorizedDeltaLengthByteArrayReader.java
 
b/paimon-format/src/main/java/org/apache/paimon/format/parquet/reader/VectorizedDeltaLengthByteArrayReader.java
index 97df39ac57..cab5cfc971 100644
--- 
a/paimon-format/src/main/java/org/apache/paimon/format/parquet/reader/VectorizedDeltaLengthByteArrayReader.java
+++ 
b/paimon-format/src/main/java/org/apache/paimon/format/parquet/reader/VectorizedDeltaLengthByteArrayReader.java
@@ -81,9 +81,16 @@ public class VectorizedDeltaLengthByteArrayReader extends 
VectorizedReaderBase
     @Override
     public void skipBinary(int total) {
         for (int i = 0; i < total; i++) {
-            int remaining = lengthsVector.getInt(currentRow + i);
-            while (remaining > 0) {
-                remaining -= in.skip(remaining);
+            int length = lengthsVector.getInt(currentRow + i);
+            // A corrupt length can be negative, and skipFully would then move 
the stream
+            // backwards instead of forwards.
+            if (length < 0) {
+                throw new ParquetDecodingException("Negative byte array 
length: " + length);
+            }
+            try {
+                in.skipFully(length);
+            } catch (IOException e) {
+                throw new ParquetDecodingException("Failed to skip " + length 
+ " bytes", e);
             }
         }
         currentRow += total;
diff --git 
a/paimon-format/src/test/java/org/apache/paimon/format/parquet/reader/DeltaLengthByteArrayEncodingTest.java
 
b/paimon-format/src/test/java/org/apache/paimon/format/parquet/reader/DeltaLengthByteArrayEncodingTest.java
index f6cd6e891b..d08efc9a20 100644
--- 
a/paimon-format/src/test/java/org/apache/paimon/format/parquet/reader/DeltaLengthByteArrayEncodingTest.java
+++ 
b/paimon-format/src/test/java/org/apache/paimon/format/parquet/reader/DeltaLengthByteArrayEncodingTest.java
@@ -25,16 +25,21 @@ import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.parquet.bytes.ByteBufferInputStream;
 import org.apache.parquet.bytes.DirectByteBufferAllocator;
 import org.apache.parquet.column.values.Utils;
+import 
org.apache.parquet.column.values.delta.DeltaBinaryPackingValuesWriterForInteger;
 import 
org.apache.parquet.column.values.deltalengthbytearray.DeltaLengthByteArrayValuesWriter;
+import org.apache.parquet.io.ParquetDecodingException;
 import org.apache.parquet.io.api.Binary;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
 import java.util.Random;
 
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 /** Test for delta length byte array encoding. */
 public class DeltaLengthByteArrayEncodingTest {
@@ -127,6 +132,36 @@ public class DeltaLengthByteArrayEncodingTest {
         }
     }
 
+    @Test
+    public void testSkipPastEndOfPage() throws Exception {
+        writeData(writer, values);
+        byte[] page = writer.getBytes().toByteArray();
+        // Drop three bytes of data. The lengths at the front of the page 
still add up to
+        // more than the page holds, which is what a truncated page looks like.
+        byte[] truncated = Arrays.copyOf(page, page.length - 3);
+        reader.initFromPage(values.length, 
ByteBufferInputStream.wrap(ByteBuffer.wrap(truncated)));
+
+        ParquetDecodingException e =
+                assertThrows(
+                        ParquetDecodingException.class, () -> 
reader.skipBinary(values.length));
+        assertEquals("Failed to skip " + values[2].length() + " bytes", 
e.getMessage());
+    }
+
+    @Test
+    public void testSkipNegativeLength() throws Exception {
+        // A length section that decodes to a negative value cannot be 
produced by the
+        // writer, so write one directly and append no data at all.
+        DeltaBinaryPackingValuesWriterForInteger lengthWriter =
+                new DeltaBinaryPackingValuesWriterForInteger(
+                        64 * 1024, 64 * 1024, new DirectByteBufferAllocator());
+        lengthWriter.writeInteger(-1);
+        reader.initFromPage(1, lengthWriter.getBytes().toInputStream());
+
+        ParquetDecodingException e =
+                assertThrows(ParquetDecodingException.class, () -> 
reader.skipBinary(1));
+        assertEquals("Negative byte array length: -1", e.getMessage());
+    }
+
     private void writeData(DeltaLengthByteArrayValuesWriter writer, String[] 
values) {
         for (String value : values) {
             writer.writeBytes(Binary.fromString(value));

Reply via email to