[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:
@@ -1714,9 +1714,11 @@ static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, u
|
|||||||
assert(mtctx->doneJobID < mtctx->nextJobID);
|
assert(mtctx->doneJobID < mtctx->nextJobID);
|
||||||
assert(cSize >= mtctx->jobs[wJobID].dstFlushed);
|
assert(cSize >= mtctx->jobs[wJobID].dstFlushed);
|
||||||
assert(mtctx->jobs[wJobID].dstBuff.start != NULL);
|
assert(mtctx->jobs[wJobID].dstBuff.start != NULL);
|
||||||
memcpy((char*)output->dst + output->pos,
|
if (toFlush > 0) {
|
||||||
(const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed,
|
memcpy((char*)output->dst + output->pos,
|
||||||
toFlush);
|
(const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed,
|
||||||
|
toFlush);
|
||||||
|
}
|
||||||
output->pos += toFlush;
|
output->pos += toFlush;
|
||||||
mtctx->jobs[wJobID].dstFlushed += toFlush; /* can write : this value is only used by mtctx */
|
mtctx->jobs[wJobID].dstFlushed += toFlush; /* can write : this value is only used by mtctx */
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
static size_t ZBUFF_limitCopy(void* dst, size_t maxDstSize, const void* src, size_t srcSize)
|
||||||
{
|
{
|
||||||
size_t length = MIN(maxDstSize, srcSize);
|
size_t length = MIN(maxDstSize, srcSize);
|
||||||
memcpy(dst, src, length);
|
if (length > 0) {
|
||||||
|
memcpy(dst, src, length);
|
||||||
|
}
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
static size_t ZBUFFv05_limitCopy(void* dst, size_t maxDstSize, const void* src, size_t srcSize)
|
||||||
{
|
{
|
||||||
size_t length = MIN(maxDstSize, srcSize);
|
size_t length = MIN(maxDstSize, srcSize);
|
||||||
memcpy(dst, src, length);
|
if (length > 0) {
|
||||||
|
memcpy(dst, src, length);
|
||||||
|
}
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
MEM_STATIC size_t ZBUFFv06_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize)
|
||||||
{
|
{
|
||||||
size_t length = MIN(dstCapacity, srcSize);
|
size_t length = MIN(dstCapacity, srcSize);
|
||||||
memcpy(dst, src, length);
|
if (length > 0) {
|
||||||
|
memcpy(dst, src, length);
|
||||||
|
}
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
MEM_STATIC size_t ZBUFFv07_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize)
|
||||||
{
|
{
|
||||||
size_t const length = MIN(dstCapacity, srcSize);
|
size_t const length = MIN(dstCapacity, srcSize);
|
||||||
memcpy(dst, src, length);
|
if (length > 0) {
|
||||||
|
memcpy(dst, src, length);
|
||||||
|
}
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,27 +27,36 @@ static ZSTD_DStream *dstream = NULL;
|
|||||||
static void* buf = NULL;
|
static void* buf = NULL;
|
||||||
uint32_t seed;
|
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 };
|
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);
|
FUZZ_ASSERT(buffer.size <= kBufSize);
|
||||||
|
|
||||||
|
if (buffer.size == 0) {
|
||||||
|
buffer.dst = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ZSTD_inBuffer makeInBuffer(const uint8_t **src, size_t *size,
|
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 };
|
ZSTD_inBuffer buffer = { *src, 0, 0 };
|
||||||
|
|
||||||
FUZZ_ASSERT(*size > 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);
|
FUZZ_ASSERT(buffer.size <= *size);
|
||||||
*src += buffer.size;
|
*src += buffer.size;
|
||||||
*size -= buffer.size;
|
*size -= buffer.size;
|
||||||
|
|
||||||
|
if (buffer.size == 0) {
|
||||||
|
buffer.src = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return buffer;
|
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
|
/* Give a random portion of src data to the producer, to use for
|
||||||
parameter generation. The rest will be used for (de)compression */
|
parameter generation. The rest will be used for (de)compression */
|
||||||
FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
|
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);
|
size = FUZZ_dataProducer_reserveDataPrefix(producer);
|
||||||
|
|
||||||
/* Allocate all buffers and contexts if not already allocated */
|
/* 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) {
|
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) {
|
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);
|
size_t const rc = ZSTD_decompressStream(dstream, &out, &in);
|
||||||
if (ZSTD_isError(rc)) goto error;
|
if (ZSTD_isError(rc)) goto error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,13 @@ static size_t compress(uint8_t *dst, size_t capacity,
|
|||||||
}
|
}
|
||||||
break;
|
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: {
|
default: {
|
||||||
size_t const ret =
|
size_t const ret =
|
||||||
ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_continue);
|
ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_continue);
|
||||||
|
|||||||
Reference in New Issue
Block a user