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)