Skip to content

GH-55388: [C++][Parquet] Reject out-of-bounds dictionary indices in all RLE_DICTIONARY decode paths - #55389

Open
b84330808 wants to merge 1 commit into
apache:mainfrom
b84330808:parquet-dict-index-bounds
Open

b84330808 wants to merge 1 commit into
apache:mainfrom
b84330808:parquet-dict-index-bounds

Conversation

@b84330808

Copy link
Copy Markdown

Rationale for this change

Out-of-bounds dictionary indices are rejected by DictDecoder::Decode() and DecodeArrow(), but:

  • DecodeIndices() and DecodeIndicesSpaced() don't check them at all, so ReadBatchWithDictionary() and read_dictionary reads can return indices past the end of the dictionary;
  • RleBitPackedDecoder doesn't stop when a run it is resuming (or a long bit-packed run) has an invalid index: it moves on to the next run. That silently skips values, and for an RLE run it loops forever in release builds (and hits a DCHECK in debug builds).

What changes are included in this PR?

  • RleBitPackedDecoder::ProcessValues() and GetSpaced() stop as soon as a run is not entirely consumed while the batch is not full, which only happens on invalid input, instead of resuming with the next run.
  • DictDecoderImpl::DecodeIndices() (both overloads) and DecodeIndicesSpaced() check the decoded indices against the dictionary length, like the other dictionary decoding methods do.

Are these changes tested?

Yes, new tests in encoding_test.cc (all DictDecoder methods with an out-of-bounds index) and rle_encoding_test.cc (resuming a partially read RLE run). Without the fix, the Parquet tests fail and the RLE test hits the DCHECK in debug builds; in release builds it doesn't return.

Are there any user-facing changes?

Reading Parquet data with out-of-bounds dictionary indices now errors out in these code paths.

This PR contains a "Critical Fix". Invalid dictionary indices could make the decoder silently return values from the following run (incorrect data) or loop forever.

Was AI used for this PR?

In accordance to the AI generation guidelines, please disclose below whether and how AI was used in this PR.

PR code and description written by:

  • Human
  • AI

Reviewed before submission by:

  • Human
  • AI
  • Not reviewed

I used Claude Code to find this (with a local fuzz harness over the RecordReader / low-level reader APIs) and to write the change and tests.

…TIONARY decode paths

DictDecoder::DecodeIndices() and DecodeIndicesSpaced() did not check the
decoded indices against the dictionary length, so ReadBatchWithDictionary()
and read_dictionary reads could return indices past the end of the
dictionary.

RleBitPackedDecoder also did not stop when a run it was resuming, or a
bit-packed run longer than one decoding chunk, held an invalid dictionary
index: it moved on to the next run. That silently skipped values, and for
an RLE run it looped forever in release builds (DCHECK failure in debug
builds).

Stop decoding as soon as a run is not entirely consumed while the batch is
not full, and check the indices in the DecodeIndices methods like the other
dictionary decoding methods do.
@github-actions github-actions Bot added the awaiting review Awaiting review label Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

⚠️ GitHub issue #55388 has been automatically assigned in GitHub to PR creator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant