Skip to main content

Module inflate

Module inflate 

Source
Expand description

Reading compressed data from an untrusted file without letting it expand without bound.

A few kilobytes of DEFLATE can stand for gigabytes of zeros — a “zip bomb”. Read to the end, one entry of it allocates until the process is killed: not an error a caller can handle, an abort. InflateGuard wraps the decompressing reader of one entry or stream and fails it with an io::Error once the data expands past InflateLimits:

  • past a grace size of output, the output may be at most a fixed multiple of the compressed size — what real documents compress to, with room to spare;
  • nothing expands past a maximum size.

The limits differ by format, so each comes from that format’s reference reader. InflateLimits::POI — Apache POI’s ZipSecureFile, the reference reader for OOXML — is the default: past 100 KiB, at most 100 times the compressed size, and at most 4 GiB. Text and markup compress 3–20 times, pictures barely, and nothing a document’s parts hold compresses a hundredfold. PDF streams do (a blank 1-bit image mask), and a PDF reader gives them its own limits.

POI measures the compressed bytes consumed so far; this measures the entry’s whole compressed size, which a reader knows from the ZIP directory or the stream’s length. The ratio is therefore never smaller here than POI’s, so nothing POI accepts is refused.

A decoder that is not a Read (one that builds its output in a loop) applies the same rule with InflateLimits::check. is_inflation_limit tells the error apart from an I/O error, so a caller can classify it (an archive error, not a failing disk).

use std::io::Read;
use unparser_shared::inflate::{is_inflation_limit, InflateGuard};

// 1 MiB of output claimed by a 1 KiB entry: past 100 KiB it is a bomb.
let bomb = std::io::repeat(0).take(1 << 20);
let err = InflateGuard::new(bomb, 1024).read_to_end(&mut Vec::new()).unwrap_err();
assert!(is_inflation_limit(&err));

// The same output from 64 KiB of input is an ordinary 16-fold compression.
let text = std::io::repeat(b'a').take(1 << 20);
assert_eq!(InflateGuard::new(text, 64 * 1024).read_to_end(&mut Vec::new()).unwrap(), 1 << 20);

Structs§

InflateGuard
A reader of decompressed data that fails once the data expands past its InflateLimits.
InflateLimits
How far compressed data may expand before it is taken for a decompression bomb.
InflationLimit
The error InflateGuard fails with.

Functions§

is_inflation_limit
Whether error is InflateGuard’s — the data expanded past the limits — rather than a failure of the reader underneath.