[lib] Fix NULL pointer dereference

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.
dev
Nick Terrell 2020-05-01 16:35:35 -07:00 committed by Nick Terrell
parent ad8dbae1b7
commit 5717bd39ee
26 changed files with 167 additions and 80 deletions

View File

@ -709,7 +709,9 @@ FORCE_INLINE_TEMPLATE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, c
state->total_len += len; state->total_len += len;
if (state->memsize + len < 32) { /* fill in tmp buffer */ 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; state->memsize += (U32)len;
return XXH_OK; return XXH_OK;
} }

View File

@ -665,15 +665,15 @@ size_t ZSTD_execSequenceEnd(BYTE* op,
{ {
BYTE* const oLitEnd = op + sequence.litLength; BYTE* const oLitEnd = op + sequence.litLength;
size_t const sequenceLength = sequence.litLength + sequence.matchLength; 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* const iLitEnd = *litPtr + sequence.litLength;
const BYTE* match = oLitEnd - sequence.offset; const BYTE* match = oLitEnd - sequence.offset;
BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH; BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH;
/* bounds checks */ /* bounds checks : careful of address space overflow in 32-bit mode */
assert(oLitEnd < oMatchEnd); RETURN_ERROR_IF(sequenceLength > (size_t)(oend - op), dstSize_tooSmall, "last match must fit within dstBuffer");
RETURN_ERROR_IF(oMatchEnd > oend, 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");
RETURN_ERROR_IF(iLitEnd > litLimit, corruption_detected, "try to read beyond literal buffer"); assert(op < op + sequenceLength);
assert(oLitEnd < op + sequenceLength);
/* copy literals */ /* copy literals */
ZSTD_safecopy(op, oend_w, *litPtr, sequence.litLength, ZSTD_no_overlap); 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; BYTE* const oLitEnd = op + sequence.litLength;
size_t const sequenceLength = sequence.litLength + sequence.matchLength; size_t const sequenceLength = sequence.litLength + sequence.matchLength;
BYTE* const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ 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* const iLitEnd = *litPtr + sequence.litLength;
const BYTE* match = oLitEnd - sequence.offset; const BYTE* match = oLitEnd - sequence.offset;
/* Errors and uncommon cases handled here. */ assert(op != NULL /* Precondition */);
assert(oLitEnd < oMatchEnd); assert(oend_w < oend /* No underflow */);
if (UNLIKELY(iLitEnd > litLimit || oMatchEnd > oend_w)) /* 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); return ZSTD_execSequenceEnd(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd);
/* Assumptions (everything else goes into ZSTD_execSequenceEnd()) */ /* 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(iLitEnd <= litLimit /* Literal length is in bounds */);
assert(oLitEnd <= oend_w /* Can wildcopy literals */); assert(oLitEnd <= oend_w /* Can wildcopy literals */);
assert(oMatchEnd <= oend_w /* Can wildcopy matches */); 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.stateLL, &seqState.DStream, dctx->LLTptr);
ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
assert(dst != NULL);
ZSTD_STATIC_ASSERT( ZSTD_STATIC_ASSERT(
BIT_DStream_unfinished < BIT_DStream_completed && BIT_DStream_unfinished < BIT_DStream_completed &&
@ -1043,8 +1055,10 @@ ZSTD_decompressSequences_body( ZSTD_DCtx* dctx,
/* last literal segment */ /* last literal segment */
{ size_t const lastLLSize = litEnd - litPtr; { size_t const lastLLSize = litEnd - litPtr;
RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, ""); RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, "");
memcpy(op, litPtr, lastLLSize); if (op != NULL) {
op += lastLLSize; memcpy(op, litPtr, lastLLSize);
op += lastLLSize;
}
} }
return op-ostart; return op-ostart;
@ -1093,6 +1107,7 @@ ZSTD_decompressSequencesLong_body(
seqState.prefixStart = prefixStart; seqState.prefixStart = prefixStart;
seqState.pos = (size_t)(op-prefixStart); seqState.pos = (size_t)(op-prefixStart);
seqState.dictEnd = dictEnd; seqState.dictEnd = dictEnd;
assert(dst != NULL);
assert(iend >= ip); assert(iend >= ip);
RETURN_ERROR_IF( RETURN_ERROR_IF(
ERR_isError(BIT_initDStream(&seqState.DStream, ip, iend-ip)), ERR_isError(BIT_initDStream(&seqState.DStream, ip, iend-ip)),
@ -1134,8 +1149,10 @@ ZSTD_decompressSequencesLong_body(
/* last literal segment */ /* last literal segment */
{ size_t const lastLLSize = litEnd - litPtr; { size_t const lastLLSize = litEnd - litPtr;
RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, ""); RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, "");
memcpy(op, litPtr, lastLLSize); if (op != NULL) {
op += lastLLSize; memcpy(op, litPtr, lastLLSize);
op += lastLLSize;
}
} }
return op-ostart; return op-ostart;
@ -1255,7 +1272,6 @@ ZSTD_getLongOffsetsShare(const ZSTD_seqSymbol* offTable)
} }
#endif #endif
size_t size_t
ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx, ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx,
void* dst, size_t dstCapacity, void* dst, size_t dstCapacity,
@ -1297,6 +1313,8 @@ ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx,
ip += seqHSize; ip += seqHSize;
srcSize -= seqHSize; srcSize -= seqHSize;
RETURN_ERROR_IF(dst == NULL && nbSeq > 0, dstSize_tooSmall, "NULL not handled");
#if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \ #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \
!defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG) !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG)
if ( !usePrefetchDecoder if ( !usePrefetchDecoder

View File

@ -1078,7 +1078,7 @@ static size_t HUF_decompress_usingDTable( /* -3% slower when non static */
BYTE* const ostart = (BYTE*) dst; BYTE* const ostart = (BYTE*) dst;
BYTE* op = ostart; BYTE* op = ostart;
BYTE* const omax = op + maxDstSize; BYTE* const omax = op + maxDstSize;
BYTE* const olimit = omax-15; BYTE* const olimit = maxDstSize < 15 ? op : omax-15;
const void* ptr = DTable; const void* ptr = DTable;
const HUF_DElt* const dt = (const HUF_DElt*)(ptr)+1; 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) static size_t ZSTD_copyUncompressedBlock(void* dst, size_t maxDstSize, const void* src, size_t srcSize)
{ {
if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall); if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall);
memcpy(dst, src, srcSize); if (srcSize > 0) {
memcpy(dst, src, srcSize);
}
return srcSize; return srcSize;
} }
@ -1541,7 +1543,9 @@ static size_t ZSTDv01_decodeLiteralsBlock(void* ctx,
size_t rleSize = litbp.origSize; size_t rleSize = litbp.origSize;
if (rleSize>maxDstSize) return ERROR(dstSize_tooSmall); if (rleSize>maxDstSize) return ERROR(dstSize_tooSmall);
if (!srcSize) return ERROR(srcSize_wrong); if (!srcSize) return ERROR(srcSize_wrong);
memset(oend - rleSize, *ip, rleSize); if (rleSize > 0) {
memset(oend - rleSize, *ip, rleSize);
}
*litStart = oend - rleSize; *litStart = oend - rleSize;
*litSize = rleSize; *litSize = rleSize;
ip++; ip++;
@ -1901,8 +1905,10 @@ static size_t ZSTD_decompressSequences(
{ {
size_t lastLLSize = litEnd - litPtr; size_t lastLLSize = litEnd - litPtr;
if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall); if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
if (op != litPtr) memmove(op, litPtr, lastLLSize); if (lastLLSize > 0) {
op += lastLLSize; if (op != litPtr) memmove(op, litPtr, lastLLSize);
op += lastLLSize;
}
} }
} }

View 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) static size_t ZSTD_copyUncompressedBlock(void* dst, size_t maxDstSize, const void* src, size_t srcSize)
{ {
if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall); if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall);
memcpy(dst, src, srcSize); if (srcSize > 0) {
memcpy(dst, src, srcSize);
}
return srcSize; return srcSize;
} }
@ -3229,8 +3231,10 @@ static size_t ZSTD_decompressSequences(
size_t lastLLSize = litEnd - litPtr; size_t lastLLSize = litEnd - litPtr;
if (litPtr > litEnd) return ERROR(corruption_detected); if (litPtr > litEnd) return ERROR(corruption_detected);
if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall); if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
if (op != litPtr) memmove(op, litPtr, lastLLSize); if (lastLLSize > 0) {
op += lastLLSize; if (op != litPtr) memmove(op, litPtr, lastLLSize);
op += lastLLSize;
}
} }
} }

View 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) static size_t ZSTD_copyUncompressedBlock(void* dst, size_t maxDstSize, const void* src, size_t srcSize)
{ {
if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall); if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall);
memcpy(dst, src, srcSize); if (srcSize > 0) {
memcpy(dst, src, srcSize);
}
return srcSize; return srcSize;
} }
@ -2870,8 +2872,10 @@ static size_t ZSTD_decompressSequences(
size_t lastLLSize = litEnd - litPtr; size_t lastLLSize = litEnd - litPtr;
if (litPtr > litEnd) return ERROR(corruption_detected); if (litPtr > litEnd) return ERROR(corruption_detected);
if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall); if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
if (op != litPtr) memmove(op, litPtr, lastLLSize); if (lastLLSize > 0) {
op += lastLLSize; if (op != litPtr) memmove(op, litPtr, lastLLSize);
op += lastLLSize;
}
} }
} }

View 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) static size_t ZSTD_copyRawBlock(void* dst, size_t maxDstSize, const void* src, size_t srcSize)
{ {
if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall); if (srcSize > maxDstSize) return ERROR(dstSize_tooSmall);
memcpy(dst, src, srcSize); if (srcSize > 0) {
memcpy(dst, src, srcSize);
}
return srcSize; return srcSize;
} }
@ -3008,8 +3010,10 @@ static size_t ZSTD_decompressSequences(
size_t lastLLSize = litEnd - litPtr; size_t lastLLSize = litEnd - litPtr;
if (litPtr > litEnd) return ERROR(corruption_detected); if (litPtr > litEnd) return ERROR(corruption_detected);
if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall); if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
if (op != litPtr) memcpy(op, litPtr, lastLLSize); if (lastLLSize > 0) {
op += lastLLSize; if (op != litPtr) memcpy(op, litPtr, lastLLSize);
op += lastLLSize;
}
} }
} }

View File

@ -3362,8 +3362,10 @@ static size_t ZSTDv05_decompressSequences(
size_t lastLLSize = litEnd - litPtr; size_t lastLLSize = litEnd - litPtr;
if (litPtr > litEnd) return ERROR(corruption_detected); /* too many literals already used */ if (litPtr > litEnd) return ERROR(corruption_detected); /* too many literals already used */
if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall); if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
memcpy(op, litPtr, lastLLSize); if (lastLLSize > 0) {
op += lastLLSize; memcpy(op, litPtr, lastLLSize);
op += lastLLSize;
}
} }
return op-ostart; return op-ostart;

View File

@ -3501,8 +3501,10 @@ static size_t ZSTDv06_decompressSequences(
{ size_t const lastLLSize = litEnd - litPtr; { size_t const lastLLSize = litEnd - litPtr;
if (litPtr > litEnd) return ERROR(corruption_detected); /* too many literals already used */ if (litPtr > litEnd) return ERROR(corruption_detected); /* too many literals already used */
if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall); if (op+lastLLSize > oend) return ERROR(dstSize_tooSmall);
memcpy(op, litPtr, lastLLSize); if (lastLLSize > 0) {
op += lastLLSize; memcpy(op, litPtr, lastLLSize);
op += lastLLSize;
}
} }
return op-ostart; return op-ostart;

View 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) static size_t ZSTDv07_copyRawBlock(void* dst, size_t dstCapacity, const void* src, size_t srcSize)
{ {
if (srcSize > dstCapacity) return ERROR(dstSize_tooSmall); if (srcSize > dstCapacity) return ERROR(dstSize_tooSmall);
memcpy(dst, src, srcSize); if (srcSize > 0) {
memcpy(dst, src, srcSize);
}
return srcSize; return srcSize;
} }
@ -3714,8 +3716,10 @@ static size_t ZSTDv07_decompressSequences(
{ size_t const lastLLSize = litEnd - litPtr; { size_t const lastLLSize = litEnd - litPtr;
/* if (litPtr > litEnd) return ERROR(corruption_detected); */ /* too many literals already used */ /* if (litPtr > litEnd) return ERROR(corruption_detected); */ /* too many literals already used */
if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall); if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall);
memcpy(op, litPtr, lastLLSize); if (lastLLSize > 0) {
op += lastLLSize; memcpy(op, litPtr, lastLLSize);
op += lastLLSize;
}
} }
return op-ostart; 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) static size_t ZSTDv07_generateNxBytes(void* dst, size_t dstCapacity, BYTE byte, size_t length)
{ {
if (length > dstCapacity) return ERROR(dstSize_tooSmall); if (length > dstCapacity) return ERROR(dstSize_tooSmall);
memset(dst, byte, length); if (length > 0) {
memset(dst, byte, length);
}
return length; return length;
} }

View File

@ -42,7 +42,7 @@ FUZZ_ARFLAGS := $(ARFLAGS)
FUZZ_TARGET_FLAGS = $(FUZZ_CPPFLAGS) $(FUZZ_CXXFLAGS) $(FUZZ_LDFLAGS) FUZZ_TARGET_FLAGS = $(FUZZ_CPPFLAGS) $(FUZZ_CXXFLAGS) $(FUZZ_LDFLAGS)
FUZZ_HEADERS := fuzz_helpers.h fuzz.h zstd_helpers.h fuzz_data_producer.h 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 ZSTDCOMMON_SRC := $(ZSTDDIR)/common/*.c
ZSTDCOMP_SRC := $(ZSTDDIR)/compress/*.c ZSTDCOMP_SRC := $(ZSTDDIR)/compress/*.c

View File

@ -32,9 +32,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
/* Allocate all buffers and contexts if not already allocated */ /* Allocate all buffers and contexts if not already allocated */
if (neededBufSize > bufSize) { if (neededBufSize > bufSize) {
free(rBuf); free(rBuf);
rBuf = malloc(neededBufSize); rBuf = FUZZ_malloc(neededBufSize);
bufSize = neededBufSize; bufSize = neededBufSize;
FUZZ_ASSERT(rBuf);
} }
if (!dctx) { if (!dctx) {
dctx = ZSTD_createDCtx(); dctx = ZSTD_createDCtx();

View File

@ -43,7 +43,9 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
FUZZ_ZASSERT(ret); FUZZ_ZASSERT(ret);
if (ret == 0) { if (ret == 0) {
FUZZ_ASSERT(resultCapacity >= srcSize); FUZZ_ASSERT(resultCapacity >= srcSize);
memcpy(result, src, srcSize); if (srcSize > 0) {
memcpy(result, src, srcSize);
}
return srcSize; return srcSize;
} }
ZSTD_decompressBegin(dctx); ZSTD_decompressBegin(dctx);
@ -67,10 +69,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
if (neededBufSize > bufSize || !cBuf || !rBuf) { if (neededBufSize > bufSize || !cBuf || !rBuf) {
free(cBuf); free(cBuf);
free(rBuf); free(rBuf);
cBuf = malloc(neededBufSize); cBuf = FUZZ_malloc(neededBufSize);
rBuf = malloc(neededBufSize); rBuf = FUZZ_malloc(neededBufSize);
bufSize = neededBufSize; bufSize = neededBufSize;
FUZZ_ASSERT(cBuf && rBuf);
} }
if (!cctx) { if (!cctx) {
cctx = ZSTD_createCCtx(); cctx = ZSTD_createCCtx();
@ -87,7 +88,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
cLevel); cLevel);
FUZZ_ZASSERT(result); FUZZ_ZASSERT(result);
FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size"); 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); FUZZ_dataProducer_free(producer);
#ifndef STATEFUL_FUZZING #ifndef STATEFUL_FUZZING

View 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); size_t const bufSize = FUZZ_dataProducer_uint32Range(producer, 0, 10 * size);
void* rBuf = malloc(bufSize); void* rBuf = FUZZ_malloc(bufSize);
FUZZ_ASSERT(rBuf);
if (ddict) { if (ddict) {
ZSTD_decompress_usingDDict(dctx, rBuf, bufSize, src, size, ddict); ZSTD_decompress_usingDDict(dctx, rBuf, bufSize, src, size, ddict);
} else { } else {

View File

@ -79,11 +79,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
DEBUGLOG(2, "Dict content type %d", dct); DEBUGLOG(2, "Dict content type %d", dct);
DEBUGLOG(2, "Dict size %u", (unsigned)size); DEBUGLOG(2, "Dict size %u", (unsigned)size);
void* const rBuf = malloc(size); void* const rBuf = FUZZ_malloc(size);
FUZZ_ASSERT(rBuf);
size_t const cBufSize = ZSTD_compressBound(size); size_t const cBufSize = ZSTD_compressBound(size);
void* const cBuf = malloc(cBufSize); void* const cBuf = FUZZ_malloc(cBufSize);
FUZZ_ASSERT(cBuf);
size_t const cSize = size_t const cSize =
compress(cBuf, cBufSize, src, size, src, size, dlm, dct, refPrefix); 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 = size_t const rSize =
decompress(rBuf, size, cBuf, cSize, src, size, dlm, dct, refPrefix); decompress(rBuf, size, cBuf, cSize, src, size, dlm, dct, refPrefix);
FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size"); 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: out:
free(cBuf); free(cBuf);

View File

@ -84,7 +84,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
size = FUZZ_dataProducer_reserveDataPrefix(producer); size = FUZZ_dataProducer_reserveDataPrefix(producer);
size_t const rBufSize = size; size_t const rBufSize = size;
void* rBuf = malloc(rBufSize); void* rBuf = FUZZ_malloc(rBufSize);
size_t cBufSize = ZSTD_compressBound(size) * 2; size_t cBufSize = ZSTD_compressBound(size) * 2;
void *cBuf; void *cBuf;
/* Half of the time fuzz with a 1 byte smaller output size. /* 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. * giving us 4 bytes of overhead.
*/ */
cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1); cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1);
cBuf = malloc(cBufSize); cBuf = FUZZ_malloc(cBufSize);
if (!cctx) { if (!cctx) {
cctx = ZSTD_createCCtx(); cctx = ZSTD_createCCtx();
@ -108,7 +108,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer); roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer);
FUZZ_ZASSERT(result); FUZZ_ZASSERT(result);
FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size"); 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(rBuf);
free(cBuf); free(cBuf);

View 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 *FUZZ_dataProducer_create(const uint8_t *data, size_t size) {
FUZZ_dataProducer_t *producer = malloc(sizeof(FUZZ_dataProducer_t)); FUZZ_dataProducer_t *producer = FUZZ_malloc(sizeof(FUZZ_dataProducer_t));
FUZZ_ASSERT(producer != NULL);
producer->data = data; producer->data = data;
producer->size = size; producer->size = size;

32
tests/fuzz/fuzz_helpers.c Normal file
View File

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

View File

@ -56,6 +56,17 @@ extern "C" {
#define FUZZ_STATIC static #define FUZZ_STATIC static
#endif #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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -41,8 +41,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
FUZZ_ASSERT(cctx); FUZZ_ASSERT(cctx);
} }
void *rBuf = malloc(bufSize); void *rBuf = FUZZ_malloc(bufSize);
FUZZ_ASSERT(rBuf);
ZSTD_compressCCtx(cctx, rBuf, bufSize, src, size, cLevel); ZSTD_compressCCtx(cctx, rBuf, bufSize, src, size, cLevel);
free(rBuf); free(rBuf);
FUZZ_dataProducer_free(producer); FUZZ_dataProducer_free(producer);

View 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); size_t const bufSize = FUZZ_dataProducer_uint32Range(producer, 0, 10 * size);
void *rBuf = malloc(bufSize); void *rBuf = FUZZ_malloc(bufSize);
FUZZ_ASSERT(rBuf);
ZSTD_decompressDCtx(dctx, rBuf, bufSize, src, size); ZSTD_decompressDCtx(dctx, rBuf, bufSize, src, size);
free(rBuf); free(rBuf);

View File

@ -70,7 +70,7 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
{ {
size_t const rBufSize = size; size_t const rBufSize = size;
void* rBuf = malloc(rBufSize); void* rBuf = FUZZ_malloc(rBufSize);
size_t cBufSize = ZSTD_compressBound(size); size_t cBufSize = ZSTD_compressBound(size);
void* cBuf; void* cBuf;
@ -85,9 +85,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
*/ */
cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1); cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1);
cBuf = malloc(cBufSize); cBuf = FUZZ_malloc(cBufSize);
FUZZ_ASSERT(cBuf && rBuf);
if (!cctx) { if (!cctx) {
cctx = ZSTD_createCCtx(); cctx = ZSTD_createCCtx();
@ -103,7 +101,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer); roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer);
FUZZ_ZASSERT(result); FUZZ_ZASSERT(result);
FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size"); 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(rBuf);
free(cBuf); free(cBuf);

View File

@ -76,9 +76,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
/* Allocate all buffers and contexts if not already allocated */ /* Allocate all buffers and contexts if not already allocated */
if (!buf) { if (!buf) {
buf = malloc(kBufSize); buf = FUZZ_malloc(kBufSize);
FUZZ_ASSERT(buf); }
}
if (!dstream) { if (!dstream) {
dstream = ZSTD_createDStream(); 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); ZSTD_inBuffer in = makeInBuffer(&src, &size, producer, prevInWasZero ? 1 : 0);
prevInWasZero = in.size == 0; prevInWasZero = in.size == 0;
while (in.pos != in.size) { 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); out = makeOutBuffer(producer, prevOutWasZero ? 1 : 0);
} }
prevOutWasZero = out.size == 0; prevOutWasZero = out.size == 0;

View File

@ -146,10 +146,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
if (neededBufSize > bufSize) { if (neededBufSize > bufSize) {
free(cBuf); free(cBuf);
free(rBuf); free(rBuf);
cBuf = (uint8_t*)malloc(neededBufSize); cBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
rBuf = (uint8_t*)malloc(neededBufSize); rBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
bufSize = neededBufSize; bufSize = neededBufSize;
FUZZ_ASSERT(cBuf && rBuf);
} }
if (!cctx) { if (!cctx) {
cctx = ZSTD_createCCtx(); cctx = ZSTD_createCCtx();
@ -166,7 +165,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
ZSTD_decompressDCtx(dctx, rBuf, neededBufSize, cBuf, cSize); ZSTD_decompressDCtx(dctx, rBuf, neededBufSize, cBuf, cSize);
FUZZ_ZASSERT(rSize); FUZZ_ZASSERT(rSize);
FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size"); 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); FUZZ_dataProducer_free(producer);

View File

@ -22,6 +22,9 @@
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
{ {
ZSTD_frameHeader zfh; ZSTD_frameHeader zfh;
if (size == 0) {
src = NULL;
}
/* You can fuzz any helper functions here that are fast, and take zstd /* 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, * compressed data as input. E.g. don't expect the input to be a dictionary,
* so don't fuzz ZSTD_getDictID_fromDict(). * so don't fuzz ZSTD_getDictID_fromDict().

View 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 dictSize = MAX(srcSize / 8, 1024);
size_t const totalSampleSize = dictSize * 11; size_t const totalSampleSize = dictSize * 11;
FUZZ_dict_t dict = { malloc(dictSize), dictSize }; FUZZ_dict_t dict = { FUZZ_malloc(dictSize), dictSize };
char* const samples = (char*)malloc(totalSampleSize); char* const samples = (char*)FUZZ_malloc(totalSampleSize);
unsigned nbSamples = 100; 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 pos = 0;
size_t sample = 0; size_t sample = 0;
ZDICT_fastCover_params_t params; ZDICT_fastCover_params_t params;
FUZZ_ASSERT(dict.buff && samples && samplesSizes);
for (sample = 0; sample < nbSamples; ++sample) { for (sample = 0; sample < nbSamples; ++sample) {
size_t const remaining = totalSampleSize - pos; 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); memcpy(samples + pos, src + offset, toCopy);
pos += toCopy; pos += toCopy;
samplesSizes[sample] = toCopy; samplesSizes[sample] = toCopy;
} }
memset(samples + pos, 0, totalSampleSize - pos); memset(samples + pos, 0, totalSampleSize - pos);

View File

@ -538,6 +538,12 @@ static int basicUnitTests(U32 const seed, double compressibility)
if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; } if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; }
DISPLAYLEVEL(3, "OK \n"); 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++); DISPLAYLEVEL(3, "test%3i : ZSTD_decompressBound test with content size missing : ", testNb++);
{ /* create compressed buffer with content size missing */ { /* create compressed buffer with content size missing */
ZSTD_CCtx* const cctx = ZSTD_createCCtx(); ZSTD_CCtx* const cctx = ZSTD_createCCtx();