Repository navigation
Conversation
IPC readers trust the offsets, views and run ends of the batches they load. A malformed batch, such as one with an offset past the end of its data buffer, is read past the end of the buffer, without bounds checks when arrow.enable_unsafe_memory_access is set. Add ArrowReader.setValidateBatches, off by default, which checks every record batch and dictionary batch with ValueVectorUtility.validateFull once it is loaded, clears it if the check fails and throws the validation error. A delta dictionary is checked before it is appended. For this, validateFull now works on every vector type an IPC batch can hold: - ListViewVector and LargeListViewVector are supported by the type, buffer and data validators. They threw UnsupportedOperationException. - RunEndEncodedVector is supported by the data validator: run ends must be strictly increasing, and each run must have a value. - FixedSizeListVector no longer reads an offset buffer it does not have, which threw UnsupportedOperationException for any non-empty vector. - MapVector is accepted by the type validator, which rejected every map as not being of type List. - Views are checked against the data buffers before their values are read. - Dense union offsets must not be negative, and type ids must have a sub-vector. TestRoundTrip now reads every batch with the option on. The test populator for LargeListViewVector wrote 4-byte sizes at the wrong stride and now writes 64-bit offsets and sizes.
jbonofre
requested review from
laurentgo,
lidavidm and
wgtmac
as code owners
November 12, 2026 14:17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What's Changed
Found while reviewing #1422. This change is independent of it and based on
main.New option:
ArrowReader.setValidateBatches(boolean), off by default. When it is on, every record batch and dictionary batch is checked withValueVectorUtility.validateFullonce it is loaded. A batch that fails the check is cleared, and the load throwsValidateUtil.ValidateException. A delta dictionary is checked before it is appended, so an invalid delta leaves the dictionary unchanged. The option is meant for reading untrusted data: without it, a malformed batch, such as aUtf8column with an offset past the end of its data buffer, loads fine and is then read out of bounds (without bounds checks whenarrow.enable_unsafe_memory_accessis set). The option is off by default because the check reads every value.validateFullnow works on every type a batch can hold. Turning the option on inTestRoundTripshowed that it did not:ListViewVector/LargeListViewVector: added to the type, buffer and data validators (they threwUnsupportedOperationException). The data check rejects a non-empty list view outside the inner vector.RunEndEncodedVector: added to the data validator. Run ends must be strictly increasing, and each run must have a value.FixedSizeListVector: the data validator no longer reads an offset buffer the vector does not have (UnsupportedOperationExceptionfor any non-empty vector).MapVector: the type validator accepted onlyArrowType.List, so it rejected every map.validateScalarsreads it.TestVariableWidthViewVector.testValidateInvalidOffsetsnow expects aValidateExceptioninstead of theIndexOutOfBoundsExceptionthe out-of-bounds read used to throw.Tests
TestArrowReaderValidation: a valid batch with string views, list views, large list views and run-end encoding passes. A batch with an invalid interior offset, and a dictionary batch with one, are rejected only when the option is on.TestValidateVectorFull: a case for each validator change.TestRoundTripreads every batch with the option on, which covers struct, union, dictionary, fixed-size list, map and the other round-tripped types.ValueVectorDataPopulator.setVector(LargeListViewVector, ...)wrote 4-byte sizes at the wrong stride. It now writes 64-bit offsets and sizes (it was not used by any other test).Without the validator changes, the new tests and the fixed-size list, map, list-as-map and union V5 round trips fail. The full
vectortest suite passes.Not covered: C Data imports carry no buffer sizes (the importer sizes buffers from the producer's length), and Flight's
FlightStreamloads batches with its ownVectorLoader.Closes #1466.