]> git.ipfire.org Git - thirdparty/zstd.git/commitdiff
fixed bug 44168
authorYann Collet <cyan@fb.com>
Sun, 30 Jan 2022 00:23:21 +0000 (16:23 -0800)
committerYann Collet <cyan@fb.com>
Sun, 30 Jan 2022 00:36:20 +0000 (16:36 -0800)
discovered by oss-fuzz

It's a bug in the test itself :
ZSTD_compressBound() as an upper bound of the compress size
only works for data compressed "normally".
But in situations where many flushes are forcefully introduced,
this creates many more blocks,
each of which has a potential to increase the size by 3 bytes.
In extreme cases (lots of small incompressible blocks), the expansion can go beyond ZSTD_compressBound().

This situation is similar when using the CompressSequences() API
with Explicit Block Delimiters.
In which case, each explicit block acts like a deliberate flush.
When employed by a fuzzer, it's possible to generate scenarios like the one described above,
with tons of incompressible blocks of small sizes,
thus going beyond ZSTD_compressBound().

fix : when using Explicit Block Delimiters, use a larger bound, to account for this scenario.

lib/common/zstd_internal.h
lib/compress/zstd_compress.c
tests/fuzz/sequence_compression_api.c

index 4e19cb3c2fbcfb159a5f00e126880cd347152570..485b23a690a7876e911861ee4a0285997a315cc0 100644 (file)
@@ -311,8 +311,8 @@ typedef struct {
      * in the seqStore that has a value larger than U16 (if it exists). To do so, we increment
      * the existing value of the litLength or matchLength by 0x10000.
      */
-    ZSTD_longLengthType_e   longLengthType;
-    U32                     longLengthPos;  /* Index of the sequence to apply long length modification to */
+    ZSTD_longLengthType_e longLengthType;
+    U32                   longLengthPos;  /* Index of the sequence to apply long length modification to */
 } seqStore_t;
 
 typedef struct {
index c82901570226a94a6befafa07eed7ef308d43071..b9358fa0c811a344a5b2866ac3af9f538b59728e 100644 (file)
@@ -6140,7 +6140,7 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
         FORWARD_IF_ERROR(blockSize, "Error while trying to determine block size");
         assert(blockSize <= remaining);
         ZSTD_resetSeqStore(&cctx->seqStore);
-        DEBUGLOG(5, "Working on new block. Blocksize: %zu", blockSize);
+        DEBUGLOG(5, "Working on new block. Blocksize: %zu (total:%zu)", blockSize, (ip - (const BYTE*)src) + blockSize);
 
         additionalByteAdjustment = sequenceCopier(cctx, &seqPos, inSeqs, inSeqsSize, ip, blockSize);
         FORWARD_IF_ERROR(additionalByteAdjustment, "Bad sequence copy");
index 443e0a181485e9383c71047bf47ecfbb3e779a19..68923e11e602bdf5e98636c0e3dd2ae3de97b84b 100644 (file)
@@ -297,7 +297,11 @@ int LLVMFuzzerTestOneInput(const uint8_t* src, size_t size)
     }
     nbSequences = generateRandomSequences(producer, ZSTD_FUZZ_GENERATED_LITERALS_SIZE, dictSize, wLog, mode);
     generatedSrcSize = decodeSequences(generatedSrc, nbSequences, ZSTD_FUZZ_GENERATED_LITERALS_SIZE, dictBuffer, dictSize, mode);
-    cBufSize = ZSTD_compressBound(generatedSrcSize);
+    /* Note : in explicit block delimiters mode,
+     * the fuzzer might generate a lot of small incompressible blocks.
+     * In which case, the final compressed size might be > ZSTD_compressBound().
+     * Solution : provide a much more generous cBufSize to cover these scenarios */
+    cBufSize = (mode == ZSTD_sf_noBlockDelimiters) ? ZSTD_compressBound(generatedSrcSize) : 256 + (generatedSrcSize * 2);
     cBuf = FUZZ_malloc(cBufSize);
 
     rBufSize = generatedSrcSize;