Reject a too large block in ZSTD_compressSequencesAndLiterals() - #4741
Reject a too large block in ZSTD_compressSequencesAndLiterals()#4741nileshpatil6 wants to merge 2 commits into
Conversation
ZSTD_compressSequences() validates every block through
determine_blockSize(), which in explicit-delimiter mode rejects a block
whose regenerated size exceeds cctx->blockSizeMax:
if (explicitBlockSize > blockSize)
RETURN_ERROR(externalSequences_invalid,
"sequences incorrectly define a too large block");
ZSTD_compressSequencesAndLiterals_internal() only supports explicit
delimiters, but does not perform that check. It calls
ZSTD_get1BlockSummary() and validates block.litSize against litSize
only, so sequences describing an over-sized block are accepted and the
function returns success while emitting a block that exceeds the
maximum block size the format allows.
The two APIs disagree on identical input:
ZSTD_compressSequences -> External sequences are not valid
ZSTD_compressSequencesAndLiterals -> SUCCESS (cSize = 36)
For a large enough block the resulting frame is not decodable, so the
caller receives a success return for bytes that cannot be read back.
The guard was added for the sibling path in 87dcd33 (2022). The
AndLiterals pipeline was split out later, in 14a21e4 (2024), without
carrying it over.
|
$ make -C tests clean
$ make -j$(nproc) -C tests fuzzer MOREFLAGS=-Werror
fuzzer.c:4382:16: error: declaration of ‘cSize’ shadows a previous local [-Werror=shadow]Renaming this local test variable, or reusing the outer one, should keep the PR passing the I verified the functional fix as well: on the parent commit with only the new test transplanted, |
basicUnitTests() already declares size_t cSize, so the inner declaration tripped -Wshadow and failed builds using MOREFLAGS=-Werror.
|
Good catch, thank you, and thanks for independently reproducing the functional part. Fixed in bf7504b: the inner local is now After the rename that build is clean, and the test still discriminates: |
|
Re-checked make -C tests clean
make -j$(nproc) -C tests fuzzer MOREFLAGS=-Werror
./tests/fuzzer -v -i0
|
Summary
ZSTD_compressSequencesAndLiterals()accepts sequences that describe a block larger thancctx->blockSizeMaxand returns success, while its siblingZSTD_compressSequences()rejects the same input.Detail
ZSTD_compressSequences_internal()routes every block throughdetermine_blockSize(), which in explicit-delimiter mode enforces:ZSTD_compressSequencesAndLiterals_internal()supports only explicit delimiters, so it is exactly the mode that guard was written for, but its block loop validates just the literals:so
block.blockSizeis never checked againstcctx->blockSizeMax.Reproduction
One sequence plus a block delimiter, describing a single block of
ZSTD_BLOCKSIZE_MAX + 1bytes, given to both APIs:The caller gets a success return for a frame containing a block that exceeds the maximum block size the format permits. How bad the result is depends on how far over the limit the block goes. At
ZSTD_BLOCKSIZE_MAX + 1the frame still happens to round-trip throughZSTD_decompress(); at a larger size it does not:So for a sufficiently over-sized block the API reports success for output that cannot be read back.
ZSTD_compressSequencesAndLiterals()is aimed at callers that hold literals and sequences separately and compute block boundaries themselves, which is precisely where a mis-sized block is plausible.How it happened
The guard was introduced for the sibling path in 87dcd33, "fix sequence compression API in Explicit Delimiter mode" (2022-01-22), and later moved into
determine_blockSize(). The AndLiterals pipeline was split out afterwards in 14a21e4, "produced ZSTD_compressSequencesAndLiterals() as a separate pipeline" (2024-12-11), without carrying the check across.Change
One
RETURN_ERROR_IF, using the same error code and message as the sibling, so both APIs now reject identical input identically.I deliberately kept this to the single check I can demonstrate.
determine_blockSize()also guardsexplicitBlockSize > remaining; I tested that case here and it is already rejected on this path, so I have not added a second line for it.Testing
New unit test in
tests/fuzzer.cnext to the existingZSTD_compressSequencesAndLiteralstests.Without the fix:
tests/fuzzer -v -i0exits 1.With the fix:
tests/fuzzer -v -i0exits 0.No regressions:
tests/fuzzer -i3000completes 3000 rounds andtests/zstreamtest -i2000completes 2001 rounds, both exit 0.