From: Nick Terrell Date: Thu, 9 Jan 2020 19:38:50 +0000 (-0800) Subject: [fuzz] Allow zero sized buffers for streaming fuzzers (#1945) X-Git-Tag: v1.4.5^2~119 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d1cc9d279704057c08ab976618c8bd629bc18f7e;p=thirdparty%2Fzstd.git [fuzz] Allow zero sized buffers for streaming fuzzers (#1945) * Allow zero sized buffers in `stream_decompress`. Ensure that we never have two zero sized buffers in a row so we guarantee forwards progress. * Make case 4 in `stream_round_trip` do a zero sized buffers call followed by a full call to guarantee forwards progress. * Fix `limitCopy()` in legacy decoders. * Fix memcpy in `zstdmt_compress.c`. Catches the bug fixed in PR #1939 --- diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index d05425629..4eaa3fcdd 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -1714,9 +1714,11 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u assert(mtctx->doneJobID < mtctx->nextJobID); assert(cSize >= mtctx->jobs[wJobID].dstFlushed); assert(mtctx->jobs[wJobID].dstBuff.start != NULL); - memcpy((char*)output->dst + output->pos, - (const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed, - toFlush); + if (toFlush > 0) { + memcpy((char*)output->dst + output->pos, + (const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed, + toFlush); + } output->pos += toFlush; mtctx->jobs[wJobID].dstFlushed += toFlush; /* can write : this value is only used by mtctx */ diff --git a/lib/legacy/zstd_v04.c b/lib/legacy/zstd_v04.c index 4dec30816..0150c9fb4 100644 --- a/lib/legacy/zstd_v04.c +++ b/lib/legacy/zstd_v04.c @@ -3407,7 +3407,9 @@ static size_t ZBUFF_decompressWithDictionary(ZBUFF_DCtx* zbc, const void* src, s static size_t ZBUFF_limitCopy(void* dst, size_t maxDstSize, const void* src, size_t srcSize) { size_t length = MIN(maxDstSize, srcSize); - memcpy(dst, src, length); + if (length > 0) { + memcpy(dst, src, length); + } return length; } diff --git a/lib/legacy/zstd_v05.c b/lib/legacy/zstd_v05.c index 570e0ff86..aa564b415 100644 --- a/lib/legacy/zstd_v05.c +++ b/lib/legacy/zstd_v05.c @@ -3791,7 +3791,9 @@ static size_t ZBUFFv05_blockHeaderSize = 3; static size_t ZBUFFv05_limitCopy(void* dst, size_t maxDstSize, const void* src, size_t srcSize) { size_t length = MIN(maxDstSize, srcSize); - memcpy(dst, src, length); + if (length > 0) { + memcpy(dst, src, length); + } return length; } diff --git a/lib/legacy/zstd_v06.c b/lib/legacy/zstd_v06.c index 2a08e8dee..d5d778fce 100644 --- a/lib/legacy/zstd_v06.c +++ b/lib/legacy/zstd_v06.c @@ -4000,7 +4000,9 @@ size_t ZBUFFv06_decompressInit(ZBUFFv06_DCtx* zbd) MEM_STATIC size_t ZBUFFv06_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize) { size_t length = MIN(dstCapacity, srcSize); - memcpy(dst, src, length); + if (length > 0) { + memcpy(dst, src, length); + } return length; } diff --git a/lib/legacy/zstd_v07.c b/lib/legacy/zstd_v07.c index a2eeff808..14a93655e 100644 --- a/lib/legacy/zstd_v07.c +++ b/lib/legacy/zstd_v07.c @@ -4378,7 +4378,9 @@ size_t ZBUFFv07_decompressInit(ZBUFFv07_DCtx* zbd) MEM_STATIC size_t ZBUFFv07_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize) { size_t const length = MIN(dstCapacity, srcSize); - memcpy(dst, src, length); + if (length > 0) { + memcpy(dst, src, length); + } return length; } diff --git a/tests/fuzz/stream_decompress.c b/tests/fuzz/stream_decompress.c index c71cc9d3e..aff9fcec7 100644 --- a/tests/fuzz/stream_decompress.c +++ b/tests/fuzz/stream_decompress.c @@ -27,27 +27,36 @@ static ZSTD_DStream *dstream = NULL; static void* buf = NULL; uint32_t seed; -static ZSTD_outBuffer makeOutBuffer(FUZZ_dataProducer_t *producer) +static ZSTD_outBuffer makeOutBuffer(FUZZ_dataProducer_t *producer, uint32_t min) { ZSTD_outBuffer buffer = { buf, 0, 0 }; - buffer.size = (FUZZ_dataProducer_uint32Range(producer, 1, kBufSize)); + buffer.size = (FUZZ_dataProducer_uint32Range(producer, min, kBufSize)); FUZZ_ASSERT(buffer.size <= kBufSize); + if (buffer.size == 0) { + buffer.dst = NULL; + } + return buffer; } static ZSTD_inBuffer makeInBuffer(const uint8_t **src, size_t *size, - FUZZ_dataProducer_t *producer) + FUZZ_dataProducer_t *producer, + uint32_t min) { ZSTD_inBuffer buffer = { *src, 0, 0 }; FUZZ_ASSERT(*size > 0); - buffer.size = (FUZZ_dataProducer_uint32Range(producer, 1, *size)); + buffer.size = (FUZZ_dataProducer_uint32Range(producer, min, *size)); FUZZ_ASSERT(buffer.size <= *size); *src += buffer.size; *size -= buffer.size; + if (buffer.size == 0) { + buffer.src = NULL; + } + return buffer; } @@ -56,6 +65,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) /* Give a random portion of src data to the producer, to use for parameter generation. The rest will be used for (de)compression */ FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size); + /* Guarantee forward progress by refusing to generate 2 zero sized + * buffers in a row. */ + int prevInWasZero = 0; + int prevOutWasZero = 0; size = FUZZ_dataProducer_reserveDataPrefix(producer); /* Allocate all buffers and contexts if not already allocated */ @@ -72,9 +85,11 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) } while (size > 0) { - ZSTD_inBuffer in = makeInBuffer(&src, &size, producer); + ZSTD_inBuffer in = makeInBuffer(&src, &size, producer, prevInWasZero ? 1 : 0); + prevInWasZero = in.size == 0; while (in.pos != in.size) { - ZSTD_outBuffer out = makeOutBuffer(producer); + ZSTD_outBuffer out = makeOutBuffer(producer, prevOutWasZero ? 1 : 0); + prevOutWasZero = out.size == 0; size_t const rc = ZSTD_decompressStream(dstream, &out, &in); if (ZSTD_isError(rc)) goto error; } diff --git a/tests/fuzz/stream_round_trip.c b/tests/fuzz/stream_round_trip.c index 703b11713..810ed05eb 100644 --- a/tests/fuzz/stream_round_trip.c +++ b/tests/fuzz/stream_round_trip.c @@ -96,6 +96,13 @@ static size_t compress(uint8_t *dst, size_t capacity, } break; } + case 4: { + ZSTD_inBuffer nullIn = { NULL, 0, 0 }; + ZSTD_outBuffer nullOut = { NULL, 0, 0 }; + size_t const ret = ZSTD_compressStream2(cctx, &nullOut, &nullIn, ZSTD_e_continue); + FUZZ_ZASSERT(ret); + } + /* fall-through */ default: { size_t const ret = ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_continue);