[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
This commit is contained in:
Nick Terrell 2020-01-09 11:38:50 -08:00 committed by GitHub
parent 03ffda7b88
commit d1cc9d2797
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 45 additions and 13 deletions

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);