DecimalVector.setBigEndian(int, byte[]) and Decimal256Vector.setBigEndian(int, byte[]) validate the input length only after copying the bytes. On a little-endian platform the method byte-swaps the value into the fixed 16/32-byte slot with a loop of unchecked MemoryUtil.putByte writes:
for (int byteIdx = 0; byteIdx < length; ++byteIdx) {
MemoryUtil.putByte(outAddress + byteIdx, value[length - 1 - byteIdx]);
}
...
throw new IllegalArgumentException("Invalid decimal value length. Valid length in [1 - 16], got " + length);
If value.length exceeds the type width, the loop writes past the slot into adjacent off-heap memory before the IllegalArgumentException is thrown. MemoryUtil.putByte does no bounds checking, so this corrupts memory in the default configuration. setBigEndianSafe(int, long, ArrowBuf, int) performs the same write with no length check at all.
Reproducer: write a value into slot 1, then call setBigEndian(0, new byte[24]) on a DecimalVector; slot 1 is left corrupted (the write spills 8 bytes into it) even though the call throws.
DecimalVector.setBigEndian(int, byte[]) and Decimal256Vector.setBigEndian(int, byte[]) validate the input length only after copying the bytes. On a little-endian platform the method byte-swaps the value into the fixed 16/32-byte slot with a loop of unchecked MemoryUtil.putByte writes:
If value.length exceeds the type width, the loop writes past the slot into adjacent off-heap memory before the IllegalArgumentException is thrown. MemoryUtil.putByte does no bounds checking, so this corrupts memory in the default configuration. setBigEndianSafe(int, long, ArrowBuf, int) performs the same write with no length check at all.
Reproducer: write a value into slot 1, then call setBigEndian(0, new byte[24]) on a DecimalVector; slot 1 is left corrupted (the write spills 8 bytes into it) even though the call throws.