Skip to content

Decompress: guess a multiple of the input for unknown-size frames - #170

Merged
KrisKennawayDD merged 1 commit into
1.xfrom
kris.kennaway/decompress-hint-multiple-of-src
Jul 27, 2026
Merged

Decompress: guess a multiple of the input for unknown-size frames#170
KrisKennawayDD merged 1 commit into
1.xfrom
kris.kennaway/decompress-hint-multiple-of-src

Conversation

@KrisKennawayDD

@KrisKennawayDD KrisKennawayDD commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Supersedes #169 (per review: keep a single decode path instead of adding a retry branch).

Problem

decompressSizeHint returned a flat lower bound of decompressSizeBufferLimit (1 MB) whenever the frame did not carry its decompressed size — legacy zstd v0.5 frames and streaming frames compressed without a pledged size. So every Decompress of such a frame with no adequately-sized caller buffer (e.g. Decompress(nil, src)) allocated at least 1 MB regardless of the real payload size.

Background

The flat-bound sizing dates to two 2022 commits:

  • 30c4b29 "Add a sanity limit to decompress buffer size allocation" added decompressSizeHint/decompressSizeBufferLimit to fix a DoS (Decoder doesn't check output size before allocating #60): the code was trusting ZSTD_getFrameContentSize, so a crafted frame could force a huge allocation. The cap (≤ 50× input, ≥ 1 MB) + streaming fallback fixed that.
  • 489b911 "Remove Decompress multiple retries" then dropped the earlier grow-and-retry sizing, on the premise that "zstd 1.3.0+ frames always carry a size hint," so you either decode right away from the hint or fall back to streaming.

That premise does not hold for legacy v0.5 frames or unpledged streaming frames — which is exactly this case. For them the hint is absent, so the code fell back to the flat 1 MB bound on every decode.

Fix

Now that decompressSizeHint reports whether the size was found (added in #168), return 3× the input length for the unknown case instead of the flat bound. Decompress already falls back to streaming if that is too small, so this needs no new branch on the decode path — the logic stays in decompressSizeHint.

  • It only ever sizes from the input length (never an attacker-claimed size), and is always within the existing 50× cap, so the DoS guard from 30c4b29 is unchanged.
  • BulkProcessor.Decompress has no streaming fallback (the streaming reader ignores the dictionary), so it keeps allocating the upper bound for unknown-size frames rather than this smaller guess, which it could not recover from.

🤖 Generated with Claude Code

@KrisKennawayDD
KrisKennawayDD force-pushed the kris.kennaway/decompress-hint-multiple-of-src branch 2 times, most recently from fedaad9 to 8d3c939 Compare July 27, 2026 17:35
decompressSizeHint returned a flat lower bound of decompressSizeBufferLimit
(1 MB) whenever the frame did not carry its decompressed size -- legacy zstd
v0.5 frames and streaming frames compressed without a pledged size. Every such
Decompress with no adequately sized caller buffer allocated at least 1 MB
regardless of the real payload size.

Now that decompressSizeHint reports whether the size was found, return 3x the
input length for the unknown case instead of the flat bound, letting Decompress
fall back to streaming if that is too small. This keeps a single decode path
(no extra retry loop) and only ever sizes from the input length, so it does not
weaken the zip-bomb guard; 3x is always within the existing 50x cap.

BulkProcessor.Decompress has no streaming fallback (the streaming reader ignores
the dictionary), so it keeps allocating the upper bound for unknown-size frames
rather than the smaller guess, which it could not recover from.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@KrisKennawayDD
KrisKennawayDD force-pushed the kris.kennaway/decompress-hint-multiple-of-src branch from 8d3c939 to 8912568 Compare July 27, 2026 17:36
@KrisKennawayDD
KrisKennawayDD marked this pull request as ready for review July 27, 2026 17:39
@KrisKennawayDD
KrisKennawayDD requested a review from Viq111 July 27, 2026 17:39

@Viq111 Viq111 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

@KrisKennawayDD
KrisKennawayDD merged commit b46aeb3 into 1.x Jul 27, 2026
11 checks passed
@KrisKennawayDD
KrisKennawayDD deleted the kris.kennaway/decompress-hint-multiple-of-src branch July 27, 2026 17:54
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.

2 participants