Skip to content

Validate stored length before decompressing - #585

Open
iliaal wants to merge 3 commits into
php-memcached-dev:masterfrom
iliaal:decompress-bounds
Open

iliaal wants to merge 3 commits into
php-memcached-dev:masterfrom
iliaal:decompress-bounds

Conversation

@iliaal

@iliaal iliaal commented Oct 15, 2026

Copy link
Copy Markdown
Member

Compressed values carry their uncompressed length in a 4-byte header, and s_decompress_value() allocated a buffer of that size before decompressing. The header was trusted as stored, so an inconsistent item could cause a very large allocation, and a fatal memory-limit error, before decompression started. Separately, the FastLZ and zstd paths kept the stored length as the value length even when the library produced fewer bytes, so a value could come back with extra bytes past the decompressed data.

  • Reject a stored length the compressed data can't produce, before allocating. The bound is each algorithm's maximum expansion ratio: zlib 1032:1 (zlib.net/zlib_tech.html), FastLZ 255:1 (from the bundled decoder), and zstd per RFC 8878, plus an exact match against the zstd frame's content size. A rejected item reads back as false with a warning, like any other decompression failure.
  • FastLZ and zstd now use the decompressed size as the value length, as zlib already did.

Maximally compressible values up to 32 MB round-trip with all three algorithms. New tests cover items whose length header doesn't match their data.

A compressed item starts with its uncompressed length, and the read path
allocated that many bytes before decompressing. Reject a length larger
than the compressed data can expand to (1032:1 for zlib, 255:1 for
FastLZ, 32768:1 for zstd), and for zstd require it to match the frame's
content size. A rejected length fails like any other decompression error:
a warning and a false result.
FastLZ and zstd results kept the stored length as the string length, so a
stored length larger than the decompressed output left uninitialized
bytes at the end of the value. Set the length to what the decompressor
returned, as the zlib path already does, and terminate the string there.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant