Skip to content

legacy/v0.7: decode RLE blocks in the streaming path - #4819

Open
ManoharPaturi wants to merge 1 commit into
meta:devfrom
ManoharPaturi:fix-legacy-v07-rle-streaming
Open

ManoharPaturi wants to merge 1 commit into
meta:devfrom
ManoharPaturi:fix-legacy-v07-rle-streaming

Conversation

@ManoharPaturi

Copy link
Copy Markdown

The v0.7 decoder expands RLE blocks in its one-shot path, through ZSTDv07_generateNxBytes() fed with the regenerated size that ZSTDv07_getcBlockSize() extracts into blockProperties. The block stage of ZSTDv07_decompressContinue() instead rejects them with a generic error.

A frame containing an RLE block therefore decodes completely through ZSTD_decompress() and fails partway through ZSTD_decompressStream(). Demonstrated with a crafted frame holding a single RLE block of byte 0x41 expanding to 100000 bytes, on unfixed dev:

one-shot : 100000 bytes
streaming: 4096 bytes, then Error (generic)

The change carries the regenerated size from the header stage to the block stage in a new dctx field, and expands RLE blocks there with the same helper the one-shot path uses. After the change the streaming decoder returns the same 100000 bytes as the one-shot decoder, and the frame is added to tests/legacy.c behind the ZSTD_LEGACY_SUPPORT level 7 gate; on unfixed dev the new test fails with Error (generic) and with the change it passes, along with the existing legacy tests at every level from 1 to 7.

For background: the v0.7.0 reference decoder shipped with the same inconsistency between its two paths, which is how it reached the legacy copy, and no encoder from the v0.7 line emits RLE blocks. The format's one-shot path defines what is accepted, so third party encoders producing RLE blocks are currently readable in one call but not through the streaming interface.

The v0.7 decoder supports RLE blocks in its one-shot path, expanding them
with ZSTDv07_generateNxBytes(), but the block stage of
ZSTDv07_decompressContinue() rejects them with a generic error. A frame
containing an RLE block therefore decodes through ZSTD_decompress() and
fails through ZSTD_decompressStream(), partway through the frame.

Transfer the block's regenerated size from the header stage, where
ZSTDv07_getcBlockSize() already extracts it into blockProperties, to the
block stage, and expand RLE blocks there with the same helper the
one-shot path uses.

The v0.7.0 reference decoder shipped with the same inconsistency, so no
frame produced by the v0.7 line itself contains an RLE block; the
asymmetry is nonetheless reachable by any third party encoder for the
format, and the one-shot path defines what the format accepts.

Add a crafted frame to the legacy test, one RLE block expanding to
100000 bytes: before this change the streaming leg fails with
Error (generic), after it the streaming output matches the one-shot
output.
Copilot AI balanced review requested due to automatic review settings October 4, 2026 04:35
@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

Copy link
Copy Markdown
Author

@Cyan4973 these legacy decoder fixes (#4819 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