]> git.ipfire.org Git - thirdparty/zstd.git/commitdiff
[lib] Fix NULL pointer dereference
authorNick Terrell <terrelln@fb.com>
Fri, 1 May 2020 23:35:35 +0000 (16:35 -0700)
committerNick Terrell <nickrterrell@gmail.com>
Wed, 6 May 2020 19:09:02 +0000 (12:09 -0700)
When the output buffer is `NULL` with size 0, but the frame content size
is non-zero, we will write to the NULL pointer because our bounds check
underflowed.

This was exposed by a recent PR that allowed an empty frame into the
single-pass shortcut in streaming mode.

* Fix the bug.
* Fix another NULL dereference in zstd-v1.
* Overflow checks in 32-bit mode.
* Add a dedicated test.
* Expose the bug in the dedicated simple_decompress fuzzer.
* Switch all mallocs in fuzzers to return NULL for size=0.
* Fix a new timeout in a fuzzer.

Neither clang nor gcc show a decompression speed regression on x86-64.
On x86-32 clang is slightly positive and gcc loses 2.5% of speed.

Credit to OSS-Fuzz.

26 files changed:
lib/common/xxhash.c
lib/decompress/zstd_decompress_block.c
lib/legacy/zstd_v01.c
lib/legacy/zstd_v02.c
lib/legacy/zstd_v03.c
lib/legacy/zstd_v04.c
lib/legacy/zstd_v05.c
lib/legacy/zstd_v06.c
lib/legacy/zstd_v07.c
tests/fuzz/Makefile
tests/fuzz/block_decompress.c
tests/fuzz/block_round_trip.c
tests/fuzz/dictionary_decompress.c
tests/fuzz/dictionary_loader.c
tests/fuzz/dictionary_round_trip.c
tests/fuzz/fuzz_data_producer.c
tests/fuzz/fuzz_helpers.c [new file with mode: 0644]
tests/fuzz/fuzz_helpers.h
tests/fuzz/simple_compress.c
tests/fuzz/simple_decompress.c
tests/fuzz/simple_round_trip.c
tests/fuzz/stream_decompress.c
tests/fuzz/stream_round_trip.c
tests/fuzz/zstd_frame_info.c
tests/fuzz/zstd_helpers.c
tests/fuzzer.c

index 72314c59f363a2befda27e0874ba0852b68e6bd4..597de18fc895619fe26145ae0c2c3f3b1982d7a5 100644 (file)
@@ -709,7 +709,9 @@ FORCE_INLINE_TEMPLATE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, c
     state->total_len += len;
 
     if (state->memsize + len < 32) {  /* fill in tmp buffer */
-        XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);
+        if (input != NULL) {
+            XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);
+        }
         state->memsize += (U32)len;
         return XXH_OK;
     }
index 6eb7462cd10c3e9d0109f2f838cc7c8552e8744d..3b9ba37717b3864fde06c5c848aad3bc58eb62db 100644 (file)
@@ -665,15 +665,15 @@ size_t ZSTD_execSequenceEnd(BYTE* op,
 {
     BYTE* const oLitEnd = op + sequence.litLength;
     size_t const sequenceLength = sequence.litLength + sequence.matchLength;
-    BYTE* const oMatchEnd = op + sequenceLength;   /* risk : address space overflow (32-bits) */
     const BYTE* const iLitEnd = *litPtr + sequence.litLength;
     const BYTE* match = oLitEnd - sequence.offset;
     BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH;
 
-    /* bounds checks */
-    assert(oLitEnd < oMatchEnd);
-    RETURN_ERROR_IF(oMatchEnd > oend, dstSize_tooSmall, "last match must fit within dstBuffer");
-    RETURN_ERROR_IF(iLitEnd > litLimit, corruption_detected, "try to read beyond literal buffer");
+    /* bounds checks : careful of address space overflow in 32-bit mode */
+    RETURN_ERROR_IF(sequenceLength > (size_t)(oend - op), dstSize_tooSmall, "last match must fit within dstBuffer");
+    RETURN_ERROR_IF(sequence.litLength > (size_t)(litLimit - *litPtr), corruption_detected, "try to read beyond literal buffer");
+    assert(op < op + sequenceLength);
+    assert(oLitEnd < op + sequenceLength);
 
     /* copy literals */
     ZSTD_safecopy(op, oend_w, *litPtr, sequence.litLength, ZSTD_no_overlap);
@@ -709,16 +709,27 @@ size_t ZSTD_execSequence(BYTE* op,
     BYTE* const oLitEnd = op + sequence.litLength;
     size_t const sequenceLength = sequence.litLength + sequence.matchLength;
     BYTE* const oMatchEnd = op + sequenceLength;   /* risk : address space overflow (32-bits) */
-    BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH;
+    BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH;   /* risk : address space underflow on oend=NULL */
     const BYTE* const iLitEnd = *litPtr + sequence.litLength;
     const BYTE* match = oLitEnd - sequence.offset;
 
-    /* Errors and uncommon cases handled here. */
-    assert(oLitEnd < oMatchEnd);
-    if (UNLIKELY(iLitEnd > litLimit || oMatchEnd > oend_w))
+    assert(op != NULL /* Precondition */);
+    assert(oend_w < oend /* No underflow */);
+    /* Handle edge cases in a slow path:
+     *   - Read beyond end of literals
+     *   - Match end is within WILDCOPY_OVERLIMIT of oend
+     *   - 32-bit mode and the match length overflows
+     */
+    if (UNLIKELY(
+            iLitEnd > litLimit ||
+            oMatchEnd > oend_w ||
+            (MEM_32bits() && (size_t)(oend - op) < sequenceLength + WILDCOPY_OVERLENGTH)))
         return ZSTD_execSequenceEnd(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd);
 
     /* Assumptions (everything else goes into ZSTD_execSequenceEnd()) */
+    assert(op <= oLitEnd /* No overflow */);
+    assert(oLitEnd < oMatchEnd /* Non-zero match & no overflow */);
+    assert(oMatchEnd <= oend /* No underflow */);
     assert(iLitEnd <= litLimit /* Literal length is in bounds */);
     assert(oLitEnd <= oend_w /* Can wildcopy literals */);
     assert(oMatchEnd <= oend_w /* Can wildcopy matches */);
@@ -968,6 +979,7 @@ ZSTD_decompressSequences_body( ZSTD_DCtx* dctx,
         ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr);
         ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
         ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
+        assert(dst != NULL);
 
         ZSTD_STATIC_ASSERT(
                 BIT_DStream_unfinished < BIT_DStream_completed &&
@@ -1043,8 +1055,10 @@ ZSTD_decompressSequences_body( ZSTD_DCtx* dctx,
     /* last literal segment */
     {   size_t const lastLLSize = litEnd - litPtr;
         RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, "");
-        memcpy(op, litPtr, lastLLSize);
-        op += lastLLSize;
+        if (op != NULL) {
+            memcpy(op, litPtr, lastLLSize);
+            op += lastLLSize;
+        }
     }
 
     return op-ostart;
@@ -1093,6 +1107,7 @@ ZSTD_decompressSequencesLong_body(
         seqState.prefixStart = prefixStart;
         seqState.pos = (size_t)(op-prefixStart);
         seqState.dictEnd = dictEnd;
+        assert(dst != NULL);
         assert(iend >= ip);
         RETURN_ERROR_IF(
             ERR_isError(BIT_initDStream(&seqState.DStream, ip, iend-ip)),
@@ -1134,8 +1149,10 @@ ZSTD_decompressSequencesLong_body(
     /* last literal segment */
     {   size_t const lastLLSize = litEnd - litPtr;
         RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, "");
-        memcpy(op, litPtr, lastLLSize);
-        op += lastLLSize;
+        if (op != NULL) {
+            memcpy(op, litPtr, lastLLSize);
+            op += lastLLSize;
+        }
     }
 
     return op-ostart;
@@ -1255,7 +1272,6 @@ ZSTD_getLongOffsetsShare(const ZSTD_seqSymbol* offTable)
 }
 #endif
 
-
 size_t
 ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx,
                               void* dst, size_t dstCapacity,
@@ -1297,6 +1313,8 @@ ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx,
         ip += seqHSize;
         srcSize -= seqHSize;
 
+        RETURN_ERROR_IF(dst == NULL && nbSeq > 0, dstSize_tooSmall, "NULL not handled");
+
 #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \
     !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG)
         if ( !usePrefetchDecoder
index 0517222ca5cef91d34cecd7fa53b92159b256f7e..eb23628330e7970a11148c4b9236bedb48b9f149 100644 (file)
@@ -1078,7 +1078,7 @@ static size_t HUF_decompress_usingDTable(   /* -3% slower when non static */
         BYTE* const ostart = (BYTE*) dst;
         BYTE* op = ostart;
         BYTE* const omax = op + maxDstSize;
-        BYTE* const olimit = omax-15;
+        BYTE* const olimit = maxDstSize < 15 ? op : omax-15;
 
         const void* ptr = DTable;
         const HUF_DElt* const dt = (const HUF_DElt*)(ptr)+1;
@@ -1483,7 +1483,9 @@ static size_t ZSTDv01_getcBlockSize(const void* src, size_t srcSize, blockProper
 static size_t ZSTD_copyUncompressedBlock(void* dst, size_t maxDstSize, const void* src, size_t srcSize)
 {
     if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall);
-    memcpy(dst, src, srcSize);
+    if (srcSize > 0) {
+        memcpy(dst, src, srcSize);
+    }
     return srcSize;
 }
 
@@ -1541,7 +1543,9 @@ static size_t ZSTDv01_decodeLiteralsBlock(void* ctx,
             size_t rleSize = litbp.origSize;
             if (rleSize>maxDstSize) return ERROR(dstSize_tooSmall);
             if (!srcSize) return ERROR(srcSize_wrong);
-            memset(oend - rleSize, *ip, rleSize);
+            if (rleSize > 0) {
+                memset(oend - rleSize, *ip, rleSize);
+            }
             *litStart = oend - rleSize;
             *litSize = rleSize;
             ip++;
@@ -1901,8 +1905,10 @@ static size_t ZSTD_decompressSequences(
         {
             size_t lastLLSize = litEnd - litPtr;
             if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
-            if (op != litPtr) memmove(op, litPtr, lastLLSize);
-            op += lastLLSize;
+            if (lastLLSize > 0) {
+                if (op != litPtr) memmove(op, litPtr, lastLLSize);
+                op += lastLLSize;
+            }
         }
     }
 
index a7aa27cdfe2fde566763dea408fcb8a05bc16813..32d45a6d4bd3683bf1eecb3d81535b332a9e838a 100644 (file)
@@ -2836,7 +2836,9 @@ static size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockPropertie
 static size_t ZSTD_copyUncompressedBlock(void* dst, size_t maxDstSize, const void* src, size_t srcSize)
 {
     if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall);
-    memcpy(dst, src, srcSize);
+    if (srcSize > 0) {
+        memcpy(dst, src, srcSize);
+    }
     return srcSize;
 }
 
@@ -3229,8 +3231,10 @@ static size_t ZSTD_decompressSequences(
             size_t lastLLSize = litEnd - litPtr;
             if (litPtr > litEnd) return ERROR(corruption_detected);
             if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
-            if (op != litPtr) memmove(op, litPtr, lastLLSize);
-            op += lastLLSize;
+            if (lastLLSize > 0) {
+                if (op != litPtr) memmove(op, litPtr, lastLLSize);
+                op += lastLLSize;
+            }
         }
     }
 
index 1b00c98bfea8b2c511a22d9026ca5df2a72831e7..b541eae2abb72ed055282c3e8cde56dd95adc242 100644 (file)
@@ -2477,7 +2477,9 @@ static size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockPropertie
 static size_t ZSTD_copyUncompressedBlock(void* dst, size_t maxDstSize, const void* src, size_t srcSize)
 {
     if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall);
-    memcpy(dst, src, srcSize);
+    if (srcSize > 0) {
+        memcpy(dst, src, srcSize);
+    }
     return srcSize;
 }
 
@@ -2870,8 +2872,10 @@ static size_t ZSTD_decompressSequences(
             size_t lastLLSize = litEnd - litPtr;
             if (litPtr > litEnd) return ERROR(corruption_detected);
             if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
-            if (op != litPtr) memmove(op, litPtr, lastLLSize);
-            op += lastLLSize;
+            if (lastLLSize > 0) {
+                if (op != litPtr) memmove(op, litPtr, lastLLSize);
+                op += lastLLSize;
+            }
         }
     }
 
index 1a3d49cf6946b10ada2807cf6699a60a6fdf1b65..56bf4522249865666ed0b3d3a3a2064e554a1bb8 100644 (file)
@@ -2603,7 +2603,9 @@ static size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, blockPropertie
 static size_t ZSTD_copyRawBlock(void* dst, size_t maxDstSize, const void* src, size_t srcSize)
 {
     if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall);
-    memcpy(dst, src, srcSize);
+    if (srcSize > 0) {
+        memcpy(dst, src, srcSize);
+    }
     return srcSize;
 }
 
@@ -3008,8 +3010,10 @@ static size_t ZSTD_decompressSequences(
             size_t lastLLSize = litEnd - litPtr;
             if (litPtr > litEnd) return ERROR(corruption_detected);
             if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
-            if (op != litPtr) memcpy(op, litPtr, lastLLSize);
-            op += lastLLSize;
+            if (lastLLSize > 0) {
+                if (op != litPtr) memcpy(op, litPtr, lastLLSize);
+                op += lastLLSize;
+            }
         }
     }
 
index 79ea59a1b842a2d408fbf50844be394ba36b6b9e..243d222562e3ff3b909ca88ac031758f23e8782a 100644 (file)
@@ -3362,8 +3362,10 @@ static size_t ZSTDv05_decompressSequences(
         size_t lastLLSize = litEnd - litPtr;
         if (litPtr > litEnd) return ERROR(corruption_detected);   /* too many literals already used */
         if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
-        memcpy(op, litPtr, lastLLSize);
-        op += lastLLSize;
+        if (lastLLSize > 0) {
+            memcpy(op, litPtr, lastLLSize);
+            op += lastLLSize;
+        }
     }
 
     return op-ostart;
index 1a670fad0fb18a9c61574f5c2ac469bd4bdffb1d..c56f582772f89e207aeffe35cbe275950eeeff92 100644 (file)
@@ -3501,8 +3501,10 @@ static size_t ZSTDv06_decompressSequences(
     {   size_t const lastLLSize = litEnd - litPtr;
         if (litPtr > litEnd) return ERROR(corruption_detected);   /* too many literals already used */
         if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
-        memcpy(op, litPtr, lastLLSize);
-        op += lastLLSize;
+        if (lastLLSize > 0) {
+            memcpy(op, litPtr, lastLLSize);
+            op += lastLLSize;
+        }
     }
 
     return op-ostart;
index 5393475dad067091540c61aeff0d1f0280ea09ff..9f3a597f1254578dae51069b494383d394eac05a 100644 (file)
@@ -3272,7 +3272,9 @@ static size_t ZSTDv07_getcBlockSize(const void* src, size_t srcSize, blockProper
 static size_t ZSTDv07_copyRawBlock(void* dst, size_t dstCapacity, const void* src, size_t srcSize)
 {
     if (srcSize > dstCapacity) return ERROR(dstSize_tooSmall);
-    memcpy(dst, src, srcSize);
+    if (srcSize > 0) {
+        memcpy(dst, src, srcSize);
+    }
     return srcSize;
 }
 
@@ -3714,8 +3716,10 @@ static size_t ZSTDv07_decompressSequences(
     {   size_t const lastLLSize = litEnd - litPtr;
         /* if (litPtr > litEnd) return ERROR(corruption_detected); */   /* too many literals already used */
         if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall);
-        memcpy(op, litPtr, lastLLSize);
-        op += lastLLSize;
+        if (lastLLSize > 0) {
+            memcpy(op, litPtr, lastLLSize);
+            op += lastLLSize;
+        }
     }
 
     return op-ostart;
@@ -3776,7 +3780,9 @@ ZSTDLIBv07_API size_t ZSTDv07_insertBlock(ZSTDv07_DCtx* dctx, const void* blockS
 static size_t ZSTDv07_generateNxBytes(void* dst, size_t dstCapacity, BYTE byte, size_t length)
 {
     if (length > dstCapacity) return ERROR(dstSize_tooSmall);
-    memset(dst, byte, length);
+    if (length > 0) {
+        memset(dst, byte, length);
+    }
     return length;
 }
 
index a7f3a73a068b801e67ba7db4034adffa30d79f02..571ab553bd7b616b8f0303af3ac4fd033810958f 100644 (file)
@@ -42,7 +42,7 @@ FUZZ_ARFLAGS := $(ARFLAGS)
 FUZZ_TARGET_FLAGS = $(FUZZ_CPPFLAGS) $(FUZZ_CXXFLAGS) $(FUZZ_LDFLAGS)
 
 FUZZ_HEADERS := fuzz_helpers.h fuzz.h zstd_helpers.h fuzz_data_producer.h
-FUZZ_SRC := $(PRGDIR)/util.c zstd_helpers.c fuzz_data_producer.c
+FUZZ_SRC := $(PRGDIR)/util.c fuzz_helpers.c zstd_helpers.c fuzz_data_producer.c
 
 ZSTDCOMMON_SRC := $(ZSTDDIR)/common/*.c
 ZSTDCOMP_SRC   := $(ZSTDDIR)/compress/*.c
index f2a280b6ca937cf34b93d6939e13599aff417c6b..64d70f005e926d00503478b2f5a633aaeac35c15 100644 (file)
@@ -32,9 +32,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
     /* Allocate all buffers and contexts if not already allocated */
     if (neededBufSize > bufSize) {
         free(rBuf);
-        rBuf = malloc(neededBufSize);
+        rBuf = FUZZ_malloc(neededBufSize);
         bufSize = neededBufSize;
-        FUZZ_ASSERT(rBuf);
     }
     if (!dctx) {
         dctx = ZSTD_createDCtx();
index 8048cb263c39f861acf3ad79bd31714f691466c2..097fc01b89b648aabc24f2301d6dd846a408ff45 100644 (file)
@@ -43,7 +43,9 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
     FUZZ_ZASSERT(ret);
     if (ret == 0) {
         FUZZ_ASSERT(resultCapacity >= srcSize);
-        memcpy(result, src, srcSize);
+        if (srcSize > 0) {
+            memcpy(result, src, srcSize);
+        }
         return srcSize;
     }
     ZSTD_decompressBegin(dctx);
@@ -67,10 +69,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
     if (neededBufSize > bufSize || !cBuf || !rBuf) {
         free(cBuf);
         free(rBuf);
-        cBuf = malloc(neededBufSize);
-        rBuf = malloc(neededBufSize);
+        cBuf = FUZZ_malloc(neededBufSize);
+        rBuf = FUZZ_malloc(neededBufSize);
         bufSize = neededBufSize;
-        FUZZ_ASSERT(cBuf && rBuf);
     }
     if (!cctx) {
         cctx = ZSTD_createCCtx();
@@ -87,7 +88,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
               cLevel);
         FUZZ_ZASSERT(result);
         FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size");
-        FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
+        FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
     }
     FUZZ_dataProducer_free(producer);
 #ifndef STATEFUL_FUZZING
index 55d2a57f18f332ab42af34b5131f0932d6d04a51..9944baa158b60ad98735abe90a5b4b31d313bbe0 100644 (file)
@@ -55,8 +55,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
 
     {
         size_t const bufSize = FUZZ_dataProducer_uint32Range(producer, 0, 10 * size);
-        void* rBuf = malloc(bufSize);
-        FUZZ_ASSERT(rBuf);
+        void* rBuf = FUZZ_malloc(bufSize);
         if (ddict) {
             ZSTD_decompress_usingDDict(dctx, rBuf, bufSize, src, size, ddict);
         } else {
index 5a211c5de4b2ab562ac81e77d6c40c8a00b90c9d..f1fdf4da9e6781f21ef6bafee24de8f5623abcae 100644 (file)
@@ -79,11 +79,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
     DEBUGLOG(2, "Dict content type %d", dct);
     DEBUGLOG(2, "Dict size %u", (unsigned)size);
 
-    void* const rBuf = malloc(size);
-    FUZZ_ASSERT(rBuf);
+    void* const rBuf = FUZZ_malloc(size);
     size_t const cBufSize = ZSTD_compressBound(size);
-    void* const cBuf = malloc(cBufSize);
-    FUZZ_ASSERT(cBuf);
+    void* const cBuf = FUZZ_malloc(cBufSize);
 
     size_t const cSize =
             compress(cBuf, cBufSize, src, size, src, size, dlm, dct, refPrefix);
@@ -95,7 +93,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
     size_t const rSize =
             decompress(rBuf, size, cBuf, cSize, src, size, dlm, dct, refPrefix);
     FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size");
-    FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
+    FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
 
 out:
     free(cBuf);
index ce3cd672797b4b0cde79422f5f18f089c187b089..cd38178d6ae8099b9cff81fe454f793710ec7041 100644 (file)
@@ -84,7 +84,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
     size = FUZZ_dataProducer_reserveDataPrefix(producer);
 
     size_t const rBufSize = size;
-    void* rBuf = malloc(rBufSize);
+    void* rBuf = FUZZ_malloc(rBufSize);
     size_t cBufSize = ZSTD_compressBound(size) * 2;
     void *cBuf;
     /* Half of the time fuzz with a 1 byte smaller output size.
@@ -92,7 +92,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
      * giving us 4 bytes of overhead.
      */
     cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1);
-    cBuf = malloc(cBufSize);
+    cBuf = FUZZ_malloc(cBufSize);
 
     if (!cctx) {
         cctx = ZSTD_createCCtx();
@@ -108,7 +108,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
             roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer);
         FUZZ_ZASSERT(result);
         FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size");
-        FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
+        FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
     }
     free(rBuf);
     free(cBuf);
index 00bb0a3b21b1eadd9585f4ed4d2ab230959c38d0..6518af30900146782af0dcc575219d5377607305 100644 (file)
@@ -16,9 +16,7 @@ struct FUZZ_dataProducer_s{
 };
 
 FUZZ_dataProducer_t *FUZZ_dataProducer_create(const uint8_t *data, size_t size) {
-    FUZZ_dataProducer_t *producer = malloc(sizeof(FUZZ_dataProducer_t));
-
-    FUZZ_ASSERT(producer != NULL);
+    FUZZ_dataProducer_t *producer = FUZZ_malloc(sizeof(FUZZ_dataProducer_t));
 
     producer->data = data;
     producer->size = size;
diff --git a/tests/fuzz/fuzz_helpers.c b/tests/fuzz/fuzz_helpers.c
new file mode 100644 (file)
index 0000000..b80dc75
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016-2020, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+#include "fuzz_helpers.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+void* FUZZ_malloc(size_t size)
+{
+    if (size > 0) {
+        void* const mem = malloc(size);
+        FUZZ_ASSERT(mem);
+        return mem;
+    }
+    return NULL;
+}
+
+int FUZZ_memcmp(void const* lhs, void const* rhs, size_t size)
+{
+    if (size == 0) {
+        return 0;
+    }
+    return memcmp(lhs, rhs, size);
+}
\ No newline at end of file
index 1420a65d2ce7951387787eea1496b5ca4e26238b..cde2c4ea7cf24814d1c8c1b8e40929a66e44a062 100644 (file)
@@ -56,6 +56,17 @@ extern "C" {
 #define FUZZ_STATIC static
 #endif
 
+/**
+ * malloc except return NULL for zero sized data and FUZZ_ASSERT
+ * that malloc doesn't fail.
+ */
+void* FUZZ_malloc(size_t size);
+
+/**
+ * memcmp but accepts NULL.
+ */
+int FUZZ_memcmp(void const* lhs, void const* rhs, size_t size);
+
 #ifdef __cplusplus
 }
 #endif
index f155e3b1c3856f33ce1ea14e8c98609a50d5e712..b64f373eb8a410e5b6c2a20302c41639f10587fc 100644 (file)
@@ -41,8 +41,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
         FUZZ_ASSERT(cctx);
     }
 
-    void *rBuf = malloc(bufSize);
-    FUZZ_ASSERT(rBuf);
+    void *rBuf = FUZZ_malloc(bufSize);
     ZSTD_compressCCtx(cctx, rBuf, bufSize, src, size, cLevel);
     free(rBuf);
     FUZZ_dataProducer_free(producer);
index 8c404d13900b888cea3a6e437497bb630f82caa4..c3903ce8bfa35db89a0b67482d0897e311606c79 100644 (file)
@@ -35,8 +35,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
     }
 
     size_t const bufSize = FUZZ_dataProducer_uint32Range(producer, 0, 10 * size);
-    void *rBuf = malloc(bufSize);
-    FUZZ_ASSERT(rBuf);
+    void *rBuf = FUZZ_malloc(bufSize);
 
     ZSTD_decompressDCtx(dctx, rBuf, bufSize, src, size);
     free(rBuf);
index 41ea96739fed01ee79c52ef53a39f2f9baf93ad4..348da03a9624b9c52b93db6254db4458d1a5f174 100644 (file)
@@ -70,7 +70,7 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
 int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
 {
     size_t const rBufSize = size;
-    void* rBuf = malloc(rBufSize);
+    void* rBuf = FUZZ_malloc(rBufSize);
     size_t cBufSize = ZSTD_compressBound(size);
     void* cBuf;
 
@@ -85,9 +85,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
      */
     cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1);
 
-    cBuf = malloc(cBufSize);
-
-    FUZZ_ASSERT(cBuf && rBuf);
+    cBuf = FUZZ_malloc(cBufSize);
 
     if (!cctx) {
         cctx = ZSTD_createCCtx();
@@ -103,7 +101,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
             roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer);
         FUZZ_ZASSERT(result);
         FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size");
-        FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
+        FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
     }
     free(rBuf);
     free(cBuf);
index 503d32d661450c2648c1493dc4f57a325017e8e9..25901b1eb37c9eb9553e0d62b31be30d1c8b2fd2 100644 (file)
@@ -76,9 +76,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
 
     /* Allocate all buffers and contexts if not already allocated */
     if (!buf) {
-      buf = malloc(kBufSize);
-        FUZZ_ASSERT(buf);
-      }
+        buf = FUZZ_malloc(kBufSize);
+    }
 
     if (!dstream) {
         dstream = ZSTD_createDStream();
@@ -99,7 +98,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
         ZSTD_inBuffer in = makeInBuffer(&src, &size, producer, prevInWasZero ? 1 : 0);
         prevInWasZero = in.size == 0;
         while (in.pos != in.size) {
-            if (!stableOutBuffer || FUZZ_dataProducer_uint32Range(producer, 0, 100) == 55) {
+            if (!stableOutBuffer || prevOutWasZero || FUZZ_dataProducer_uint32Range(producer, 0, 100) == 55) {
               out = makeOutBuffer(producer, prevOutWasZero ? 1 : 0);
             }
             prevOutWasZero = out.size == 0;
index e060950f01632f6e8752cba6b86590918bc631ca..286d3871b7add4c82c8b7b2897760147b035120d 100644 (file)
@@ -146,10 +146,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
     if (neededBufSize > bufSize) {
         free(cBuf);
         free(rBuf);
-        cBuf = (uint8_t*)malloc(neededBufSize);
-        rBuf = (uint8_t*)malloc(neededBufSize);
+        cBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
+        rBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
         bufSize = neededBufSize;
-        FUZZ_ASSERT(cBuf && rBuf);
     }
     if (!cctx) {
         cctx = ZSTD_createCCtx();
@@ -166,7 +165,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
             ZSTD_decompressDCtx(dctx, rBuf, neededBufSize, cBuf, cSize);
         FUZZ_ZASSERT(rSize);
         FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size");
-        FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
+        FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
     }
 
     FUZZ_dataProducer_free(producer);
index 16a28d6903a30740257679837243084730e549c1..876a74e9aeb4c92f23802e06db142a87d481383e 100644 (file)
@@ -22,6 +22,9 @@
 int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
 {
     ZSTD_frameHeader zfh;
+    if (size == 0) {
+        src = NULL;
+   }
     /* You can fuzz any helper functions here that are fast, and take zstd
      * compressed data as input. E.g. don't expect the input to be a dictionary,
      * so don't fuzz ZSTD_getDictID_fromDict().
index 8294f6fe235d1b3215240455a7d77996982a86da..5680bd628e1672b6b9e2977fb54da63090165406 100644 (file)
@@ -106,14 +106,13 @@ FUZZ_dict_t FUZZ_train(void const* src, size_t srcSize, FUZZ_dataProducer_t *pro
 {
     size_t const dictSize = MAX(srcSize / 8, 1024);
     size_t const totalSampleSize = dictSize * 11;
-    FUZZ_dict_t dict = { malloc(dictSize), dictSize };
-    char* const samples = (char*)malloc(totalSampleSize);
+    FUZZ_dict_t dict = { FUZZ_malloc(dictSize), dictSize };
+    char* const samples = (char*)FUZZ_malloc(totalSampleSize);
     unsigned nbSamples = 100;
-    size_t* const samplesSizes = (size_t*)malloc(sizeof(size_t) * nbSamples);
+    size_t* const samplesSizes = (size_t*)FUZZ_malloc(sizeof(size_t) * nbSamples);
     size_t pos = 0;
     size_t sample = 0;
     ZDICT_fastCover_params_t params;
-    FUZZ_ASSERT(dict.buff && samples && samplesSizes);
 
     for (sample = 0; sample < nbSamples; ++sample) {
       size_t const remaining = totalSampleSize - pos;
@@ -123,7 +122,6 @@ FUZZ_dict_t FUZZ_train(void const* src, size_t srcSize, FUZZ_dataProducer_t *pro
       memcpy(samples + pos, src + offset, toCopy);
       pos += toCopy;
       samplesSizes[sample] = toCopy;
-
     }
     memset(samples + pos, 0, totalSampleSize - pos);
 
index 233468106a490167ea7a172b5d7f6890c8fe0130..c9cf85bdbe45953447e81c8cf8f9cab8c3a87cfa 100644 (file)
@@ -538,6 +538,12 @@ static int basicUnitTests(U32 const seed, double compressibility)
       if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; }
     DISPLAYLEVEL(3, "OK \n");
 
+    DISPLAYLEVEL(3, "test%3i : decompress into NULL buffer : ", testNb++);
+    { size_t const r = ZSTD_decompress(NULL, 0, compressedBuffer, compressedBufferSize);
+      if (!ZSTD_isError(r)) goto _output_error;
+      if (ZSTD_getErrorCode(r) != ZSTD_error_dstSize_tooSmall) goto _output_error; }
+    DISPLAYLEVEL(3, "OK \n");
+
     DISPLAYLEVEL(3, "test%3i : ZSTD_decompressBound test with content size missing : ", testNb++);
     {   /* create compressed buffer with content size missing */
         ZSTD_CCtx* const cctx = ZSTD_createCCtx();