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

Jefffrey pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 1233a18ed7 add test to catch mis-aligned ipc buffers (#10405)
1233a18ed7 is described below

commit 1233a18ed7b007ecb05f6403e47e6bbfe6b5bb96
Author: RIchard Baah <[email protected]>
AuthorDate: Thu Jul 23 10:10:53 2026 -0400

    add test to catch mis-aligned ipc buffers (#10405)
    
    # Which issue does this PR close?
    
    <!--
    We generally require a GitHub issue to be filed for all bug fixes and
    enhancements and this helps us generate change logs for our releases.
    You can link an issue to this PR using the GitHub syntax.
    -->
    
    - Closes #10394.
    
    # Rationale for this change
    see #10394
    <!--
    Why are you proposing this change? If this is already explained clearly
    in the issue then this section is not needed.
    Explaining clearly why changes are proposed helps reviewers understand
    your changes and offer better suggestions for fixes.
    -->
    
    # What changes are included in this PR?
    Adds a single test `test_misaligned_buffers_error()` in
    `arrow-ipc/src/reader.rs` that verifies misaligned IPC buffers are
    caught by require_alignment = true. The test loops over byte offsets
    0..128 across both fixed-width (Int32, Int64) and variable-width
    (StringArray, LargeStringArray) array types, asserting that any offset
    not satisfying the type's alignment requirement returns a "Misaligned
    buffers" error, and that aligned offsets decode successfulldy.
    <!--
    There is no need to duplicate the description in the issue here but it
    is sometimes worth providing a summary of the individual changes in this
    PR.
    -->
    
    # Are these changes tested?
    yes
    
    ```
    type=LargeUtf8 offset=105: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
1) (expected Err)
    type=LargeUtf8 offset=106: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
2) (expected Err)
    type=LargeUtf8 offset=107: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
3) (expected Err)
    type=LargeUtf8 offset=108: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
4) (expected Err)
    type=LargeUtf8 offset=109: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
3) (expected Err)
    type=LargeUtf8 offset=110: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
2) (expected Err)
    type=LargeUtf8 offset=111: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
1) (expected Err)
    type=LargeUtf8 offset=112: Ok (expected Ok)
    type=LargeUtf8 offset=113: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
1) (expected Err)
    type=LargeUtf8 offset=114: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
2) (expected Err)
    type=LargeUtf8 offset=115: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
3) (expected Err)
    type=LargeUtf8 offset=116: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
4) (expected Err)
    type=LargeUtf8 offset=117: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
3) (expected Err)
    type=LargeUtf8 offset=118: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
2) (expected Err)
    type=LargeUtf8 offset=119: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
1) (expected Err)
    type=LargeUtf8 offset=120: Ok (expected Ok)
    type=LargeUtf8 offset=121: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
1) (expected Err)
    type=LargeUtf8 offset=122: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
2) (expected Err)
    type=LargeUtf8 offset=123: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
3) (expected Err)
    type=LargeUtf8 offset=124: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
4) (expected Err)
    type=LargeUtf8 offset=125: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
3) (expected Err)
    type=LargeUtf8 offset=126: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
2) (expected Err)
    type=LargeUtf8 offset=127: Err(Invalid argument error: Misaligned 
buffers[0] in array of type LargeUtf8, offset from expected alignment of 8 by 
1) (expected Err)
    ```
    
    
    <!--
    We typically require tests for all PRs in order to:
    1. Prevent the code from being accidentally broken by subsequent changes
    2. Serve as another way to document the expected behavior of the code
    
    If tests are not included in your PR, please explain why (for example,
    are they covered by existing tests)?
    
    If this PR claims a performance improvement, please include evidence
    such as benchmark results.
    -->
    
    # Are there any user-facing changes?
    no
    <!--
    If there are user-facing changes then we may require documentation to be
    updated before approving the PR.
    
    If there are any breaking changes to public APIs, please call them out.
    -->
---
 arrow-ipc/src/reader.rs | 95 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/arrow-ipc/src/reader.rs b/arrow-ipc/src/reader.rs
index 9fe1672542..8349cafc72 100644
--- a/arrow-ipc/src/reader.rs
+++ b/arrow-ipc/src/reader.rs
@@ -3116,6 +3116,101 @@ mod tests {
         );
     }
 
+    /// Verify that misaligned IPC buffers are caught by `require_alignment = 
true`.
+    ///
+    /// For each array type we shift the IPC body by every byte offset in 
0..OFFSET_RANGE and
+    /// assert that the decoder errors exactly when the offset violates the 
type's
+    /// minimum alignment requirement.  The Arrow columnar spec permits 
alignment to
+    /// any multiple of 8 or 64 bytes, so multiples of 8 (which include every 
multiple
+    /// of 64) must succeed; everything else must return a "Misaligned 
buffers" error.
+    /// See 
<https://arrow.apache.org/docs/format/Columnar.html#buffer-alignment-and-padding>.
+    #[test]
+    fn test_misaligned_buffers_error() {
+        const OFFSET_RANGE: usize = 128;
+
+        // (array, minimum required alignment in bytes)
+        // Fixed-width: alignment == element byte width.
+        // Variable-width (e.g. StringArray): the offsets buffer drives 
alignment (Int32 → 4).
+        let cases: Vec<(ArrayRef, usize)> = vec![
+            (Arc::new(Int32Array::from_iter(0i32..100)) as _, 4),
+            (Arc::new(Int64Array::from_iter(0i64..100)) as _, 8),
+            (
+                Arc::new(StringArray::from_iter_values(
+                    (0..100).map(|i| i.to_string()),
+                )) as _,
+                4,
+            ),
+            (
+                Arc::new(LargeStringArray::from_iter_values(
+                    (0..100).map(|i| i.to_string()),
+                )) as _,
+                8,
+            ),
+        ];
+
+        for (array, alignment) in cases {
+            let batch = RecordBatch::try_from_iter(vec![("col", 
Arc::clone(&array))]).unwrap();
+            let encoder = IpcDataGenerator {};
+            let mut dict_tracker = DictionaryTracker::new(false);
+            let (_, encoded) = encoder
+                .encode(
+                    &batch,
+                    &mut dict_tracker,
+                    &Default::default(),
+                    &mut Default::default(),
+                )
+                .unwrap();
+            let message = root_as_message(&encoded.ipc_message).unwrap();
+            let ipc_batch = message.header_as_record_batch().unwrap();
+
+            for offset in 0..OFFSET_RANGE {
+                // MutableBuffer always allocates at a 64-byte aligned base 
address.
+                // Slicing by `offset` bytes yields a pointer at `base_ptr + 
offset`,
+                // whose alignment is gcd(64, offset).  That makes `offset` 
the sole
+                // determinant of the resulting alignment — without this 
guarantee the
+                // test would be non-deterministic depending on what the 
allocator returns.
+                let mut storage = 
MutableBuffer::with_capacity(encoded.arrow_data.len() + offset);
+                for _ in 0..offset {
+                    storage.push(0_u8);
+                }
+                storage.extend_from_slice(&encoded.arrow_data);
+                let buf = Buffer::from(storage).slice(offset);
+
+                let result = RecordBatchDecoder::try_new(
+                    &buf,
+                    ipc_batch,
+                    batch.schema(),
+                    &Default::default(),
+                    &message.version(),
+                )
+                .unwrap()
+                .with_require_alignment(true)
+                .read_record_batch();
+
+                if offset % alignment == 0 {
+                    assert!(
+                        result.is_ok(),
+                        "type={} offset={offset}: expected Ok but got {:?}",
+                        array.data_type(),
+                        result.unwrap_err(),
+                    );
+                } else {
+                    let err = result
+                        .expect_err(&format!(
+                            "type={} offset={offset}: expected Err for 
misaligned buffer",
+                            array.data_type()
+                        ))
+                        .to_string();
+                    assert!(
+                        err.contains("Misaligned buffers"),
+                        "type={} offset={offset}: unexpected error: {err}",
+                        array.data_type(),
+                    );
+                }
+            }
+        }
+    }
+
     #[test]
     fn test_file_with_massive_column_count() {
         // 499_999 is upper limit for default settings (1_000_000)

Reply via email to