Skip to content

legacy/v0.4: size the streaming output buffer for one extra block - #4815

Open
ManoharPaturi wants to merge 1 commit into
meta:devfrom
ManoharPaturi:fix-legacy-v04-stream-wrap
Open

ManoharPaturi wants to merge 1 commit into
meta:devfrom
ManoharPaturi:fix-legacy-v04-stream-wrap

Conversation

@ManoharPaturi

Copy link
Copy Markdown

The v0.4 streaming decoder (ZBUFF) sizes its output buffer at exactly the frame window, so it must wrap the buffer back to offset zero whenever a flushed block leaves less than a full block of room. The wrap discards history that later sequences can still reference, because a block is allowed to be smaller than the window while following blocks use offsets that reach back into it. Match sources then read bytes the current block has already overwritten, and the streaming API silently returns different output than the one shot API for the same frame.

The v0.5 decoder had the same flaw (issue #4553). The v0.6 and v0.7 decoders already solve it by sizing the buffer at windowSize plus one block, which pushes the wrap point past a full window and makes it harmless. This change applies the same sizing to v0.4.

The defect only shows on frames whose block layout leaves the buffer partly filled at the wrap decision, which the v0.4 encoder never produces (it always fills blocks up to the window), so the frame in the test is hand crafted:

windowLog 11 (window of 2048 bytes)
block 1 : raw, 600 bytes
block 2 : compressed, one sequence with offset 1000 referencing block 1

Behavior on unfixed dev, via the public API:

one-shot  : 1148 bytes
streaming : 1148 bytes, content differs starting at offset 1100

With this change the two decoders agree, and the new test in tests/legacy.c locks that in at any ZSTD_LEGACY_SUPPORT level that can decode v0.4. The test fails on unfixed dev with "streaming decoder output differs from one-shot decoder" and passes after the change; the existing legacy tests pass at every level from 1 to 7.

The v0.4 streaming decoder sizes its output buffer at exactly the frame
window. When a flushed block leaves less than a full block of room, it
wraps the buffer back to offset zero, discarding the history that later
sequences still reference through offsets within the window. Match
sources then read bytes the current block has already overwritten, so
the streaming API silently returns different output than the one-shot
API for the same frame.

The v0.5 decoder had the same flaw (issue meta#4553), and the v0.6 and v0.7
decoders already solve it by sizing the buffer at windowSize plus one
block, which makes the wrap harmless. Apply the same sizing here.

Add a crafted frame to the legacy test whose first block is smaller
than the declared window and whose next block references it: before
this change the streaming decode differs from the one-shot decode,
after it they match.
Copilot AI balanced review requested due to automatic review settings October 4, 2026 02:48
@meta-cla meta-cla Bot added the CLA Signed label Oct 4, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ManoharPaturi
ManoharPaturi force-pushed the fix-legacy-v04-stream-wrap branch from 51edc6f to 4023f9d Compare October 4, 2026 04:37
@ManoharPaturi

Copy link
Copy Markdown
Author

@Cyan4973 these legacy decoder fixes (#4815 among the v0.2 through v0.7 batch) have been green on all checks for a few weeks now with no reviewer assigned yet. Each one is a narrow correctness fix in one legacy format with a round-trip regression test in tests/legacy.c. Could you or another maintainer take a look when the queue allows?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants