From e60cbb5cacaa43eda7a3df7c2d9051a9437e7046 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 10 May 2017 16:28:36 -0700 Subject: [PATCH] added test for LZ4F_resetDecompressionContext() --- doc/lz4frame_manual.html | 9 +++---- lib/lz4frame.c | 66 ++++++++++++++++++++++++++++++++---------------- lib/lz4frame.h | 9 +++---- lib/lz4frame_static.h | 9 ------- tests/frametest.c | 45 +++++++++++++++++++++------------ 5 files changed, 79 insertions(+), 59 deletions(-) diff --git a/doc/lz4frame_manual.html b/doc/lz4frame_manual.html index fa2c0b9..87750a1 100644 --- a/doc/lz4frame_manual.html +++ b/doc/lz4frame_manual.html @@ -255,14 +255,11 @@ LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx);


-
LZ4F_errorCode_t LZ4F_resetDecompressionContext(LZ4F_dctx* dctx);
-

When decompression ends successfully, - it's possible to start a new decompression immediately - re-using the same context. - However, in case of an error, the context is left in "undefined" state. +

void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx);   /* always successful */
+

In case of an error, the context is left in "undefined" state. In which case, it's necessary to reset it, before re-using it. This method can also be used to abruptly stop an unfinished decompression, - and start a new on the same context. + and start a new with the same context.


diff --git a/lib/lz4frame.c b/lib/lz4frame.c index 7422638..fb37789 100644 --- a/lib/lz4frame.c +++ b/lib/lz4frame.c @@ -798,10 +798,9 @@ typedef enum { dstage_skipSkippable } dStage_t; -LZ4F_errorCode_t LZ4F_resetDecompressionContext(LZ4F_dctx* dctx) +void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx) { dctx->dStage = dstage_getHeader; - return 0; } @@ -939,7 +938,8 @@ LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctxPtr, LZ4F_frameInfo_t* frameIn size_t o=0, i=0; *srcSizePtr = 0; *frameInfoPtr = dctxPtr->frameInfo; - return LZ4F_decompress(dctxPtr, NULL, &o, NULL, &i, NULL); /* returns : recommended nb of bytes for LZ4F_decompress() */ + /* returns : recommended nb of bytes for LZ4F_decompress() */ + return LZ4F_decompress(dctxPtr, NULL, &o, NULL, &i, NULL); } else { if (dctxPtr->dStage == dstage_storeHeader) { /* frame decoding already started, in the middle of header => automatic fail */ @@ -949,7 +949,10 @@ LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctxPtr, LZ4F_frameInfo_t* frameIn size_t decodeResult; size_t const hSize = LZ4F_headerSize(srcBuffer, *srcSizePtr); if (LZ4F_isError(hSize)) { *srcSizePtr=0; return hSize; } - if (*srcSizePtr < hSize) { *srcSizePtr=0; return err0r(LZ4F_ERROR_frameHeader_incomplete); } + if (*srcSizePtr < hSize) { + *srcSizePtr=0; + return err0r(LZ4F_ERROR_frameHeader_incomplete); + } decodeResult = LZ4F_decodeHeader(dctxPtr, srcBuffer, hSize); if (LZ4F_isError(decodeResult)) { @@ -965,10 +968,15 @@ LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctxPtr, LZ4F_frameInfo_t* frameIn /* trivial redirector, for common prototype */ -static int LZ4F_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize) +static int LZ4F_decompress_safe (const char* src, + char* dst, + int compressedSize, + int dstCapacity, + const char* dictStart, + int dictSize) { (void)dictStart; (void)dictSize; - return LZ4_decompress_safe (source, dest, compressedSize, maxDecompressedSize); + return LZ4_decompress_safe (src, dst, compressedSize, dstCapacity); } @@ -1101,7 +1109,7 @@ size_t LZ4F_decompress(LZ4F_dctx* dctxPtr, doAnotherStage = 0; /* not enough src data, ask for some more */ break; } - { LZ4F_errorCode_t const hSize = LZ4F_decodeHeader(dctxPtr, dctxPtr->header, dctxPtr->tmpInTarget); + { LZ4F_errorCode_t const hSize = LZ4F_decodeHeader(dctxPtr, dctxPtr->header, dctxPtr->tmpInTarget); /* will change dStage appropriately */ if (LZ4F_isError(hSize)) return hSize; } break; @@ -1149,7 +1157,7 @@ size_t LZ4F_decompress(LZ4F_dctx* dctxPtr, memcpy(dctxPtr->tmpIn + dctxPtr->tmpInSize, srcPtr, sizeToCopy); srcPtr += sizeToCopy; dctxPtr->tmpInSize += sizeToCopy; - if (dctxPtr->tmpInSize < BHSize) { /* not enough input to get full cBlockSize; wait for more */ + if (dctxPtr->tmpInSize < BHSize) { /* not enough input for cBlockSize */ nextSrcSizeHint = BHSize - dctxPtr->tmpInSize; doAnotherStage = 0; break; @@ -1157,13 +1165,14 @@ size_t LZ4F_decompress(LZ4F_dctx* dctxPtr, selectedIn = dctxPtr->tmpIn; } - /* case dstage_decodeCBlockSize: */ /* no more direct access, to prevent scan-build warning */ + /* case dstage_decodeCBlockSize: */ /* no more direct access, to remove scan-build warning */ { size_t const nextCBlockSize = LZ4F_readLE32(selectedIn) & 0x7FFFFFFFU; if (nextCBlockSize==0) { /* frameEnd signal, no more CBlock */ dctxPtr->dStage = dstage_getSuffix; break; } - if (nextCBlockSize > dctxPtr->maxBlockSize) return err0r(LZ4F_ERROR_GENERIC); /* invalid cBlockSize */ + if (nextCBlockSize > dctxPtr->maxBlockSize) + return err0r(LZ4F_ERROR_maxBlockSize_invalid); dctxPtr->tmpInTarget = nextCBlockSize; if (LZ4F_readLE32(selectedIn) & LZ4F_BLOCKUNCOMPRESSED_FLAG) { dctxPtr->dStage = dstage_copyDirect; @@ -1179,11 +1188,13 @@ size_t LZ4F_decompress(LZ4F_dctx* dctxPtr, case dstage_copyDirect: /* uncompressed block */ { size_t sizeToCopy = dctxPtr->tmpInTarget; - if ((size_t)(srcEnd-srcPtr) < sizeToCopy) sizeToCopy = srcEnd - srcPtr; /* not enough input to read full block */ + if ((size_t)(srcEnd-srcPtr) < sizeToCopy) sizeToCopy = srcEnd - srcPtr; if ((size_t)(dstEnd-dstPtr) < sizeToCopy) sizeToCopy = dstEnd - dstPtr; memcpy(dstPtr, srcPtr, sizeToCopy); - if (dctxPtr->frameInfo.contentChecksumFlag) XXH32_update(&(dctxPtr->xxh), srcPtr, sizeToCopy); - if (dctxPtr->frameInfo.contentSize) dctxPtr->frameRemainingSize -= sizeToCopy; + if (dctxPtr->frameInfo.contentChecksumFlag) + XXH32_update(&(dctxPtr->xxh), srcPtr, sizeToCopy); + if (dctxPtr->frameInfo.contentSize) + dctxPtr->frameRemainingSize -= sizeToCopy; /* dictionary management */ if (dctxPtr->frameInfo.blockMode==LZ4F_blockLinked) @@ -1244,10 +1255,14 @@ size_t LZ4F_decompress(LZ4F_dctx* dctxPtr, else decoder = LZ4F_decompress_safe; - decodedSize = decoder((const char*)selectedIn, (char*)dstPtr, (int)dctxPtr->tmpInTarget, (int)dctxPtr->maxBlockSize, (const char*)dctxPtr->dict, (int)dctxPtr->dictSize); + decodedSize = decoder((const char*)selectedIn, (char*)dstPtr, + (int)dctxPtr->tmpInTarget, (int)dctxPtr->maxBlockSize, + (const char*)dctxPtr->dict, (int)dctxPtr->dictSize); if (decodedSize < 0) return err0r(LZ4F_ERROR_GENERIC); /* decompression failed */ - if (dctxPtr->frameInfo.contentChecksumFlag) XXH32_update(&(dctxPtr->xxh), dstPtr, decodedSize); - if (dctxPtr->frameInfo.contentSize) dctxPtr->frameRemainingSize -= decodedSize; + if (dctxPtr->frameInfo.contentChecksumFlag) + XXH32_update(&(dctxPtr->xxh), dstPtr, decodedSize); + if (dctxPtr->frameInfo.contentSize) + dctxPtr->frameRemainingSize -= decodedSize; /* dictionary management */ if (dctxPtr->frameInfo.blockMode==LZ4F_blockLinked) @@ -1284,10 +1299,15 @@ size_t LZ4F_decompress(LZ4F_dctx* dctxPtr, } /* Decode */ - decodedSize = decoder((const char*)selectedIn, (char*)dctxPtr->tmpOut, (int)dctxPtr->tmpInTarget, (int)dctxPtr->maxBlockSize, (const char*)dctxPtr->dict, (int)dctxPtr->dictSize); - if (decodedSize < 0) return err0r(LZ4F_ERROR_decompressionFailed); /* decompression failed */ - if (dctxPtr->frameInfo.contentChecksumFlag) XXH32_update(&(dctxPtr->xxh), dctxPtr->tmpOut, decodedSize); - if (dctxPtr->frameInfo.contentSize) dctxPtr->frameRemainingSize -= decodedSize; + decodedSize = decoder((const char*)selectedIn, (char*)dctxPtr->tmpOut, + (int)dctxPtr->tmpInTarget, (int)dctxPtr->maxBlockSize, + (const char*)dctxPtr->dict, (int)dctxPtr->dictSize); + if (decodedSize < 0) + return err0r(LZ4F_ERROR_decompressionFailed); /* decompression failed */ + if (dctxPtr->frameInfo.contentChecksumFlag) + XXH32_update(&(dctxPtr->xxh), dctxPtr->tmpOut, decodedSize); + if (dctxPtr->frameInfo.contentSize) + dctxPtr->frameRemainingSize -= decodedSize; dctxPtr->tmpOutSize = decodedSize; dctxPtr->tmpOutStart = 0; dctxPtr->dStage = dstage_flushOut; @@ -1318,7 +1338,8 @@ size_t LZ4F_decompress(LZ4F_dctx* dctxPtr, case dstage_getSuffix: { size_t const suffixSize = dctxPtr->frameInfo.contentChecksumFlag * 4; - if (dctxPtr->frameRemainingSize) return err0r(LZ4F_ERROR_frameSize_wrong); /* incorrect frame size decoded */ + if (dctxPtr->frameRemainingSize) + return err0r(LZ4F_ERROR_frameSize_wrong); /* incorrect frame size decoded */ if (suffixSize == 0) { /* frame completed */ nextSrcSizeHint = 0; dctxPtr->dStage = dstage_getHeader; @@ -1379,7 +1400,8 @@ size_t LZ4F_decompress(LZ4F_dctx* dctxPtr, memcpy(dctxPtr->header + dctxPtr->tmpInSize, srcPtr, sizeToCopy); srcPtr += sizeToCopy; dctxPtr->tmpInSize += sizeToCopy; - if (dctxPtr->tmpInSize < dctxPtr->tmpInTarget) { /* not enough input to get full sBlockSize; wait for more */ + if (dctxPtr->tmpInSize < dctxPtr->tmpInTarget) { + /* not enough input to get full sBlockSize; wait for more */ nextSrcSizeHint = dctxPtr->tmpInTarget - dctxPtr->tmpInSize; doAnotherStage = 0; break; diff --git a/lib/lz4frame.h b/lib/lz4frame.h index 9a2130a..b1719e2 100644 --- a/lib/lz4frame.h +++ b/lib/lz4frame.h @@ -364,14 +364,11 @@ LZ4FLIB_API size_t LZ4F_decompress(LZ4F_dctx* dctx, /*! LZ4F_resetDecompressionContext() : v1.8.0 - * When decompression ends successfully, - * it's possible to start a new decompression immediately - * re-using the same context. - * However, in case of an error, the context is left in "undefined" state. + * In case of an error, the context is left in "undefined" state. * In which case, it's necessary to reset it, before re-using it. * This method can also be used to abruptly stop an unfinished decompression, - * and start a new on the same context. */ -LZ4FLIB_API LZ4F_errorCode_t LZ4F_resetDecompressionContext(LZ4F_dctx* dctx); + * and start a new with the same context. */ +LZ4FLIB_API void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx); /* always successful */ diff --git a/lib/lz4frame_static.h b/lib/lz4frame_static.h index 8ea496d..d3bae82 100644 --- a/lib/lz4frame_static.h +++ b/lib/lz4frame_static.h @@ -50,15 +50,6 @@ extern "C" { #include "lz4frame.h" -/* --- Experimental functions --- */ -/* LZ4F_resetDecompressionContext() : - * LZ4F_decompress() does not guarantee to leave dctx in clean state in case of errors. - * In order to re-use a dctx after a decompression error, - * use LZ4F_resetDecompressionContext() first. - * dctx will be able to start decompression on a new frame */ -LZ4FLIB_API LZ4F_errorCode_t LZ4F_resetDecompressionContext(LZ4F_dctx* dctx); - - /* --- Error List --- */ #define LZ4F_LIST_ERRORS(ITEM) \ ITEM(OK_NoError) \ diff --git a/tests/frametest.c b/tests/frametest.c index 04597a4..538bedc 100644 --- a/tests/frametest.c +++ b/tests/frametest.c @@ -40,6 +40,7 @@ #include /* fprintf */ #include /* strcmp */ #include /* clock_t, clock(), CLOCKS_PER_SEC */ +#include #include "lz4frame_static.h" #include "lz4.h" /* LZ4_VERSION_STRING */ #define XXH_STATIC_LINKING_ONLY @@ -67,7 +68,6 @@ static void FUZ_writeLE32 (void* dstVoidPtr, U32 value32) #define GB *(1U<<30) static const U32 nbTestsDefault = 256 KB; -#define COMPRESSIBLE_NOISE_LENGTH (2 MB) #define FUZ_COMPRESSIBILITY_DEFAULT 50 static const U32 prime1 = 2654435761U; static const U32 prime2 = 2246822519U; @@ -166,7 +166,7 @@ static unsigned FUZ_highbit(U32 v32) *********************************************************/ int basicTests(U32 seed, double compressibility) { - int testResult = 0; +#define COMPRESSIBLE_NOISE_LENGTH (2 MB) void* const CNBuffer = malloc(COMPRESSIBLE_NOISE_LENGTH); size_t const cBuffSize = LZ4F_compressFrameBound(COMPRESSIBLE_NOISE_LENGTH, NULL); void* const compressedBuffer = malloc(cBuffSize); @@ -176,9 +176,10 @@ int basicTests(U32 seed, double compressibility) LZ4F_decompressionContext_t dCtx = NULL; LZ4F_compressionContext_t cctx = NULL; U64 crcOrig; - + int basicTests_error = 0; LZ4F_preferences_t prefs; memset(&prefs, 0, sizeof(prefs)); + if (!CNBuffer || !compressedBuffer || !decodedBuffer) { DISPLAY("allocation error, not enough memory to start fuzzer tests \n"); goto _output_error; @@ -198,7 +199,7 @@ int basicTests(U32 seed, double compressibility) DISPLAYLEVEL(3, "LZ4F_compressFrame, compress null content : "); cSize = LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, NULL), CNBuffer, testSize, NULL); if (LZ4F_isError(cSize)) goto _output_error; - DISPLAYLEVEL(3, "Compressed null content into a %i bytes frame \n", (int)cSize); + DISPLAYLEVEL(3, "null content encoded into a %u bytes frame \n", (unsigned)cSize); DISPLAYLEVEL(3, "LZ4F_createDecompressionContext \n"); { LZ4F_errorCode_t const errorCode = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION); @@ -236,8 +237,6 @@ int basicTests(U32 seed, double compressibility) DISPLAYLEVEL(3, "Decompression test : \n"); { size_t decodedBufferSize = COMPRESSIBLE_NOISE_LENGTH; size_t compressedBufferSize = cSize; - BYTE* ip = (BYTE*)compressedBuffer; - BYTE* const iend = (BYTE*)compressedBuffer + cSize; LZ4F_errorCode_t errorCode = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION); if (LZ4F_isError(errorCode)) goto _output_error; @@ -280,6 +279,7 @@ int basicTests(U32 seed, double compressibility) { size_t oSize = 0; size_t iSize = 0; LZ4F_frameInfo_t fi; + const BYTE* ip = (BYTE*)compressedBuffer; DISPLAYLEVEL(3, "Start by feeding 0 bytes, to get next input size : "); errorCode = LZ4F_decompress(dCtx, NULL, &oSize, ip, &iSize, NULL); @@ -315,10 +315,30 @@ int basicTests(U32 seed, double compressibility) ip += iSize; } + DISPLAYLEVEL(3, "Decode a buggy input : "); + assert(COMPRESSIBLE_NOISE_LENGTH > 64); + assert(cSize > 48); + memcpy(decodedBuffer, (char*)compressedBuffer+16, 32); /* save correct data */ + memcpy((char*)compressedBuffer+16, (const char*)decodedBuffer+32, 32); /* insert noise */ + { size_t dbSize = COMPRESSIBLE_NOISE_LENGTH; + size_t cbSize = cSize; + size_t const decompressError = LZ4F_decompress(dCtx, decodedBuffer, &dbSize, + compressedBuffer, &cbSize, + NULL); + if (!LZ4F_isError(decompressError)) goto _output_error; + DISPLAYLEVEL(3, "error detected : %s \n", LZ4F_getErrorName(decompressError)); + } + memcpy((char*)compressedBuffer+16, decodedBuffer, 32); /* restore correct data */ + + DISPLAYLEVEL(3, "Reset decompression context, since it's left in error state \n"); + LZ4F_resetDecompressionContext(dCtx); /* always successful */ + DISPLAYLEVEL(3, "Byte after byte : "); { BYTE* const ostart = (BYTE*)decodedBuffer; BYTE* op = ostart; BYTE* const oend = (BYTE*)decodedBuffer + COMPRESSIBLE_NOISE_LENGTH; + const BYTE* ip = compressedBuffer; + const BYTE* const iend = ip + cSize; while (ip < iend) { size_t oSize = oend-op; size_t iSize = 1; @@ -330,11 +350,7 @@ int basicTests(U32 seed, double compressibility) { U64 const crcDest = XXH64(decodedBuffer, COMPRESSIBLE_NOISE_LENGTH, 1); if (crcDest != crcOrig) goto _output_error; } DISPLAYLEVEL(3, "Regenerated %u/%u bytes \n", (unsigned)(op-ostart), COMPRESSIBLE_NOISE_LENGTH); - } - - errorCode = LZ4F_freeDecompressionContext(dCtx); - if (LZ4F_isError(errorCode)) goto _output_error; - dCtx = NULL; + } } DISPLAYLEVEL(3, "Using 64 KB block : "); @@ -366,9 +382,6 @@ int basicTests(U32 seed, double compressibility) const BYTE* ip = (const BYTE*)compressedBuffer; const BYTE* const iend = (const BYTE*)compressedBuffer + cSize; - { LZ4F_errorCode_t const createError = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION); - if (LZ4F_isError(createError)) goto _output_error; } - DISPLAYLEVEL(3, "random segment sizes : "); while (ip < iend) { unsigned const nbBits = FUZ_rand(&randState) % maxBits; @@ -552,10 +565,10 @@ _end: free(decodedBuffer); LZ4F_freeDecompressionContext(dCtx); dCtx = NULL; LZ4F_freeCompressionContext(cctx); cctx = NULL; - return testResult; + return basicTests_error; _output_error: - testResult = 1; + basicTests_error = 1; DISPLAY("Error detected ! \n"); goto _end; } -- 2.7.4