This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-11658-5effeb51234f713c4cc81070b038f9dfc58e6cfd
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git

commit e30b544d13f78d5644c3d587a9d0f98a5dc6b311
Author: Aleksandar Zecevic <[email protected]>
AuthorDate: Sun Oct 18 01:46:37 2026 +0000

    Return errors for undersized struct validity buffers in IPC reader (#11658)
    
    # Which issue does this PR close?
    
    - Relates to #7124 and #11044.
    
    # Rationale for this change
    
    #11044 made validated IPC reads return an error, instead of panicking,
    when a validity buffer is too short. Its check is in
    `ArrayDataBuilder::build`. The IPC reader does not build struct arrays
    with `ArrayDataBuilder`: when a struct node has `null_count > 0`,
    `create_struct_array` calls `BooleanBuffer::new(null_buffer, 0, len)`
    directly. A struct validity buffer shorter than `ceil(len / 8)` bytes
    therefore still panics with "buffer not large enough", even with
    validation enabled.
    
    # What changes are included in this PR?
    
    - `create_struct_array` checks the validity buffer length before it
    builds the `BooleanBuffer`, and returns the same error as #11044
    (`null_bit_buffer size too small. got {} needed {}`).
    - The check runs only when validation is enabled; the
    `with_skip_validation` path is unchanged. `arrow-ipc` has no
    `force_validate` feature, so unlike #11044 there is no condition on it.
    - A regression test next to the #11044 one: it writes a `StructArray`
    with a short validity buffer (built with `StructArray::new_unchecked`)
    with `StreamWriter` and reads it back with `StreamReader`.
    
    # Are these changes tested?
    
    Yes. `test_stream_reader_rejects_short_struct_validity_buffer` panics
    with "buffer not large enough" without the fix and passes with it.
    
    # Are there any significant user-facing changes?
    
    No.
---
 arrow-ipc/src/reader.rs | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/arrow-ipc/src/reader.rs b/arrow-ipc/src/reader.rs
index f891b6dd3c..35ca545ac3 100644
--- a/arrow-ipc/src/reader.rs
+++ b/arrow-ipc/src/reader.rs
@@ -369,6 +369,17 @@ impl RecordBatchDecoder<'_> {
         let skip_validation = self.skip_validation.get();
 
         let nulls = if null_count > 0 {
+            if !skip_validation {
+                // Check before constructing the BooleanBuffer, which would 
otherwise panic.
+                let needed_len = len.div_ceil(8);
+                if null_buffer.len() < needed_len {
+                    return Err(ArrowError::InvalidArgumentError(format!(
+                        "null_bit_buffer size too small. got {} needed {}",
+                        null_buffer.len(),
+                        needed_len
+                    )));
+                }
+            }
             let validity_buffer = BooleanBuffer::new(null_buffer, 0, len);
             let null_buffer = if skip_validation {
                 // safety: flag can only be set via unsafe code
@@ -3618,6 +3629,30 @@ mod tests {
         );
     }
 
+    #[test]
+    fn test_stream_reader_rejects_short_struct_validity_buffer() {
+        // Serialize a StructArray with too few validity bits.
+        let fields = Fields::from(vec![Field::new("b", DataType::Int32, 
false)]);
+        let child: ArrayRef = Arc::new(Int32Array::from_iter_values(0..8000));
+        let nulls = NullBuffer::from(&[true, false, true, false]);
+        let array: ArrayRef =
+            unsafe { Arc::new(StructArray::new_unchecked(fields, vec![child], 
Some(nulls))) };
+        let batch = RecordBatch::try_from_iter([("a", array)]).unwrap();
+
+        let mut stream = Vec::new();
+        let mut writer =
+            crate::writer::StreamWriter::try_new(&mut stream, 
&batch.schema()).unwrap();
+        writer.write(&batch).unwrap();
+        writer.finish().unwrap();
+
+        let mut reader = StreamReader::try_new(Cursor::new(stream), 
None).unwrap();
+        let err = reader.next().unwrap().unwrap_err();
+        assert_eq!(
+            err.to_string(),
+            "Invalid argument error: null_bit_buffer size too small. got 1 
needed 1000"
+        );
+    }
+
     #[test]
     fn test_invalid_struct_array_ipc_read_errors() {
         let a_field = Field::new("a", DataType::Int32, false);

Reply via email to