diff --git a/NEWS b/NEWS index 2667ba43..e9f5b78d 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ fix : `LZ4_compress_fast_continue()` produced wrong data when a block overwrote fix : `LZ4_compress_destSize_extState()` corrupted data with `acceleration <= 0`, and could read out of bounds (since v1.10.0) fix : negative sizes crashed `LZ4_decompress_safe_partial()` (since v1.9.3) and `LZ4_decompress_fast()` (since v1.9.4) fix : `LZ4_saveDict()` with a negative `dictSize` copied up to 64 KB into `safeBuffer`, by @arthuz-th +fix : `LZ4_saveDictHC()` before any compression (new or reset stream) corrupted the next compressed block fix : `LZ4_decompress_safe_partial()` could leave previous `dst` content in decoded data with an invalid offset 0 (since v1.8.3) fix : heap overflow with an invalid `blockSizeID` in `LZ4F_compressBegin()` and `LZ4F_writeOpen()`, by @jaypatrickhoward fix : `LZ4F_compressBegin()` failing on allocation left an inconsistent frame in progress : it is now invalidated diff --git a/lib/lz4hc.c b/lib/lz4hc.c index 6fd6e7e4..a98bb881 100644 --- a/lib/lz4hc.c +++ b/lib/lz4hc.c @@ -1793,6 +1793,9 @@ int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictS LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse; int const prefixSize = (int)(streamPtr->end - streamPtr->prefixStart); DEBUGLOG(5, "LZ4_saveDictHC(%p, %p, %d)", LZ4_streamHCPtr, safeBuffer, dictSize); + /* nothing compressed yet (new or reset stream) : nothing to save, + * the stream remains to be initialized by next compression (attached dictionary included) */ + if (streamPtr->prefixStart == NULL) return 0; assert(prefixSize >= 0); if (dictSize > 64 KB) dictSize = 64 KB; if (dictSize < 4) dictSize = 0; diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 1daedd3e..fafc099d 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -1747,6 +1747,34 @@ static void FUZ_unitTests(int compressionLevel) { char tmp_buffer[240] = { 0 }; FUZ_CHECKTEST( LZ4_saveDictHC(ctx, tmp_buffer, sizeof(tmp_buffer)) != 0, "LZ4_saveDictHC() can't save anything since compression hasn't started"); + } + + /* next block is compressed as the first one of the stream, + * without (attach==0) or with (attach==1) an attached dictionary */ + { LZ4_streamHC_t* const dictHC = LZ4_createStreamHC(); + int const levels[] = { LZ4HC_CLEVEL_DEFAULT, 3, LZ4HC_CLEVEL_OPT_MIN, LZ4HC_CLEVEL_MAX }; /* first : level of LZ4_initStreamHC() */ + int attach, l; + FUZ_CHECKTEST(dictHC == NULL, "LZ4_createStreamHC() allocation failed"); + for (l = 0; l < (int)(sizeof(levels) / sizeof(levels[0])); l++) { + for (attach = 0; attach <= 1; attach++) { + char saveBuffer[240]; + int cSize, dSize; + if (l || attach) LZ4_resetStreamHC_fast(ctx, levels[l]); /* first test : right after init */ + if (attach) { + LZ4_resetStreamHC_fast(dictHC, levels[l]); + FUZ_CHECKTEST(LZ4_loadDictHC(dictHC, testInput, 64 KB) != 64 KB, "LZ4_loadDictHC() failed"); + LZ4_attach_HC_dictionary(ctx, dictHC); + } + FUZ_CHECKTEST( LZ4_saveDictHC(ctx, saveBuffer, sizeof(saveBuffer)) != 0, + "LZ4_saveDictHC() can't save anything since compression hasn't started"); + cSize = LZ4_compress_HC_continue(ctx, testInput + 64 KB, testCompressed, 64 KB, testCompressedSize); + FUZ_CHECKTEST(cSize <= 0, "LZ4_compress_HC_continue() failed"); + dSize = LZ4_decompress_safe_usingDict(testCompressed, testVerify, cSize, 64 KB, + testInput, attach ? 64 KB : 0); + FUZ_CHECKTEST(dSize != 64 KB || memcmp(testVerify, testInput + 64 KB, 64 KB), + "level %i, dictionary %i : block compressed after LZ4_saveDictHC() is corrupted", levels[l], attach); + } } + LZ4_freeStreamHC(dictHC); } } DISPLAYLEVEL(3, "OK \n");