This is an automated email from the ASF dual-hosted git repository.
lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-java.git
The following commit(s) were added to refs/heads/main by this push:
new 3e47560c7 GH-1098: Fix always-true precondition check in
LargeListVector.setValueCount (#1099)
3e47560c7 is described below
commit 3e47560c773b1bab9d13d446ea69869c782e4666
Author: YangJie <[email protected]>
AuthorDate: Tue Aug 25 11:39:26 2026 +0800
GH-1098: Fix always-true precondition check in
LargeListVector.setValueCount (#1099)
## What's Changed
Fix the precondition check in `LargeListVector.setValueCount()` where
`||` made the condition always true (`childValueCount <=
Integer.MAX_VALUE || childValueCount >= Integer.MIN_VALUE` is a
tautology for any `long` value), allowing silent data corruption when
`childValueCount` exceeds `Integer.MAX_VALUE` or is negative.
Changes:
- Replace `||` with `&&` and `Integer.MIN_VALUE` with `0` to correctly
bound `childValueCount` to `[0, Integer.MAX_VALUE]`, consistent with
`LargeListViewVector`
- Add rejection tests for negative and overflow `childValueCount`
- Add positive boundary tests for zero, valid, and `Integer.MAX_VALUE`
values
Closes #1098.
---
.../arrow/vector/complex/LargeListVector.java | 4 +-
.../apache/arrow/vector/TestLargeListVector.java | 89 ++++++++++++++++++++++
2 files changed, 91 insertions(+), 2 deletions(-)
diff --git
a/vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java
b/vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java
index 92dd3eaef..9368b5ce3 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java
@@ -1041,8 +1041,8 @@ public class LargeListVector extends BaseValueVector
* TODO: revisit when 64-bit vectors are supported
*/
Preconditions.checkArgument(
- childValueCount <= Integer.MAX_VALUE || childValueCount >=
Integer.MIN_VALUE,
- "LargeListVector doesn't yet support 64-bit allocations: %s",
+ childValueCount >= 0 && childValueCount <= Integer.MAX_VALUE,
+ "LargeListVector childValueCount must be in [0, Integer.MAX_VALUE] but
was: %s",
childValueCount);
vector.setValueCount((int) childValueCount);
}
diff --git
a/vector/src/test/java/org/apache/arrow/vector/TestLargeListVector.java
b/vector/src/test/java/org/apache/arrow/vector/TestLargeListVector.java
index bf9bba9c7..b6823fbf3 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestLargeListVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestLargeListVector.java
@@ -21,7 +21,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
import java.util.ArrayList;
import java.util.Arrays;
@@ -29,6 +31,7 @@ import java.util.List;
import java.util.UUID;
import org.apache.arrow.memory.ArrowBuf;
import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.OutOfMemoryException;
import org.apache.arrow.vector.complex.BaseRepeatedValueVector;
import org.apache.arrow.vector.complex.LargeListVector;
import org.apache.arrow.vector.complex.ListVector;
@@ -42,6 +45,7 @@ import org.apache.arrow.vector.types.Types.MinorType;
import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;
+import org.apache.arrow.vector.util.OversizedAllocationException;
import org.apache.arrow.vector.util.TransferPair;
import org.apache.arrow.vector.util.UuidUtility;
import org.junit.jupiter.api.AfterEach;
@@ -1127,4 +1131,89 @@ public class TestLargeListVector {
}
writer.endList();
}
+
+ @Test
+ public void testSetValueCountRejectsNegativeChildValueCount() {
+ try (final LargeListVector vector = LargeListVector.empty("list",
allocator)) {
+ vector.addOrGetVector(FieldType.nullable(MinorType.INT.getType()));
+ vector.allocateNew();
+
+ // Write a negative value into the offset buffer to simulate a corrupted
offset.
+ // The Preconditions check should reject any negative childValueCount.
+ vector.getOffsetBuffer().setLong(LargeListVector.OFFSET_WIDTH, -1L);
+ vector.setLastSet(0);
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> vector.setValueCount(1),
+ "setValueCount should reject negative childValueCount");
+ }
+ }
+
+ @Test
+ public void testSetValueCountRejectsOverflowChildValueCount() {
+ try (final LargeListVector vector = LargeListVector.empty("list",
allocator)) {
+ vector.addOrGetVector(FieldType.nullable(MinorType.INT.getType()));
+ vector.allocateNew();
+
+ // Write a value exceeding Integer.MAX_VALUE into the offset buffer
+ vector.getOffsetBuffer().setLong(LargeListVector.OFFSET_WIDTH, (long)
Integer.MAX_VALUE + 1L);
+ vector.setLastSet(0);
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> vector.setValueCount(1),
+ "setValueCount should reject childValueCount exceeding
Integer.MAX_VALUE");
+ }
+ }
+
+ @Test
+ public void testSetValueCountAcceptsZeroChildValueCount() {
+ try (final LargeListVector vector = LargeListVector.empty("list",
allocator)) {
+ vector.addOrGetVector(FieldType.nullable(MinorType.INT.getType()));
+ vector.allocateNew();
+
+ // childValueCount = 0 when valueCount = 0 (no elements)
+ vector.setValueCount(0);
+ assertEquals(0, vector.getValueCount());
+ }
+ }
+
+ @Test
+ public void testSetValueCountAcceptsValidChildValueCount() {
+ try (final LargeListVector vector = LargeListVector.empty("list",
allocator)) {
+ vector.addOrGetVector(FieldType.nullable(MinorType.INT.getType()));
+ vector.allocateNew();
+
+ // Write a valid childValueCount (5) into the offset buffer
+ vector.getOffsetBuffer().setLong(LargeListVector.OFFSET_WIDTH, 5L);
+ vector.setLastSet(0);
+ vector.setValueCount(1);
+ assertEquals(1, vector.getValueCount());
+ }
+ }
+
+ @Test
+ public void testSetValueCountAcceptsMaxIntChildValueCount() {
+ try (final LargeListVector vector = LargeListVector.empty("list",
allocator)) {
+ vector.addOrGetVector(FieldType.nullable(MinorType.INT.getType()));
+ vector.allocateNew();
+
+ // Write Integer.MAX_VALUE into the offset buffer to verify the upper
boundary is inclusive.
+ // The Preconditions check should accept this value (not throw
IllegalArgumentException).
+ // The child vector may throw OversizedAllocationException due to memory
limits,
+ // which is expected and unrelated to the precondition validation.
+ vector.getOffsetBuffer().setLong(LargeListVector.OFFSET_WIDTH,
Integer.MAX_VALUE);
+ vector.setLastSet(0);
+ try {
+ vector.setValueCount(1);
+ } catch (IllegalArgumentException e) {
+ fail("setValueCount should not reject childValueCount =
Integer.MAX_VALUE", e);
+ } catch (OversizedAllocationException | OutOfMemoryException e) {
+ // OversizedAllocationException or other allocation errors are expected
+ // when trying to allocate Integer.MAX_VALUE elements — this is fine,
+ // the precondition check itself passed.
+ }
+ }
+ }
}