fix(puffin): return DataInvalid instead of panicking on malformed footer length (#2867)

## What changes are included in this PR?

`FileMetadata::read` locates the footer by subtracting from the file
size, but never checks the file is big enough first. Two subtractions
can underflow:

- `read_footer_payload_length` does `input_file_length -
FOOTER_STRUCT_LENGTH`, so any file shorter than 12 bytes underflows.
- `read_footer_bytes` does `input_file_length - footer_length`, where
`footer_length` is derived from `footer_payload_length`, the u32 read
out of the FooterPayloadSize field of the file being parsed. A file that
declares a payload larger than itself underflows here.

In debug builds both panic with "attempt to subtract with overflow". In
release the subtraction wraps and you get a bogus read range instead.

Worth saying up front: I'm treating this as robustness, not a security
issue. Puffin files are table statistics written by the engines that
already write the table, so I'm not claiming a meaningful trust
boundary. The point is narrower - a truncated or corrupt file should
surface as a `DataInvalid` error, the way the magic checks and the
bounds checks in `decode_flags` / `extract_footer_payload_as_str`
already do in this same file, rather than panicking on the caller.

The fix adds a `MIN_FILE_LENGTH` check in `read` and switches the second
subtraction to `checked_sub`, both returning `ErrorKind::DataInvalid`.

I left `read_with_prefetch` alone. Its `prefetch_hint > 16` and
`prefetch_hint <= input_file_length` guards already keep its own slicing
in range, and when the declared footer is larger than the hint it falls
back to `read`, which is now checked.

## Are these changes tested?

Two unit tests, one per case. Both panic before the fix:

```
thread 'puffin::metadata::tests::test_file_shorter_than_minimum_length_returns_error'
panicked at crates/iceberg/src/puffin/metadata.rs:201:21:
attempt to subtract with overflow

thread 'puffin::metadata::tests::test_footer_payload_length_larger_than_file_returns_error'
panicked at crates/iceberg/src/puffin/metadata.rs:218:21:
attempt to subtract with overflow
```

After the fix, `cargo test -p iceberg --lib puffin` gives 39 passed, 0
failed. Full crate `cargo test -p iceberg --lib` is 1444 passed, 0
failed, and `cargo clippy -p iceberg --all-targets` is clean.

Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
1 file changed
tree: 2da34e473e623a8739128847f659cf19cb2d8868
  1. .cargo/
  2. .devcontainer/
  3. .github/
  4. .idea/
  5. bindings/
  6. crates/
  7. dev/
  8. docs/
  9. website/
  10. .asf.yaml
  11. .gitattributes
  12. .gitignore
  13. .licenserc.yaml
  14. .taplo.toml
  15. .typos.toml
  16. AGENTS.md
  17. Cargo.lock
  18. Cargo.toml
  19. CHANGELOG.md
  20. CONTRIBUTING.md
  21. deny.toml
  22. LICENSE
  23. Makefile
  24. NOTICE
  25. README.md
  26. rust-toolchain.toml
  27. rustfmt.toml
  28. SECURITY-THREAT-MODEL.md
README.md

Apache Iceberg™ Rust

Rust implementation of Apache Iceberg™.

Components

The Apache Iceberg Rust project is composed of the following components:

NameReleaseDocs
icebergiceberg imagedocs release docs dev
iceberg-catalog-loadericeberg-catalog-loader imagedocs release docs dev
iceberg-catalog-glueiceberg-catalog-glue imagedocs release docs dev
iceberg-catalog-hmsiceberg-catalog-hms imagedocs release docs dev
iceberg-catalog-resticeberg-catalog-rest imagedocs release docs dev
iceberg-catalog-s3tablesiceberg-catalog-s3tables imagedocs release docs dev
iceberg-catalog-sqliceberg-catalog-sql imagedocs release docs dev
iceberg-cache-mokaiceberg-cache-moka imagedocs release docs dev
iceberg-datafusioniceberg-datafusion imagedocs release docs dev
iceberg-storage-opendaliceberg-storage-opendal imagedocs release docs dev

Iceberg Rust Implementation Status

The features that Iceberg Rust currently supports can be found here.

Supported Rust Version

Iceberg Rust is built and tested with stable rust, and will keep a rolling MSRV (minimum supported rust version). At least three months from latest rust release is supported. MSRV is updated when we release iceberg-rust.

Check the current MSRV on crates.io.

Contribute

Apache Iceberg is an active open-source project, governed under the Apache Software Foundation (ASF). Apache Iceberg Rust is always open to people who want to use or contribute to it. Here are some ways to get involved.

The Apache Iceberg community is built on the principles described in the Apache Way and all who engage with the community are expected to be respectful, open, come with the best interests of the community in mind, and abide by the Apache Foundation Code of Conduct.

Users

  • Databend: An open-source cloud data warehouse that serves as a cost-effective alternative to Snowflake.
  • Lakekeeper: An Apache-licensed Iceberg REST Catalog with data access controls.
  • Moonlink: A Rust library that enables sub-second mirroring (CDC) of Postgres tables into Iceberg.
  • RisingWave: A Postgres-compatible SQL database designed for real-time event streaming data processing, analysis, and management.
  • Wrappers: Postgres Foreign Data Wrapper development framework in Rust.
  • ETL: Stream your Postgres data anywhere in real-time.
  • Apache DataFusion Comet: High-performance accelerator for Apache Spark, built on top of the powerful Apache DataFusion query engine.
  • Spice.ai: An open-source compute and AI runtime for data-grounded agents.

License

Licensed under the Apache License, Version 2.0