Skip to content

Commit 4d35fac

Browse files
committed
GH-XXXX: Defer byte[] allocation until after bounds check
Also, document that when enable_unsafe_memory_access is enabled, all bets are off. Reported by n0mi1k.
1 parent 91b4a2c commit 4d35fac

4 files changed

Lines changed: 43 additions & 8 deletions

File tree

memory/memory-core/src/main/java/org/apache/arrow/memory/ArrowBuf.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,24 @@ public void getBytes(long index, byte[] dst, int dstIndex, int length) {
731731
}
732732
}
733733

734+
/**
735+
* Copy data from this ArrowBuf into a newly allocated array.
736+
*
737+
* <p>This method is more resilient to invalid data inadvertently causing large allocations,
738+
* as the byte[] will not be allocated until we check the length.
739+
*
740+
* @param index index (0 based relative to the portion of memory this ArrowBuf has access to)
741+
* @param length length of data to copy from this ArrowBuf
742+
*/
743+
public byte[] getBytesAsArray(long index, int length) {
744+
checkIndex(index, length);
745+
byte[] dst = new byte[length];
746+
if (length != 0) {
747+
MemoryUtil.copyFromMemory(addr(index), dst, 0, length);
748+
}
749+
return dst;
750+
}
751+
734752
/**
735753
* Copy data from a given byte array into this ArrowBuf starting at a given index.
736754
*
@@ -1008,8 +1026,7 @@ public int setBytes(long index, InputStream in, int length) throws IOException {
10081026
/**
10091027
* Copy a certain length of bytes from this ArrowBuf at a given index into the given OutputStream.
10101028
*
1011-
* @param index index index (0 based relative to the portion of memory this ArrowBuf has access
1012-
* to)
1029+
* @param index index (0 based relative to the portion of memory this ArrowBuf has access to)
10131030
* @param out dst stream to copy data into
10141031
* @param length length of data to copy
10151032
* @throws IOException on failing to write to stream

memory/memory-core/src/main/java/org/apache/arrow/memory/BoundsChecking.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
* "arrow.enable_unsafe_memory_access" or "drill.enable_unsafe_memory_access". The latter is
2525
* deprecated. The environmental variable is named "ARROW_ENABLE_UNSAFE_MEMORY_ACCESS". When both
2626
* the system property and the environmental variable are set, the system property takes precedence.
27+
*
28+
* <p>WARNING: disabling bounds checking means that out-of-bounds memory access is possible! This can
29+
* lead to security vulnerabilities. You should not read or write untrusted data when bounds checking
30+
* is disabled.
2731
*/
2832
public class BoundsChecking {
2933

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,7 +1556,6 @@ public int hashCode(int index, ArrowBufHasher hasher) {
15561556
*/
15571557
protected byte[] getData(int index) {
15581558
final int dataLength = getValueLength(index);
1559-
byte[] result = new byte[dataLength];
15601559
if (dataLength > INLINE_SIZE) {
15611560
// data is in the data buffer
15621561
// get buffer index
@@ -1566,12 +1565,11 @@ protected byte[] getData(int index) {
15661565
final int dataOffset =
15671566
viewBuffer.getInt(
15681567
((long) index * ELEMENT_SIZE) + LENGTH_WIDTH + PREFIX_WIDTH + BUF_INDEX_WIDTH);
1569-
dataBuffers.get(bufferIndex).getBytes(dataOffset, result, 0, dataLength);
1570-
} else {
1571-
// data is in the view buffer
1572-
viewBuffer.getBytes((long) index * ELEMENT_SIZE + BUF_INDEX_WIDTH, result, 0, dataLength);
1568+
ArrowBuf dataBuffer = dataBuffers.get(bufferIndex);
1569+
return dataBuffer.getBytesAsArray(dataOffset, dataLength);
15731570
}
1574-
return result;
1571+
// data is in the view buffer
1572+
return viewBuffer.getBytesAsArray((long) index * ELEMENT_SIZE + BUF_INDEX_WIDTH, dataLength);
15751573
}
15761574

15771575
protected void getData(int index, ReusableBuffer<?> buffer) {

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2921,4 +2921,20 @@ public void testValidate() {
29212921
assertTrue(e.getMessage().contains("Not enough capacity for data buffer"));
29222922
}
29232923
}
2924+
2925+
@Test
2926+
public void testValidateInvalidOffsets() {
2927+
try (final ViewVarCharVector vector = new ViewVarCharVector("v", allocator)) {
2928+
vector.allocateNew(8, 1);
2929+
vector.allocateOrGetLastDataBuffer(8);
2930+
var offsets = vector.getDataBuffer();
2931+
offsets.setInt(0, 64);
2932+
offsets.setInt(1, 0);
2933+
offsets.setInt(2, 0);
2934+
offsets.setInt(3, 1024);
2935+
vector.setValueCount(1);
2936+
vector.setIndexDefined(0);
2937+
assertThrows(IndexOutOfBoundsException.class, vector::validateFull);
2938+
}
2939+
}
29242940
}

0 commit comments

Comments
 (0)