Skip to content

fix(protocol): cap decompressed payload size to prevent gzip bombs (#942) - #943

Merged
smallnest merged 1 commit into
masterfrom
fix/decompression-bomb-942
Jul 7, 2026
Merged

smallnest merged 1 commit into
masterfrom
fix/decompression-bomb-942

Conversation

@smallnest

Copy link
Copy Markdown
Owner

Summary

Fixes #942 โ€” an unauthenticated gzip decompression bomb in the rpcx wire protocol.

Message.Decode decompressed gzip (and snappy) payloads with io.ReadAll and no cap on output size, before service lookup or auth. MaxMessageLength only bounds the compressed wire length, so a small frame (<2MB) could expand to gigabytes and OOM the server fully pre-authentication.

Changes

  • util/compress.go: add UnzipLimited(data, maxSize) using io.LimitReader(gr, maxSize+1) to detect overflow without allocating the oversized payload; returns new ErrDecompressedSizeTooLarge. Unzip now honors package-level MaxDecompressedSize.
  • protocol/compressor.go: add optional LimitedUnzipper interface, implemented on GzipCompressor and SnappyCompressor (snappy had the same unbounded io.ReadAll).
  • protocol/message.go: add MaxDecompressedLength; Decode caps decompression via LimitedUnzipper when set โ€” bounded before service lookup and auth.
  • server/option.go: add WithMaxDecompressedLength(int64) and WithMaxMessageLength(int) options.

Backward compatibility

Caps default to 0 (unlimited), so behavior is unchanged unless configured. Servers accepting untrusted traffic should set one, e.g.:

server.NewServer(server.WithMaxDecompressedLength(64 << 20))

Tests

  • util.TestUnzipLimited โ€” payload over/at/under the cap.
  • protocol.TestDecodeDecompressBomb โ€” a small compressed frame expanding to 1 MiB is rejected when the cap is set, decodes fine when unlimited.

All of ./util, ./protocol, ./server build, vet, and test green.

Message.Decode decompressed gzip/snappy payloads with io.ReadAll and no
output cap, before service lookup or auth. MaxMessageLength only bounds
the compressed wire length, so a small frame could expand to gigabytes
and OOM the server pre-authentication (issue #942).

- util: add UnzipLimited using io.LimitReader to bound output; Unzip
  honors package-level MaxDecompressedSize (default 0 = unlimited).
- protocol: add optional LimitedUnzipper interface, implement on Gzip
  and Snappy compressors; Decode caps output via MaxDecompressedLength.
- server: add WithMaxDecompressedLength / WithMaxMessageLength options.

Defaults stay unlimited for backward compatibility.

Fixes #942
@smallnest
smallnest merged commit 047aec1 into master Jul 7, 2026
1 check passed
@smallnest
smallnest deleted the fix/decompression-bomb-942 branch July 7, 2026 08:22
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.

Unauthenticated gzip decompression bomb in rpcx wire protocol causes multi-GB memory allocation from a single small request

1 participant