From 7d777bbfe2613640ba9517d8af2fbbd574c2fa24 Mon Sep 17 00:00:00 2001 From: nileshpatil6 Date: Fri, 21 Aug 2026 12:13:21 +0530 Subject: [PATCH 1/2] Reject a too large block in ZSTD_compressSequencesAndLiterals() 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. --- lib/compress/zstd_compress.c | 1 + tests/fuzzer.c | 43 ++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index c06f2e1bb98..52c00d275aa 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -8033,6 +8033,7 @@ ZSTD_compressSequencesAndLiterals_internal(ZSTD_CCtx* cctx, FORWARD_IF_ERROR(block.nbSequences, "Error while trying to determine nb of sequences for a block"); assert(block.nbSequences <= nbSequences); RETURN_ERROR_IF(block.litSize > litSize, externalSequences_invalid, "discrepancy: Sequences require more literals than present in buffer"); + RETURN_ERROR_IF(block.blockSize > cctx->blockSizeMax, externalSequences_invalid, "sequences incorrectly define a too large block"); ZSTD_resetSeqStore(&cctx->seqStore); conversionStatus = ZSTD_convertBlockSequences(cctx, diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 7b7c9d66631..3ff1d0fa1c9 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -4365,6 +4365,49 @@ static int basicUnitTests(U32 const seed, double compressibility) } DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3i : ZSTD_compressSequencesAndLiterals : reject a too large block : ", testNb++); + { + /* A single sequence, followed by an explicit block delimiter, describing + * one block whose regenerated size exceeds ZSTD_BLOCKSIZE_MAX. + * ZSTD_compressSequences() already rejects this in determine_blockSize(), + * so the AndLiterals() variant must reject it too. Otherwise it reports + * success while emitting a block that violates the format. */ + const size_t litLen = 16; + const size_t blockSize = ZSTD_BLOCKSIZE_MAX + 1; + BYTE* const dst = (BYTE*)compressedBuffer; + size_t const dstCapacity = ZSTD_compressBound(blockSize); + BYTE litBuffer[32]; + ZSTD_Sequence seqs[2]; + ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + size_t cSize; + size_t i; + + assert(cctx != NULL); + for (i = 0; i < sizeof(litBuffer); i++) litBuffer[i] = (BYTE)('a' + (i % 3)); + + seqs[0].offset = 1; + seqs[0].litLength = (U32)litLen; + seqs[0].matchLength = (U32)(blockSize - litLen); + seqs[0].rep = 0; + seqs[1].offset = 0; /* block delimiter */ + seqs[1].litLength = 0; + seqs[1].matchLength = 0; + seqs[1].rep = 0; + + CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_blockDelimiters, ZSTD_sf_explicitBlockDelimiters)); + cSize = ZSTD_compressSequencesAndLiterals(cctx, dst, dstCapacity, + seqs, 2, + litBuffer, litLen, sizeof(litBuffer), + blockSize); + if (!ZSTD_isError(cSize)) { + DISPLAY("ZSTD_compressSequencesAndLiterals() should have failed: sequences define a block larger than ZSTD_BLOCKSIZE_MAX\n"); + ZSTD_freeCCtx(cctx); + goto _output_error; + } + ZSTD_freeCCtx(cctx); + } + DISPLAYLEVEL(3, "OK \n"); + /* Multiple blocks of zeros test */ #define LONGZEROSLENGTH 1000000 /* 1MB of zeros */ DISPLAYLEVEL(3, "test%3i : compress %u zeroes : ", testNb++, LONGZEROSLENGTH); From bf7504bf1a65e48faa14f0293b1a961a81534b8e Mon Sep 17 00:00:00 2001 From: nileshpatil6 Date: Sat, 22 Aug 2026 21:55:33 +0530 Subject: [PATCH 2/2] tests: rename cSize in the new test to avoid shadowing basicUnitTests() already declares size_t cSize, so the inner declaration tripped -Wshadow and failed builds using MOREFLAGS=-Werror. --- tests/fuzzer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 3ff1d0fa1c9..2fd92a54ec7 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -4379,7 +4379,7 @@ static int basicUnitTests(U32 const seed, double compressibility) BYTE litBuffer[32]; ZSTD_Sequence seqs[2]; ZSTD_CCtx* const cctx = ZSTD_createCCtx(); - size_t cSize; + size_t cSizeTooLarge; size_t i; assert(cctx != NULL); @@ -4395,11 +4395,11 @@ static int basicUnitTests(U32 const seed, double compressibility) seqs[1].rep = 0; CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_blockDelimiters, ZSTD_sf_explicitBlockDelimiters)); - cSize = ZSTD_compressSequencesAndLiterals(cctx, dst, dstCapacity, + cSizeTooLarge = ZSTD_compressSequencesAndLiterals(cctx, dst, dstCapacity, seqs, 2, litBuffer, litLen, sizeof(litBuffer), blockSize); - if (!ZSTD_isError(cSize)) { + if (!ZSTD_isError(cSizeTooLarge)) { DISPLAY("ZSTD_compressSequencesAndLiterals() should have failed: sequences define a block larger than ZSTD_BLOCKSIZE_MAX\n"); ZSTD_freeCCtx(cctx); goto _output_error;