DictionaryEncoder.retrieveIndexVector guards each index from the index vector with indexAsInt > dictionaryCount before transfer.copyValueSafe(indexAsInt, i). Valid indices are 0..dictionaryCount-1, so the check is off by one: an index equal to dictionaryCount is accepted and reads one slot past the dictionary vector, and a negative index (a signed index type with the high bit set) is not rejected either and also reaches copyValueSafe. The index vector is decoded from an IPC/C-data payload, so a crafted dictionary-encoded batch yields an out-of-bounds read of the dictionary vector, exposing adjacent off-heap memory when bounds checking is disabled via arrow.enable_unsafe_memory_access.
The same helper backs DictionaryEncoder.decode, ListSubfieldEncoder.decodeListSubField and StructSubfieldEncoder.decode.
The bound should be indexAsInt < 0 || indexAsInt >= dictionaryCount.
DictionaryEncoder.retrieveIndexVectorguards each index from the index vector withindexAsInt > dictionaryCountbeforetransfer.copyValueSafe(indexAsInt, i). Valid indices are0..dictionaryCount-1, so the check is off by one: an index equal todictionaryCountis accepted and reads one slot past the dictionary vector, and a negative index (a signed index type with the high bit set) is not rejected either and also reachescopyValueSafe. The index vector is decoded from an IPC/C-data payload, so a crafted dictionary-encoded batch yields an out-of-bounds read of the dictionary vector, exposing adjacent off-heap memory when bounds checking is disabled viaarrow.enable_unsafe_memory_access.The same helper backs
DictionaryEncoder.decode,ListSubfieldEncoder.decodeListSubFieldandStructSubfieldEncoder.decode.The bound should be
indexAsInt < 0 || indexAsInt >= dictionaryCount.