When decompression fails inside a legacy (zstd v0.4 to v0.7) frame, the
bundled zstd library reports the output buffer as fully written although
nothing was written. `DCtx::decompress_stream` committed that position even
on error, and for a `Vec<u8>` destination this extended the vector's length
over uninitialized memory.
Legacy support is enabled by default. A 5- or 6-byte corrupt legacy frame is
enough: through `zstd::stream::write::Decoder`, up to 32 KiB of stale heap
memory is then passed to the wrapped writer on the next `write()` or
`flush()`, or when an `auto_flush()`/`on_flush()` decoder is dropped. Whether
this reaches an attacker depends on where the application sends the
decompressed output. `zstd::stream::read::Decoder`, `zstd::decode_all` and
the bulk API are not affected.
Before zstd-safe 4.0.0, output buffers could only be initialized slices.
The zstd crate's own writer had the same flaw from 0.4.22 to 0.7.x, which is
covered by a separate advisory.
The flaw was corrected by only committing the buffer positions of a
successful call.