Skip to content

Commit d4485f1

Browse files
committed
validate decimal byte length before writing in setBigEndian
1 parent 21b6a05 commit d4485f1

4 files changed

Lines changed: 65 additions & 4 deletions

File tree

vector/src/main/java/org/apache/arrow/vector/Decimal256Vector.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,14 @@ public void setBigEndian(int index, byte[] value) {
208208
BitVectorHelper.setBit(validityBuffer, index);
209209
final int length = value.length;
210210

211+
// Reject an oversized value before any bytes are written. The little-endian path below
212+
// copies all `length` bytes into the fixed TYPE_WIDTH slot with unchecked native writes,
213+
// so validating after the copy would leave adjacent memory already corrupted.
214+
if (length > TYPE_WIDTH) {
215+
throw new IllegalArgumentException(
216+
"Invalid decimal value length. Valid length in [1 - 32], got " + length);
217+
}
218+
211219
// do the bound check.
212220
valueBuffer.checkBytes((long) index * TYPE_WIDTH, (long) (index + 1) * TYPE_WIDTH);
213221

@@ -243,8 +251,6 @@ public void setBigEndian(int index, byte[] value) {
243251
return;
244252
}
245253
}
246-
throw new IllegalArgumentException(
247-
"Invalid decimal value length. Valid length in [1 - 32], got " + length);
248254
}
249255

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

317+
if (length > TYPE_WIDTH) {
318+
throw new IllegalArgumentException(
319+
"Invalid decimal value length. Valid length in [1 - 32], got " + length);
320+
}
321+
311322
// do the bound checks.
312323
buffer.checkBytes(start, start + length);
313324
valueBuffer.checkBytes((long) index * TYPE_WIDTH, (long) (index + 1) * TYPE_WIDTH);

vector/src/main/java/org/apache/arrow/vector/DecimalVector.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,14 @@ public void setBigEndian(int index, byte[] value) {
207207
BitVectorHelper.setBit(validityBuffer, index);
208208
final int length = value.length;
209209

210+
// Reject an oversized value before any bytes are written. The little-endian path below
211+
// copies all `length` bytes into the fixed TYPE_WIDTH slot with unchecked native writes,
212+
// so validating after the copy would leave adjacent memory already corrupted.
213+
if (length > TYPE_WIDTH) {
214+
throw new IllegalArgumentException(
215+
"Invalid decimal value length. Valid length in [1 - 16], got " + length);
216+
}
217+
210218
// do the bound check.
211219
valueBuffer.checkBytes((long) index * TYPE_WIDTH, (long) (index + 1) * TYPE_WIDTH);
212220

@@ -241,8 +249,6 @@ public void setBigEndian(int index, byte[] value) {
241249
return;
242250
}
243251
}
244-
throw new IllegalArgumentException(
245-
"Invalid decimal value length. Valid length in [1 - 16], got " + length);
246252
}
247253

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

315+
if (length > TYPE_WIDTH) {
316+
throw new IllegalArgumentException(
317+
"Invalid decimal value length. Valid length in [1 - 16], got " + length);
318+
}
319+
309320
// do the bound checks.
310321
buffer.checkBytes(start, start + length);
311322
valueBuffer.checkBytes((long) index * TYPE_WIDTH, (long) (index + 1) * TYPE_WIDTH);

vector/src/test/java/org/apache/arrow/vector/TestDecimal256Vector.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,25 @@ public void decimalBE2NE() {
283283
}
284284
}
285285

286+
@Test
287+
public void setBigEndianOversizedDoesNotCorruptNeighbor() {
288+
try (Decimal256Vector decimalVector =
289+
TestUtils.newVector(
290+
Decimal256Vector.class, "decimal", new ArrowType.Decimal(60, 0, 256), allocator)) {
291+
decimalVector.allocateNew(2);
292+
293+
// A value whose low bytes are all non-zero, stored in the slot right after the target.
294+
final BigInteger neighbor = new BigInteger("305419896"); // 0x12345678
295+
decimalVector.setBigEndian(1, neighbor.toByteArray());
296+
297+
// A value longer than the 32-byte decimal must be rejected before anything is written.
298+
assertThrows(
299+
IllegalArgumentException.class, () -> decimalVector.setBigEndian(0, new byte[40]));
300+
301+
assertEquals(neighbor, decimalVector.getObject(1).unscaledValue());
302+
}
303+
}
304+
286305
@Test
287306
public void setUsingArrowBufOfLEInts() {
288307
try (Decimal256Vector decimalVector =

vector/src/test/java/org/apache/arrow/vector/TestDecimalVector.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import static org.junit.jupiter.api.Assertions.assertEquals;
2020
import static org.junit.jupiter.api.Assertions.assertSame;
21+
import static org.junit.jupiter.api.Assertions.assertThrows;
2122
import static org.junit.jupiter.api.Assertions.assertTrue;
2223
import static org.junit.jupiter.api.Assertions.fail;
2324

@@ -279,6 +280,25 @@ public void decimalBE2NE() {
279280
}
280281
}
281282

283+
@Test
284+
public void setBigEndianOversizedDoesNotCorruptNeighbor() {
285+
try (DecimalVector decimalVector =
286+
TestUtils.newVector(
287+
DecimalVector.class, "decimal", new ArrowType.Decimal(38, 0, 128), allocator)) {
288+
decimalVector.allocateNew(2);
289+
290+
// A value whose low bytes are all non-zero, stored in the slot right after the target.
291+
final BigInteger neighbor = new BigInteger("305419896"); // 0x12345678
292+
decimalVector.setBigEndian(1, neighbor.toByteArray());
293+
294+
// A value longer than the 16-byte decimal must be rejected before anything is written.
295+
assertThrows(
296+
IllegalArgumentException.class, () -> decimalVector.setBigEndian(0, new byte[24]));
297+
298+
assertEquals(neighbor, decimalVector.getObject(1).unscaledValue());
299+
}
300+
}
301+
282302
@Test
283303
public void setUsingArrowBufOfInts() {
284304
try (DecimalVector decimalVector =

0 commit comments

Comments
 (0)