1fanwang opened a new pull request, #51128:
URL: https://github.com/apache/arrow/pull/51128

   ### Rationale for this change
   
   Reading a Parquet file that contains a corrupt `DELTA_BINARY_PACKED` page 
makes the decoder allocate memory far larger than the page it is reading. A 
10-byte page allocates 1 MiB, and because the miniblock count in the page 
header is a VLQ `uint32`, a header can ask for close to 4 GiB. The read then 
fails with `Decode bit-width EOF`, which does not say which header field was 
wrong.
   
   Before this change, a reader hitting such a page allocates whatever the 
header asked for and then reports an unrelated error. After it, the reader 
allocates nothing and the error names both the miniblock count and the bytes 
actually available.
   
   ### What changes are included in this PR?
   
   `InitHeader` now rejects a page whose miniblock count is larger than the 
bytes remaining in the page. `InitBlock` reads one bit-width byte per 
miniblock, so a page that trips this check could never have decoded, and no 
page that read correctly before is affected. A page holding a single value 
keeps that value in the header and never initializes a block, so the check 
skips it.
   
   ### Are these changes tested?
   
   Yes, by two new cases in `parquet-encoding-test`. 
`RejectsMiniblockWidthsLargerThanInput` decodes the 10-byte page and asserts 
both the reported error and, via a `ProxyMemoryPool`, that nothing was 
allocated. `SingleValueRoundTrip` covers the single-value page that the check 
has to leave alone.
   
   <details>
   <summary>Red, with the decoder change reverted to the parent commit</summary>
   
   ```console
   $ git checkout HEAD~1 cpp/src/parquet/decoder.cc
   $ ninja parquet-encoding-test
   $ ./debug/parquet-encoding-test 
--gtest_filter='*SingleValueRoundTrip*:*RejectsMiniblockWidthsLargerThanInput*'
   [==========] Running 4 tests from 2 test suites.
   [ RUN      ] TestDeltaBitPackEncoding/0.SingleValueRoundTrip
   [       OK ] TestDeltaBitPackEncoding/0.SingleValueRoundTrip (8 ms)
   [ RUN      ] TestDeltaBitPackEncoding/0.RejectsMiniblockWidthsLargerThanInput
   cpp/src/parquet/encoding_test.cc:1933: Failure
   Value of: err
   Expected: is an object whose given property has substring "the number of 
miniblocks per block (1048576) is larger than the number of bytes remaining in 
the page (1)"
     Actual: Unexpected end of stream: Decode bit-width EOF (of type 
parquet::ParquetException), whose given property is 0x46e0228b0 pointing to 
"Unexpected end of stream: Decode bit-width EOF" (of type char const*)
   cpp/src/parquet/encoding_test.cc:1934: Failure
   Expected equality of these values:
     pool.bytes_allocated()
       Which is: 1048576
     0
   [  FAILED  ] 
TestDeltaBitPackEncoding/0.RejectsMiniblockWidthsLargerThanInput, where 
TypeParam = parquet::PhysicalType<(parquet::Type::type)1> (2 ms)
   [ RUN      ] TestDeltaBitPackEncoding/1.SingleValueRoundTrip
   [       OK ] TestDeltaBitPackEncoding/1.SingleValueRoundTrip (0 ms)
   [ RUN      ] TestDeltaBitPackEncoding/1.RejectsMiniblockWidthsLargerThanInput
   cpp/src/parquet/encoding_test.cc:1933: Failure
   Value of: err
   Expected: is an object whose given property has substring "the number of 
miniblocks per block (1048576) is larger than the number of bytes remaining in 
the page (1)"
     Actual: Unexpected end of stream: Decode bit-width EOF (of type 
parquet::ParquetException), whose given property is 0x46e0227f0 pointing to 
"Unexpected end of stream: Decode bit-width EOF" (of type char const*)
   cpp/src/parquet/encoding_test.cc:1934: Failure
   Expected equality of these values:
     pool.bytes_allocated()
       Which is: 1048576
     0
   [  FAILED  ] 
TestDeltaBitPackEncoding/1.RejectsMiniblockWidthsLargerThanInput, where 
TypeParam = parquet::PhysicalType<(parquet::Type::type)2> (0 ms)
   [==========] 4 tests from 2 test suites ran. (12 ms total)
   [  PASSED  ] 2 tests.
   [  FAILED  ] 2 tests, listed below:
   [  FAILED  ] 
TestDeltaBitPackEncoding/0.RejectsMiniblockWidthsLargerThanInput, where 
TypeParam = parquet::PhysicalType<(parquet::Type::type)1>
   [  FAILED  ] 
TestDeltaBitPackEncoding/1.RejectsMiniblockWidthsLargerThanInput, where 
TypeParam = parquet::PhysicalType<(parquet::Type::type)2>
    2 FAILED TESTS
   ```
   </details>
   
   <details>
   <summary>Green, with the fix applied</summary>
   
   ```console
   $ git checkout HEAD cpp/src/parquet/decoder.cc
   $ ninja parquet-encoding-test
   $ ./debug/parquet-encoding-test 
--gtest_filter='*SingleValueRoundTrip*:*RejectsMiniblockWidthsLargerThanInput*'
   [==========] Running 4 tests from 2 test suites.
   [ RUN      ] TestDeltaBitPackEncoding/0.SingleValueRoundTrip
   [       OK ] TestDeltaBitPackEncoding/0.SingleValueRoundTrip (4 ms)
   [ RUN      ] TestDeltaBitPackEncoding/0.RejectsMiniblockWidthsLargerThanInput
   [       OK ] 
TestDeltaBitPackEncoding/0.RejectsMiniblockWidthsLargerThanInput (0 ms)
   [ RUN      ] TestDeltaBitPackEncoding/1.SingleValueRoundTrip
   [       OK ] TestDeltaBitPackEncoding/1.SingleValueRoundTrip (0 ms)
   [ RUN      ] TestDeltaBitPackEncoding/1.RejectsMiniblockWidthsLargerThanInput
   [       OK ] 
TestDeltaBitPackEncoding/1.RejectsMiniblockWidthsLargerThanInput (0 ms)
   [==========] 4 tests from 2 test suites ran. (5 ms total)
   [  PASSED  ] 4 tests.
   ```
   </details>
   
   The full encoding suite and the neighbouring Parquet suites 
(`parquet-internals-test`, `parquet-reader-test`, 
`parquet-arrow-reader-writer-test`) report identical results before and after 
the change, their pre-existing failures all caused by test data files absent 
from this checkout.
   
   ### Are there any user-facing changes?
   
   A `DELTA_BINARY_PACKED` page whose header asks for more miniblocks than the 
page has bytes now raises a `ParquetException` naming both numbers, instead of 
allocating first and failing with `Decode bit-width EOF`.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to