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 3b98da0a32 fix(arrow-ipc): return an error instead of panicking on IPC
file with no footer schema (#10744)
3b98da0a32 is described below
commit 3b98da0a32f373007387f0058a84c5de885c6b03
Author: Aditya Mishra <[email protected]>
AuthorDate: Wed Aug 19 06:05:40 2026 +0530
fix(arrow-ipc): return an error instead of panicking on IPC file with no
footer schema (#10744)
# Which issue does this PR close?
- Closes #10663
# Rationale for this change
`FileReaderBuilder::build` read the footer's schema with
[`footer.schema().unwrap()`](https://github.com/bit2swaz/arrow-rs/blob/93180c87b97fe0b19cd4b84fdef6839230a01400/arrow-ipc/src/reader.rs#L1253).
the schema table is optional in the footer flatbuffer, so an IPC file
whose footer verifies but has no schema table makes that `unwrap()`
panic instead of returning an error. the flatbuffer verifier accepts
such a file, so this is reachable from untrusted input
this is the same class of untrusted-input panic as #10437 and #10575.
those two are in `convert.rs` and #10647 made them fallible, but this
sink is one level up in `reader.rs` where the footer's schema table is
absent, so #10647 leaves it alone
# What changes are included in this PR?
swap the `unwrap()` for `ok_or_else(...)?` returning an
`ArrowError::ParseError`, matching the `recordBatches` check right above
it
# Are these changes tested?
yes. added `test_missing_footer_schema_error` which builds a footer that
verifies but has no schema table and checks `FileReader::try_new`
returns `Err` instead of panicking
# Are there any user-facing changes?
reading a malformed IPC file with no footer schema now returns an
`ArrowError` instead of panicking. no API changes
---------
Co-authored-by: Andrew Lamb <[email protected]>
---
arrow-ipc/src/reader.rs | 41 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)
diff --git a/arrow-ipc/src/reader.rs b/arrow-ipc/src/reader.rs
index 5e8a46cfab..c42cc5d31c 100644
--- a/arrow-ipc/src/reader.rs
+++ b/arrow-ipc/src/reader.rs
@@ -1250,7 +1250,9 @@ impl FileReaderBuilder {
let total_blocks = blocks.len();
- let ipc_schema = footer.schema().unwrap();
+ let ipc_schema = footer.schema().ok_or_else(|| {
+ ArrowError::ParseError("Unable to get schema from IPC
Footer".to_string())
+ })?;
if !ipc_schema.endianness().equals_to_target_endianness() {
return Err(ArrowError::IpcError(
"the endianness of the source system does not match the
endianness of the target system.".to_owned()
@@ -2194,6 +2196,43 @@ mod tests {
}
}
+ #[test]
+ fn test_missing_footer_schema_error() {
+ use crate::r#gen::File::{Footer, FooterArgs};
+ use flatbuffers::FlatBufferBuilder;
+
+ // a footer that verifies but has no schema table. record batches
present
+ // (so the earlier ok_or_else passes) but schema absent, which used to
panic.
+ let mut fbb = FlatBufferBuilder::new();
+ let record_batches = fbb.create_vector::<Block>(&[]);
+ let footer = Footer::create(
+ &mut fbb,
+ &FooterArgs {
+ version: MetadataVersion::V5,
+ schema: None,
+ dictionaries: None,
+ recordBatches: Some(record_batches),
+ custom_metadata: None,
+ },
+ );
+ fbb.finish(footer, None);
+ let footer_data = fbb.finished_data();
+
+ // assemble a minimal IPC file: magic header, footer, footer length,
magic trailer
+ let mut buf = Vec::new();
+ buf.extend_from_slice(&crate::ARROW_MAGIC);
+ buf.extend_from_slice(footer_data);
+ buf.extend_from_slice(&(footer_data.len() as i32).to_le_bytes());
+ buf.extend_from_slice(&crate::ARROW_MAGIC);
+
+ let err = FileReader::try_new(Cursor::new(buf), None)
+ .expect_err("expected an error, not a panic");
+ assert!(
+ matches!(err, ArrowError::ParseError(_)),
+ "expected ParseError, got {err:?}"
+ );
+ }
+
/// Test that the reader can read legacy files where empty list arrays
were written with a 0-byte offsets buffer.
#[test]
fn test_read_legacy_empty_list_without_offsets_buffer() {