Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,14 @@ public void setBigEndian(int index, byte[] value) {
BitVectorHelper.setBit(validityBuffer, index);
final int length = value.length;

// Reject an oversized value before any bytes are written. The little-endian path below
// copies all `length` bytes into the fixed TYPE_WIDTH slot with unchecked native writes,
// so validating after the copy would leave adjacent memory already corrupted.
if (length > TYPE_WIDTH) {
throw new IllegalArgumentException(
"Invalid decimal value length. Valid length in [1 - 32], got " + length);
}

// do the bound check.
valueBuffer.checkBytes((long) index * TYPE_WIDTH, (long) (index + 1) * TYPE_WIDTH);

Expand Down Expand Up @@ -243,8 +251,6 @@ public void setBigEndian(int index, byte[] value) {
return;
}
}
throw new IllegalArgumentException(
"Invalid decimal value length. Valid length in [1 - 32], got " + length);
}

/**
Expand Down Expand Up @@ -308,6 +314,11 @@ public void setBigEndianSafe(int index, long start, ArrowBuf buffer, int length)
handleSafe(index);
BitVectorHelper.setBit(validityBuffer, index);

if (length > TYPE_WIDTH) {
throw new IllegalArgumentException(
"Invalid decimal value length. Valid length in [1 - 32], got " + length);
}

// do the bound checks.
buffer.checkBytes(start, start + length);
valueBuffer.checkBytes((long) index * TYPE_WIDTH, (long) (index + 1) * TYPE_WIDTH);
Expand Down
15 changes: 13 additions & 2 deletions vector/src/main/java/org/apache/arrow/vector/DecimalVector.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ public void setBigEndian(int index, byte[] value) {
BitVectorHelper.setBit(validityBuffer, index);
final int length = value.length;

// Reject an oversized value before any bytes are written. The little-endian path below
// copies all `length` bytes into the fixed TYPE_WIDTH slot with unchecked native writes,
// so validating after the copy would leave adjacent memory already corrupted.
if (length > TYPE_WIDTH) {
throw new IllegalArgumentException(
"Invalid decimal value length. Valid length in [1 - 16], got " + length);
}

// do the bound check.
valueBuffer.checkBytes((long) index * TYPE_WIDTH, (long) (index + 1) * TYPE_WIDTH);

Expand Down Expand Up @@ -241,8 +249,6 @@ public void setBigEndian(int index, byte[] value) {
return;
}
}
throw new IllegalArgumentException(
"Invalid decimal value length. Valid length in [1 - 16], got " + length);
}

/**
Expand Down Expand Up @@ -306,6 +312,11 @@ public void setBigEndianSafe(int index, long start, ArrowBuf buffer, int length)
handleSafe(index);
BitVectorHelper.setBit(validityBuffer, index);

if (length > TYPE_WIDTH) {
throw new IllegalArgumentException(
"Invalid decimal value length. Valid length in [1 - 16], got " + length);
}

// do the bound checks.
buffer.checkBytes(start, start + length);
valueBuffer.checkBytes((long) index * TYPE_WIDTH, (long) (index + 1) * TYPE_WIDTH);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,25 @@ public void decimalBE2NE() {
}
}

@Test
public void setBigEndianOversizedDoesNotCorruptNeighbor() {
try (Decimal256Vector decimalVector =
TestUtils.newVector(
Decimal256Vector.class, "decimal", new ArrowType.Decimal(60, 0, 256), allocator)) {
decimalVector.allocateNew(2);

// A value whose low bytes are all non-zero, stored in the slot right after the target.
final BigInteger neighbor = new BigInteger("305419896"); // 0x12345678
decimalVector.setBigEndian(1, neighbor.toByteArray());

// A value longer than the 32-byte decimal must be rejected before anything is written.
assertThrows(
IllegalArgumentException.class, () -> decimalVector.setBigEndian(0, new byte[40]));

assertEquals(neighbor, decimalVector.getObject(1).unscaledValue());
}
}

@Test
public void setUsingArrowBufOfLEInts() {
try (Decimal256Vector decimalVector =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
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;

Expand Down Expand Up @@ -279,6 +280,25 @@ public void decimalBE2NE() {
}
}

@Test
public void setBigEndianOversizedDoesNotCorruptNeighbor() {
try (DecimalVector decimalVector =
TestUtils.newVector(
DecimalVector.class, "decimal", new ArrowType.Decimal(38, 0, 128), allocator)) {
decimalVector.allocateNew(2);

// A value whose low bytes are all non-zero, stored in the slot right after the target.
final BigInteger neighbor = new BigInteger("305419896"); // 0x12345678
decimalVector.setBigEndian(1, neighbor.toByteArray());

// A value longer than the 16-byte decimal must be rejected before anything is written.
assertThrows(
IllegalArgumentException.class, () -> decimalVector.setBigEndian(0, new byte[24]));

assertEquals(neighbor, decimalVector.getObject(1).unscaledValue());
}
}

@Test
public void setUsingArrowBufOfInts() {
try (DecimalVector decimalVector =
Expand Down
Loading