techgopal opened a new issue, #11691:
URL: https://github.com/apache/arrow-rs/issues/11691

   ### Describe the bug
   
   Reading a nullable column whose definition levels contain a bit-packed run 
that is longer than the remaining level data panics in `arrow-buffer` instead 
of returning an error.
   
   `PackedDecoder::next_rle_block` 
(`parquet/src/arrow/record_reader/definition_levels.rs`) sets `packed_count` 
from the run header, and `PackedDecoder::read` then calls
   
   ```rust
   buffer.append_packed_range(offset..offset + to_read, self.data.as_ref());
   ```
   
   without checking that `self.data` holds `ceil((offset + to_read) / 8)` 
bytes. `BooleanBufferBuilder::append_packed_range` documents that it panics in 
that case, and it does, from `BitChunks::new`:
   
   ```
   assert!(ceil(end, 8) <= buffer.len(), "offset + len out of bounds");
   ```
   
   This is different from #11516 / #11517 (overflow of the run length): here 
the run length is small and valid, it just exceeds the bytes available in the 
page.
   
   ### To Reproduce
   
   With `parquet = "60.0.0"`, `arrow-array = "60.0.0"`, `arrow-schema = 
"60.0.0"`, `bytes = "1"`:
   
   ```rust
   use std::sync::Arc;
   
   use arrow_array::{Int32Array, RecordBatch};
   use arrow_schema::{DataType, Field, Schema};
   use bytes::Bytes;
   use parquet::arrow::arrow_reader::ParquetRecordBatchReaderBuilder;
   use parquet::arrow::ArrowWriter;
   use parquet::file::properties::WriterProperties;
   
   fn main() {
       // A nullable INT32 column with every other value null: the data page
       // (v1, uncompressed, plain) starts with the definition levels as
       // [4-byte length = 127][bit-packed header 0x7F (63 groups = 504 
levels)][63 bytes]...
       let array: Int32Array = (0..1000).map(|i| (i % 2 == 
0).then_some(i)).collect();
       let schema = Arc::new(Schema::new(vec![Field::new("c", DataType::Int32, 
true)]));
       let batch = RecordBatch::try_new(schema.clone(), 
vec![Arc::new(array)]).unwrap();
       let props = 
WriterProperties::builder().set_dictionary_enabled(false).build();
       let mut buf = Vec::new();
       let mut writer = ArrowWriter::try_new(&mut buf, schema, 
Some(props)).unwrap();
       writer.write(&batch).unwrap();
       writer.close().unwrap();
   
       // Shrink the definition-level length prefix from 127 to 3 bytes; the
       // first bit-packed run header still claims 504 levels.
       let pattern = [0x7F, 0, 0, 0, 0x7F, 0x55];
       let pos = buf.windows(pattern.len()).position(|w| w == 
pattern).expect("pattern");
       buf[pos] = 3;
   
       let reader = ParquetRecordBatchReaderBuilder::try_new(Bytes::from(buf))
           .unwrap()
           .build()
           .unwrap();
       for batch in reader {
           println!("{:?}", batch.map(|b| b.num_rows()));
       }
   }
   ```
   
   Output (debug and release):
   
   ```
   thread 'main' panicked at 
arrow-buffer-60.0.0/src/util/bit_chunk_iterator.rs:235:9:
   offset + len out of bounds
   ```
   
   Backtrace (abridged):
   
   ```
   arrow_buffer::util::bit_chunk_iterator::BitChunks::new
   arrow_buffer::util::bit_util::byte_aligned_bitwise_bin_op_helper
   arrow_buffer::util::bit_util::apply_bitwise_binary_op
   arrow_buffer::builder::boolean::BooleanBufferBuilder::append_packed_range
   parquet::arrow::record_reader::definition_levels::PackedDecoder::read
   
<parquet::arrow::record_reader::definition_levels::DefinitionLevelBufferDecoder 
as DefinitionLevelDecoder>::read_def_levels
   
parquet::column::reader::GenericColumnReader<R,D,V>::read_records_with_reservation
   ...
   ```
   
   The same panic is reachable through `ParquetPushDecoder::try_decode`.
   
   ### Expected behavior
   
   The reader returns a `ParquetError` (e.g. unexpected end of definition level 
data) instead of panicking.
   
   ### Additional context
   
   - Reproduces with `parquet` / `arrow-buffer` 60.0.0 and on `main` at 
33d338ddf (`definition_levels.rs:446`).
   - Found while fuzzing a Parquet reader built on arrow-rs. Related: #11516, 
#9742.
   


-- 
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