Skip to content

AVRO-4323: [Java] Bound DataFileStream block size against available input before allocating the block buffer - #3919

Merged
RyanSkraba merged 2 commits into
apache:mainfrom
iemejia:AVRO-4323-datafilestream-block-size
Aug 6, 2026
Merged

AVRO-4323: [Java] Bound DataFileStream block size against available input before allocating the block buffer#3919
RyanSkraba merged 2 commits into
apache:mainfrom
iemejia:AVRO-4323-datafilestream-block-size

Conversation

@iemejia

@iemejia iemejia commented Aug 5, 2026

Copy link
Copy Markdown
Member

What changes were proposed in this pull request?

When reading an Avro data (container) file, DataFileStream reads each block's declared size as a long and validated it only against the Integer range before allocating the block byte[] buffer (in DataFileStream.DataBlock). For a malformed, corrupted, or truncated file, the declared block size can be much larger than the number of bytes actually present, so the reader eagerly allocated a very large buffer on the first hasNext()/next() call before any block byte had been read.

This adds a check: when the number of bytes remaining in the input is known (byte-array- or known-length-stream-backed decoders), a declared block size larger than the bytes remaining is rejected with a clear IOException before allocating. The check is skipped when the remaining count is unknown (-1), so non-seekable streams are unaffected.

How was this patch tested?

  • New tests in TestDataFileReader:
    • oversizedBlockSizeIsRejectedBeforeAllocation — a crafted file whose block header declares a size near Integer.MAX_VALUE with no block bytes now fails fast instead of attempting a large allocation.
    • validFileWithSingleRecordStillReads — negative control confirming a valid file still reads.
  • Full avro module test suite passes.

JIRA

…nput

When reading a data (container) file, DataFileStream validated the declared
block size only against the Integer range before allocating the block buffer.
For a malformed, corrupted, or truncated file the declared size can greatly
exceed the bytes actually present, so the reader eagerly allocated a large
buffer before reading any block byte.

Reject a declared block size that exceeds the number of bytes remaining in the
input when that count is known (byte-array- or known-length-stream-backed
decoders), so reading a malformed file fails fast with a clear IOException.
The check is skipped when the remaining count is unknown (-1).
@github-actions github-actions Bot added the Java Pull Requests for Java binding label Aug 5, 2026
@iemejia
iemejia requested a lite review from Copilot August 6, 2026 09:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the Java DataFileStream container-file reader against malformed/truncated inputs that declare an unrealistically large block size, by validating the declared size against the decoder’s known remaining input bytes before allocating the block buffer.

Changes:

  • Add a remaining-bytes guard in DataFileStream.hasNextBlock() to fail fast on oversized declared block sizes when the remaining byte count is known.
  • Add a regression test that crafts a malformed container to ensure the oversized block size is rejected before allocation.
  • Add a negative-control test to confirm valid single-record files still read successfully.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
lang/java/avro/src/main/java/org/apache/avro/file/DataFileStream.java Adds remaining-bytes validation before block buffer allocation in hasNextBlock().
lang/java/avro/src/test/java/org/apache/avro/TestDataFileReader.java Adds tests covering oversized declared block sizes and a valid-read regression case.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +330 to +334
int remaining = vin.remainingBytes();
if (remaining >= 0 && blockSize > remaining) {
throw new IOException("Block size " + blockSize + " exceeds the number of bytes remaining in the input ("
+ remaining + "). The file is likely corrupted or truncated.");
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Updated the guard to require blockSize + DataFileConstants.SYNC_SIZE to fit in the remaining bytes, so a file truncated right before the sync marker is now rejected before the block buffer is allocated. (2a09e3c)

Comment on lines +350 to +356
AvroRuntimeException exception = assertThrows(AvroRuntimeException.class, () -> {
DataFileStream<Object> reader = new DataFileStream<>(new ByteArrayInputStream(malformed),
new GenericDatumReader<>());
while (reader.hasNext()) {
reader.next();
}
});

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — the regression test now wraps the DataFileStream in try-with-resources so it is closed even when the expected exception is thrown, matching the pattern used elsewhere in this class. (2a09e3c)

…d; close reader in test

Include the trailing sync-marker length (DataFileConstants.SYNC_SIZE) in the
remaining-bytes check so a file truncated right before the sync marker is
rejected before allocating the block buffer, and use try-with-resources in the
regression test.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@iemejia
iemejia requested a review from RyanSkraba August 6, 2026 09:25
@RyanSkraba
RyanSkraba merged commit 23aad52 into apache:main Aug 6, 2026
9 checks passed
RyanSkraba pushed a commit that referenced this pull request Aug 6, 2026
…nput before allocating the block buffer (#3919)

* AVRO-4323: [Java] Bound DataFileStream block size against available input

When reading a data (container) file, DataFileStream validated the declared
block size only against the Integer range before allocating the block buffer.
For a malformed, corrupted, or truncated file the declared size can greatly
exceed the bytes actually present, so the reader eagerly allocated a large
buffer before reading any block byte.

Reject a declared block size that exceeds the number of bytes remaining in the
input when that count is known (byte-array- or known-length-stream-backed
decoders), so reading a malformed file fails fast with a clear IOException.
The check is skipped when the remaining count is unknown (-1).

* AVRO-4323: Address review: account for sync marker in block-size guard; close reader in test

Include the trailing sync-marker length (DataFileConstants.SYNC_SIZE) in the
remaining-bytes check so a file truncated right before the sync marker is
rejected before allocating the block buffer, and use try-with-resources in the
regression test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Java Pull Requests for Java binding

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants