Guard against invalid sequences from external matchfinders (#3465)
This commit is contained in:
@@ -108,6 +108,29 @@ size_t zstreamExternalMatchFinder(
|
||||
compressionLevel,
|
||||
windowSize
|
||||
);
|
||||
case EMF_INVALID_OFFSET:
|
||||
outSeqs[0].offset = 1 << 20;
|
||||
outSeqs[0].matchLength = 4;
|
||||
outSeqs[0].litLength = (U32)(srcSize - 4);
|
||||
return 1;
|
||||
case EMF_INVALID_MATCHLEN:
|
||||
outSeqs[0].offset = 1;
|
||||
outSeqs[0].matchLength = (U32)(srcSize);
|
||||
outSeqs[0].litLength = 1;
|
||||
return 1;
|
||||
case EMF_INVALID_LITLEN:
|
||||
outSeqs[0].offset = 0;
|
||||
outSeqs[0].matchLength = 0;
|
||||
outSeqs[0].litLength = (U32)(srcSize + 1);
|
||||
return 1;
|
||||
case EMF_INVALID_LAST_LITS:
|
||||
outSeqs[0].offset = 1;
|
||||
outSeqs[0].matchLength = 1;
|
||||
outSeqs[0].litLength = 1;
|
||||
outSeqs[1].offset = 0;
|
||||
outSeqs[1].matchLength = 0;
|
||||
outSeqs[1].litLength = (U32)(srcSize - 1);
|
||||
return 2;
|
||||
case EMF_SMALL_ERROR:
|
||||
return outSeqsCapacity + 1;
|
||||
case EMF_BIG_ERROR:
|
||||
|
||||
@@ -20,7 +20,11 @@ typedef enum {
|
||||
EMF_ONE_BIG_SEQ = 1,
|
||||
EMF_LOTS_OF_SEQS = 2,
|
||||
EMF_BIG_ERROR = 3,
|
||||
EMF_SMALL_ERROR = 4
|
||||
EMF_SMALL_ERROR = 4,
|
||||
EMF_INVALID_OFFSET = 5,
|
||||
EMF_INVALID_MATCHLEN = 6,
|
||||
EMF_INVALID_LITLEN = 7,
|
||||
EMF_INVALID_LAST_LITS = 8
|
||||
} EMF_testCase;
|
||||
|
||||
size_t zstreamExternalMatchFinder(
|
||||
|
||||
@@ -128,6 +128,7 @@ void FUZZ_setRandomParameters(ZSTD_CCtx *cctx, size_t srcSize, FUZZ_dataProducer
|
||||
setRand(cctx, ZSTD_c_deterministicRefPrefix, 0, 1, producer);
|
||||
setRand(cctx, ZSTD_c_prefetchCDictTables, 0, 2, producer);
|
||||
setRand(cctx, ZSTD_c_maxBlockSize, ZSTD_BLOCKSIZE_MAX_MIN, ZSTD_BLOCKSIZE_MAX, producer);
|
||||
setRand(cctx, ZSTD_c_validateSequences, 0, 1, producer);
|
||||
if (FUZZ_dataProducer_uint32Range(producer, 0, 1) == 0) {
|
||||
setRand(cctx, ZSTD_c_srcSizeHint, ZSTD_SRCSIZEHINT_MIN, 2 * srcSize, producer);
|
||||
}
|
||||
|
||||
+36
-23
@@ -1873,48 +1873,61 @@ static int basicUnitTests(U32 seed, double compressibility, int bigTests)
|
||||
* check that the reference is preserved across compressions */
|
||||
ZSTD_registerExternalMatchFinder(zc, &externalMatchState, zstreamExternalMatchFinder);
|
||||
|
||||
for (enableFallback = 0; enableFallback < 1; enableFallback++) {
|
||||
for (enableFallback = 0; enableFallback <= 1; enableFallback++) {
|
||||
size_t testCaseId;
|
||||
size_t const numTestCases = 9;
|
||||
|
||||
EMF_testCase const EMF_successCases[] = {
|
||||
EMF_testCase const testCases[] = {
|
||||
EMF_ONE_BIG_SEQ,
|
||||
EMF_LOTS_OF_SEQS,
|
||||
};
|
||||
size_t const EMF_numSuccessCases = 2;
|
||||
|
||||
EMF_testCase const EMF_failureCases[] = {
|
||||
EMF_ZERO_SEQS,
|
||||
EMF_BIG_ERROR,
|
||||
EMF_SMALL_ERROR,
|
||||
EMF_INVALID_OFFSET,
|
||||
EMF_INVALID_MATCHLEN,
|
||||
EMF_INVALID_LITLEN,
|
||||
EMF_INVALID_LAST_LITS
|
||||
};
|
||||
size_t const EMF_numFailureCases = 3;
|
||||
|
||||
/* Test external matchfinder success scenarios */
|
||||
for (testCaseId = 0; testCaseId < EMF_numSuccessCases; testCaseId++) {
|
||||
ZSTD_ErrorCode const errorCodes[] = {
|
||||
ZSTD_error_no_error,
|
||||
ZSTD_error_no_error,
|
||||
ZSTD_error_externalMatchFinder_failed,
|
||||
ZSTD_error_externalMatchFinder_failed,
|
||||
ZSTD_error_externalMatchFinder_failed,
|
||||
ZSTD_error_externalSequences_invalid,
|
||||
ZSTD_error_externalSequences_invalid,
|
||||
ZSTD_error_externalSequences_invalid,
|
||||
ZSTD_error_externalSequences_invalid
|
||||
};
|
||||
|
||||
for (testCaseId = 0; testCaseId < numTestCases; testCaseId++) {
|
||||
size_t res;
|
||||
externalMatchState = EMF_successCases[testCaseId];
|
||||
|
||||
int const compressionShouldSucceed = (
|
||||
(errorCodes[testCaseId] == ZSTD_error_no_error) ||
|
||||
(enableFallback && errorCodes[testCaseId] == ZSTD_error_externalMatchFinder_failed)
|
||||
);
|
||||
|
||||
int const testWithSequenceValidation = (
|
||||
testCases[testCaseId] == EMF_INVALID_OFFSET
|
||||
);
|
||||
|
||||
externalMatchState = testCases[testCaseId];
|
||||
|
||||
ZSTD_CCtx_reset(zc, ZSTD_reset_session_only);
|
||||
CHECK_Z(ZSTD_CCtx_setParameter(zc, ZSTD_c_validateSequences, testWithSequenceValidation));
|
||||
CHECK_Z(ZSTD_CCtx_setParameter(zc, ZSTD_c_enableMatchFinderFallback, enableFallback));
|
||||
res = ZSTD_compress2(zc, dstBuf, dstBufSize, CNBuffer, CNBufferSize);
|
||||
CHECK(ZSTD_isError(res), "EMF: Compression error: %s", ZSTD_getErrorName(res));
|
||||
CHECK_Z(ZSTD_decompress(checkBuf, checkBufSize, dstBuf, res));
|
||||
CHECK(memcmp(CNBuffer, checkBuf, CNBufferSize) != 0, "EMF: Corruption!");
|
||||
}
|
||||
|
||||
/* Test external matchfinder failure scenarios */
|
||||
for (testCaseId = 0; testCaseId < EMF_numFailureCases; testCaseId++) {
|
||||
size_t res;
|
||||
externalMatchState = EMF_failureCases[testCaseId];
|
||||
ZSTD_CCtx_reset(zc, ZSTD_reset_session_only);
|
||||
CHECK_Z(ZSTD_CCtx_setParameter(zc, ZSTD_c_enableMatchFinderFallback, enableFallback));
|
||||
res = ZSTD_compress2(zc, dstBuf, dstBufSize, CNBuffer, CNBufferSize);
|
||||
if (enableFallback) {
|
||||
if (compressionShouldSucceed) {
|
||||
CHECK(ZSTD_isError(res), "EMF: Compression error: %s", ZSTD_getErrorName(res));
|
||||
CHECK_Z(ZSTD_decompress(checkBuf, checkBufSize, dstBuf, res));
|
||||
CHECK(memcmp(CNBuffer, checkBuf, CNBufferSize) != 0, "EMF: Corruption!");
|
||||
} else {
|
||||
CHECK(!ZSTD_isError(res), "EMF: Should have raised an error!");
|
||||
CHECK(
|
||||
ZSTD_getErrorCode(res) != ZSTD_error_externalMatchFinder_failed,
|
||||
ZSTD_getErrorCode(res) != errorCodes[testCaseId],
|
||||
"EMF: Wrong error code: %s", ZSTD_getErrorName(res)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user