decompress_chunk() already threaded chunk_size (the pipeline's declared
decompressed size) into the scale-offset/nbit/szip decoders to bound their
output, but not into deflate/lz4/zstd/pcodec, all four of which allocated
based on attacker-controlled input with no cap:
- lz4: read a raw u32 "orig_size" straight from the compressed payload's
first 4 bytes and passed it directly to lz4_flex::block::decompress with
no upper bound — a 4-byte attacker-controlled field could request ~4 GiB.
- deflate (non-macOS path): unbounded flate2 read_to_end into a fresh Vec.
- zstd: zstd::decode_all with no output cap (classic decompression-bomb
vector, ratios can exceed 1000:1).
- pcodec: simple_decompress with no cap.
All four now take the expected chunk size and reject output that exceeds it
(or a 256 MiB absolute ceiling when the size is unavailable), matching the
pattern the other three filters already used. Also fixes the same unbounded
read_to_end in clawhdf5-filters' fast_deflate streaming fallback (used when
no size hint is available).
Added tests for each codec plus one exercising the actually-exploited path
through the public decompress_chunk() entrypoint.
Co-Authored-By: Claude Sonnet 5 <[email protected]>