From 4aeb5020c35e2464c25eb69d8bf6c7645b8faf20 Mon Sep 17 00:00:00 2001
From: Alexander Mohr
+typedef enum {
+ LZ4B_COMPRESSED,
+ LZ4B_UNCOMPRESSED,
+} LZ4F_blockCompression_t;
+
typedef struct {
LZ4F_blockSizeID_t blockSizeID; /* max64KB, max256KB, max1MB, max4MB; 0 == default */
LZ4F_blockMode_t blockMode; /* LZ4F_blockLinked, LZ4F_blockIndependent; 0 == default */
diff --git a/examples/frameCompress.c b/examples/frameCompress.c
index 9eaa4da..3219014 100644
--- a/examples/frameCompress.c
+++ b/examples/frameCompress.c
@@ -11,9 +11,9 @@
#include
LZ4LIB_STATIC_API int LZ4_getDictSize (LZ4_stream_t* LZ4_dict, int dictSize); +Get the size of the dictionary. This can be used for adding data without + compression to the LZ4 archive. If linked blocked mode is used the memory + of the dictionary is kept free. + This way uncompressed data does not influence the effectiveness of the + dictionary. + @param LZ4_dict Pointer to the dictionary to get the size of. + @param dictSize The maximum dictionary size. (Normally 64 KB). + @return The size of the dictionary. + +
It's possible to have input and output sharing the same buffer, for highly constrained memory environments. diff --git a/doc/lz4frame_manual.html b/doc/lz4frame_manual.html index 9490b51..b47a92f 100644 --- a/doc/lz4frame_manual.html +++ b/doc/lz4frame_manual.html @@ -75,11 +75,6 @@ LZ4F_OBSOLETE_ENUM(skippableFrame) } LZ4F_frameType_t;
typedef enum { - LZ4B_COMPRESSED, - LZ4B_UNCOMPRESSED, -} LZ4F_blockCompression_t; -
typedef struct { LZ4F_blockSizeID_t blockSizeID; /* max64KB, max256KB, max1MB, max4MB; 0 == default */ LZ4F_blockMode_t blockMode; /* LZ4F_blockLinked, LZ4F_blockIndependent; 0 == default */ @@ -189,22 +184,40 @@ LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctx); This value is provided by LZ4F_compressBound(). If this condition is not respected, LZ4F_compress() will fail (result is an errorCode). After an error, the state is left in a UB state, and must be re-initialized or freed. + If previously an uncompressed block was written, buffered data is flushed + before appending compressed data is continued. `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). or an error code if it fails (which can be tested using LZ4F_isError())
size_t LZ4F_flush(LZ4F_cctx* cctx, +size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* cOptPtr); +/*! LZ4F_flush() : + * When data must be generated and sent immediately, without waiting for a block to be completely filled, + * it's possible to call LZ4_flush(). It will immediately compress any data buffered within cctx. + * `dstCapacity` must be large enough to ensure the operation will be successful. + * `cOptPtr` is optional : it's possible to provide NULL, all options will be set to default. + * @return : nb of bytes written into dstBuffer (can be zero, when there is no data stored within cctx) + * or an error code if it fails (which can be tested using LZ4F_isError()) + * Note : LZ4F_flush() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr). + */ +size_t LZ4F_flush(LZ4F_cctx* cctx, void* dstBuffer, size_t dstCapacity, const LZ4F_compressOptions_t* cOptPtr); -When data must be generated and sent immediately, without waiting for a block to be completely filled, - it's possible to call LZ4_flush(). It will immediately compress any data buffered within cctx. - `dstCapacity` must be large enough to ensure the operation will be successful. - `cOptPtr` is optional : it's possible to provide NULL, all options will be set to default. - @return : nb of bytes written into dstBuffer (can be zero, when there is no data stored within cctx) +
LZ4F_uncompressedUpdate() can be called repetitively to add as much data uncompressed data as necessary. + Important rule: dstCapacity MUST be large enough to store the entire source buffer as + no compression is done for this operation + If this condition is not respected, LZ4F_uncompressedUpdate() will fail (result is an errorCode). + After an error, the state is left in a UB state, and must be re-initialized or freed. + If previously a compressed block was written, buffered data is flushed + before appending uncompressed data is continued. + `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. + @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). or an error code if it fails (which can be tested using LZ4F_isError()) - Note : LZ4F_flush() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr).
size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctx, - void* dstBuffer, size_t dstCapacity, - const void* srcBuffer, size_t srcSize, - const LZ4F_compressOptions_t* cOptPtr); -/*! LZ4F_flush() : - * When data must be generated and sent immediately, without waiting for a block to be completely filled, - * it's possible to call LZ4_flush(). It will immediately compress any data buffered within cctx. - * `dstCapacity` must be large enough to ensure the operation will be successful. - * `cOptPtr` is optional : it's possible to provide NULL, all options will be set to default. - * @return : nb of bytes written into dstBuffer (can be zero, when there is no data stored within cctx) - * or an error code if it fails (which can be tested using LZ4F_isError()) - * Note : LZ4F_flush() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr). - */ -size_t LZ4F_flush(LZ4F_cctx* cctx, +size_t LZ4F_flush(LZ4F_cctx* cctx, void* dstBuffer, size_t dstCapacity, const LZ4F_compressOptions_t* cOptPtr); -LZ4F_uncompressedUpdate() can be called repetitively to add as much data uncompressed data as necessary. - Important rule: dstCapacity MUST be large enough to store the entire source buffer as - no compression is done for this operation - If this condition is not respected, LZ4F_uncompressedUpdate() will fail (result is an errorCode). - After an error, the state is left in a UB state, and must be re-initialized or freed. - If previously a compressed block was written, buffered data is flushed - before appending uncompressed data is continued. - `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. - @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). +
When data must be generated and sent immediately, without waiting for a block to be completely filled, + it's possible to call LZ4_flush(). It will immediately compress any data buffered within cctx. + `dstCapacity` must be large enough to ensure the operation will be successful. + `cOptPtr` is optional : it's possible to provide NULL, all options will be set to default. + @return : nb of bytes written into dstBuffer (can be zero, when there is no data stored within cctx) or an error code if it fails (which can be tested using LZ4F_isError()) + Note : LZ4F_flush() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr).
typedef enum { LZ4F_LIST_ERRORS(LZ4F_GENERATE_ENUM) _LZ4F_dummy_error_enum_for_c89_never_used } LZ4F_errorCodes;
LZ4FLIB_STATIC_API size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* cOptPtr); +LZ4F_uncompressedUpdate() can be called repetitively to add as much data uncompressed data as necessary. + Important rule: dstCapacity MUST be large enough to store the entire source buffer as + no compression is done for this operation + If this condition is not respected, LZ4F_uncompressedUpdate() will fail (result is an errorCode). + After an error, the state is left in a UB state, and must be re-initialized or freed. + If previously a compressed block was written, buffered data is flushed + before appending uncompressed data is continued. + `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. + @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). + or an error code if it fails (which can be tested using LZ4F_isError()) + +
LZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize); diff --git a/examples/frameCompress.c b/examples/frameCompress.c index 3219014..25ff729 100644 --- a/examples/frameCompress.c +++ b/examples/frameCompress.c @@ -13,6 +13,7 @@ #include#include +#include #define IN_CHUNK_SIZE (16*1024) diff --git a/lib/lz4frame.h b/lib/lz4frame.h index 20bfb8b..7d81fa0 100644 --- a/lib/lz4frame.h +++ b/lib/lz4frame.h @@ -314,22 +314,6 @@ LZ4FLIB_API size_t LZ4F_compressUpdate(LZ4F_cctx* cctx, const void* srcBuffer, size_t srcSize, const LZ4F_compressOptions_t* cOptPtr); -/*! LZ4F_uncompressedUpdate() : - * LZ4F_uncompressedUpdate() can be called repetitively to add as much data uncompressed data as necessary. - * Important rule: dstCapacity MUST be large enough to store the entire source buffer as - * no compression is done for this operation - * If this condition is not respected, LZ4F_uncompressedUpdate() will fail (result is an errorCode). - * After an error, the state is left in a UB state, and must be re-initialized or freed. - * If previously a compressed block was written, buffered data is flushed - * before appending uncompressed data is continued. - * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. - * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). - * or an error code if it fails (which can be tested using LZ4F_isError()) - */ -LZ4FLIB_API size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctx, - void* dstBuffer, size_t dstCapacity, - const void* srcBuffer, size_t srcSize, - const LZ4F_compressOptions_t* cOptPtr); /*! LZ4F_flush() : * When data must be generated and sent immediately, without waiting for a block to be completely filled, * it's possible to call LZ4_flush(). It will immediately compress any data buffered within cctx. @@ -560,6 +544,23 @@ LZ4FLIB_STATIC_API LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult); LZ4FLIB_STATIC_API size_t LZ4F_getBlockSize(unsigned); +/*! LZ4F_uncompressedUpdate() : + * LZ4F_uncompressedUpdate() can be called repetitively to add as much data uncompressed data as necessary. + * Important rule: dstCapacity MUST be large enough to store the entire source buffer as + * no compression is done for this operation + * If this condition is not respected, LZ4F_uncompressedUpdate() will fail (result is an errorCode). + * After an error, the state is left in a UB state, and must be re-initialized or freed. + * If previously a compressed block was written, buffered data is flushed + * before appending uncompressed data is continued. + * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. + * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). + * or an error code if it fails (which can be tested using LZ4F_isError()) + */ +LZ4FLIB_STATIC_API size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* cOptPtr); + /********************************** * Bulk processing dictionary API *********************************/ diff --git a/ossfuzz/fuzz.h b/ossfuzz/fuzz.h index eefac63..917a304 100644 --- a/ossfuzz/fuzz.h +++ b/ossfuzz/fuzz.h @@ -41,6 +41,11 @@ extern "C" { int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size); +/** + * Test if injection of uncompressed data into a stream is working properly + */ +int LLVMFuzzerUncompressedDataInjection(const uint8_t *data, size_t size) + #ifdef __cplusplus } #endif -- cgit v0.12 From 1738b50443d1446c9c80ebd7c9272427f2644809 Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Fri, 10 Jun 2022 19:45:21 +0000 Subject: fuzz-test: add fuzz test for uncompressed api add a fuzzing test for uncompressed frame api Signed-off-by: Alexander Mohr --- lib/lz4frame.c | 3 +- ossfuzz/Makefile | 1 + ossfuzz/fuzz.h | 5 - ossfuzz/round_trip_frame_uncompressed_fuzzer.c | 127 +++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 ossfuzz/round_trip_frame_uncompressed_fuzzer.c diff --git a/lib/lz4frame.c b/lib/lz4frame.c index 0c78a1f..c7e0595 100644 --- a/lib/lz4frame.c +++ b/lib/lz4frame.c @@ -129,8 +129,7 @@ static int g_debuglog_enable = 1; **************************************/ #if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include -#include -typedef uint8_t BYTE; + typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; typedef int32_t S32; diff --git a/ossfuzz/Makefile b/ossfuzz/Makefile index 2ec1675..deb2938 100644 --- a/ossfuzz/Makefile +++ b/ossfuzz/Makefile @@ -45,6 +45,7 @@ FUZZERS := \ round_trip_hc_fuzzer \ compress_frame_fuzzer \ round_trip_frame_fuzzer \ + round_trip_frame_uncompressed_fuzzer \ decompress_frame_fuzzer .PHONY: all diff --git a/ossfuzz/fuzz.h b/ossfuzz/fuzz.h index 917a304..eefac63 100644 --- a/ossfuzz/fuzz.h +++ b/ossfuzz/fuzz.h @@ -41,11 +41,6 @@ extern "C" { int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size); -/** - * Test if injection of uncompressed data into a stream is working properly - */ -int LLVMFuzzerUncompressedDataInjection(const uint8_t *data, size_t size) - #ifdef __cplusplus } #endif diff --git a/ossfuzz/round_trip_frame_uncompressed_fuzzer.c b/ossfuzz/round_trip_frame_uncompressed_fuzzer.c new file mode 100644 index 0000000..631d149 --- /dev/null +++ b/ossfuzz/round_trip_frame_uncompressed_fuzzer.c @@ -0,0 +1,127 @@ +/** + * This fuzz target performs a lz4 round-trip test (compress & decompress), + * compares the result with the original, and calls abort() on corruption. + */ + +#include +#include +#include +#include + +#include "fuzz_data_producer.h" +#include "fuzz_helpers.h" +#include "lz4.h" +#include "lz4_helpers.h" +#include "lz4frame.h" +#include "lz4frame_static.h" + +static void decompress_data(LZ4F_dctx *dctx, void *src, void *dst, + size_t dstCapacity, size_t readSize) { + size_t ret = 1; + const void *srcPtr = (const char *)src; + void *dstPtr = (char *)dst; + const void *const srcEnd = (const char *)srcPtr + readSize; + + while (ret != 0) { + while (srcPtr < srcEnd && ret != 0) { + /* Any data within dst has been flushed at this stage */ + size_t dstSize = dstCapacity; + size_t srcSize = (const char *)srcEnd - (const char *)srcPtr; + ret = LZ4F_decompress(dctx, dstPtr, &dstSize, srcPtr, &srcSize, + /* LZ4F_decompressOptions_t */ NULL); + FUZZ_ASSERT(!LZ4F_isError(ret)); + + /* Update input */ + srcPtr = (const char *)srcPtr + srcSize; + dstPtr = (char *)dstPtr + dstSize; + } + + FUZZ_ASSERT(srcPtr <= srcEnd); + } +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(data, size); + LZ4F_preferences_t const prefs = FUZZ_dataProducer_preferences(producer); + size = FUZZ_dataProducer_remainingBytes(producer); + + uint8_t *uncompressedData = malloc(size); + size_t uncompressedOffset = rand() % (size + 1); + + FUZZ_dataProducer_t *uncompressedProducer = + FUZZ_dataProducer_create(uncompressedData, size); + size_t uncompressedSize = + FUZZ_dataProducer_remainingBytes(uncompressedProducer); + + size_t const dstCapacity = + LZ4F_compressFrameBound(LZ4_compressBound(size), &prefs) + + uncompressedSize; + char *const dst = (char *)malloc(dstCapacity); + size_t rtCapacity = dstCapacity; + char *const rt = (char *)malloc(rtCapacity); + + FUZZ_ASSERT(dst); + FUZZ_ASSERT(rt); + + /* Compression must succeed and round trip correctly. */ + LZ4F_compressionContext_t ctx; + size_t const ctxCreation = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); + FUZZ_ASSERT(!LZ4F_isError(ctxCreation)); + + size_t const headerSize = LZ4F_compressBegin(ctx, dst, dstCapacity, &prefs); + FUZZ_ASSERT(!LZ4F_isError(headerSize)); + size_t compressedSize = headerSize; + + /* Compress data before uncompressed offset */ + size_t lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity, + data, uncompressedOffset, NULL); + FUZZ_ASSERT(!LZ4F_isError(lz4Return)); + compressedSize += lz4Return; + + /* Add uncompressed data */ + lz4Return = LZ4F_uncompressedUpdate(ctx, dst + compressedSize, dstCapacity, + uncompressedData, uncompressedSize, NULL); + FUZZ_ASSERT(!LZ4F_isError(lz4Return)); + compressedSize += lz4Return; + + /* Compress data after uncompressed offset */ + lz4Return = LZ4F_compressUpdate(ctx, dst + compressedSize, dstCapacity, + data + uncompressedOffset, + size - uncompressedOffset, NULL); + FUZZ_ASSERT(!LZ4F_isError(lz4Return)); + compressedSize += lz4Return; + + /* Finish compression */ + lz4Return = LZ4F_compressEnd(ctx, dst + compressedSize, dstCapacity, NULL); + FUZZ_ASSERT(!LZ4F_isError(lz4Return)); + compressedSize += lz4Return; + + LZ4F_decompressOptions_t opts; + memset(&opts, 0, sizeof(opts)); + opts.stableDst = 1; + LZ4F_dctx *dctx; + LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION); + FUZZ_ASSERT(dctx); + + decompress_data(dctx, dst, rt, rtCapacity, compressedSize); + + LZ4F_freeDecompressionContext(dctx); + + char *const expectedData = (char *)malloc(size + uncompressedSize); + memcpy(expectedData, data, uncompressedOffset); + memcpy(expectedData + uncompressedOffset, uncompressedData, uncompressedSize); + memcpy(expectedData + uncompressedOffset + uncompressedSize, + data + uncompressedOffset, size - uncompressedOffset); + + FUZZ_ASSERT_MSG(!memcmp(expectedData, rt, size), "Corruption!"); + free(expectedData); + + free(dst); + free(rt); + free(uncompressedData); + + FUZZ_dataProducer_free(producer); + FUZZ_dataProducer_free(uncompressedProducer); + LZ4F_freeCompressionContext(ctx); + return 0; +} -- cgit v0.12 From 3c57d2f185c2b482fde69a4c5f04dbb48bacab58 Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Sat, 11 Jun 2022 22:46:49 +0200 Subject: lz4frame: fix different linkage error Signed-off-by: Alexander Mohr --- lib/lz4frame.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lz4frame.c b/lib/lz4frame.c index c7e0595..788f19f 100644 --- a/lib/lz4frame.c +++ b/lib/lz4frame.c @@ -1047,7 +1047,7 @@ size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, * or an error code if it fails (which can be tested using LZ4F_isError()) * After an error, the state is left in a UB state, and must be re-initialized. */ -LZ4FLIB_API size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr, +LZ4FLIB_STATIC_API size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr, void* dstBuffer, size_t dstCapacity, const void* srcBuffer, size_t srcSize, const LZ4F_compressOptions_t* compressOptionsPtr) { -- cgit v0.12 From 9a42a9db94ff4c71de5590cae8d0f90339e6733b Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Sat, 11 Jun 2022 23:14:56 +0200 Subject: dict-size: make lz4 context const change the context to const to make clear that the context is not modified --- doc/lz4_manual.html | 2 +- lib/lz4.c | 4 ++-- lib/lz4.h | 2 +- lib/lz4hc.c | 4 ++-- lib/lz4hc.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/lz4_manual.html b/doc/lz4_manual.html index 13c1ae6..700cb84 100644 --- a/doc/lz4_manual.html +++ b/doc/lz4_manual.html @@ -391,7 +391,7 @@ int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
LZ4LIB_STATIC_API int LZ4_getDictSize (LZ4_stream_t* LZ4_dict, int dictSize); +LZ4LIB_STATIC_API int LZ4_getDictSize (const LZ4_stream_t* LZ4_dict, int dictSize);Get the size of the dictionary. This can be used for adding data without compression to the LZ4 archive. If linked blocked mode is used the memory of the dictionary is kept free. diff --git a/lib/lz4.c b/lib/lz4.c index 932e2cd..289162d 100644 --- a/lib/lz4.c +++ b/lib/lz4.c @@ -1689,9 +1689,9 @@ int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* * @param dictSize The maximum dictionary size. (Normally 64 KB). * @return The size of the dictionary. */ -int LZ4_getDictSize (LZ4_stream_t* LZ4_dict, int dictSize) +int LZ4_getDictSize (const LZ4_stream_t* LZ4_dict, int dictSize) { - LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; + const LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } diff --git a/lib/lz4.h b/lib/lz4.h index f2a529f..ce2288e 100644 --- a/lib/lz4.h +++ b/lib/lz4.h @@ -519,7 +519,7 @@ LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const * @param dictSize The maximum dictionary size. (Normally 64 KB). * @return The size of the dictionary. */ -LZ4LIB_STATIC_API int LZ4_getDictSize (LZ4_stream_t* LZ4_dict, int dictSize); +LZ4LIB_STATIC_API int LZ4_getDictSize (const LZ4_stream_t* LZ4_dict, int dictSize); /*! In-place compression and decompression * diff --git a/lib/lz4hc.c b/lib/lz4hc.c index bf6294d..b5bc880 100644 --- a/lib/lz4hc.c +++ b/lib/lz4hc.c @@ -1164,8 +1164,8 @@ int LZ4_compress_HC_continue_destSize (LZ4_streamHC_t* LZ4_streamHCPtr, const ch * @param dictSize The maximum dictionary size. (Normally 64 KB). * @return The size of the dictionary. */ -int LZ4_getDictHCSize(LZ4_streamHC_t* LZ4_streamHCPtr, int dictSize) { - LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse; +int LZ4_getDictHCSize(const LZ4_streamHC_t* LZ4_streamHCPtr, int dictSize) { + const LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse; int const prefixSize = (int)(streamPtr->end - (streamPtr->base + streamPtr->dictLimit)); DEBUGLOG(5, "LZ4_saveDictHC(%p, %p, %d)", LZ4_streamHCPtr, safeBuffer, dictSize); assert(prefixSize >= 0); diff --git a/lib/lz4hc.h b/lib/lz4hc.h index e62dfa7..3b31624 100644 --- a/lib/lz4hc.h +++ b/lib/lz4hc.h @@ -415,7 +415,7 @@ LZ4LIB_STATIC_API void LZ4_attach_HC_dictionary( * @param dictSize The maximum dictionary size. (Normally 64 KB). * @return The size of the dictionary. */ -LZ4LIB_STATIC_API int LZ4_getDictHCSize(LZ4_streamHC_t* LZ4_streamHCPtr, int dictSize); +LZ4LIB_STATIC_API int LZ4_getDictHCSize(const LZ4_streamHC_t* LZ4_streamHCPtr, int dictSize); #if defined (__cplusplus) } -- cgit v0.12 From af447b22c80d25c8d88cc3147c120e027b18d0ae Mon Sep 17 00:00:00 2001 From: Alexander Mohr
Date: Sun, 12 Jun 2022 00:41:55 +0200 Subject: meson: fix meson build add static dependency to examples --- contrib/meson/meson/examples/meson.build | 2 +- lib/lz4frame.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/meson/meson/examples/meson.build b/contrib/meson/meson/examples/meson.build index 791bd94..f6b6c41 100644 --- a/contrib/meson/meson/examples/meson.build +++ b/contrib/meson/meson/examples/meson.build @@ -26,7 +26,7 @@ foreach e, src : examples executable( e, lz4_source_root / 'examples' / src, - dependencies: liblz4_dep, + dependencies: [liblz4_dep, liblz4_internal_dep], install: false ) endforeach diff --git a/lib/lz4frame.c b/lib/lz4frame.c index 788f19f..251521e 100644 --- a/lib/lz4frame.c +++ b/lib/lz4frame.c @@ -1047,7 +1047,7 @@ size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, * or an error code if it fails (which can be tested using LZ4F_isError()) * After an error, the state is left in a UB state, and must be re-initialized. */ -LZ4FLIB_STATIC_API size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr, +size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr, void* dstBuffer, size_t dstCapacity, const void* srcBuffer, size_t srcSize, const LZ4F_compressOptions_t* compressOptionsPtr) { -- cgit v0.12 From 5065080664437efcb4215823b9ad95f5571a9d7c Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Mon, 13 Jun 2022 07:46:53 +0200 Subject: ossfuzz: extend fuzzing test to include linked blocks fuzzing test now tests linked and independent blocks Signed-off-by: Alexander Mohr --- ossfuzz/round_trip_frame_uncompressed_fuzzer.c | 29 +++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/ossfuzz/round_trip_frame_uncompressed_fuzzer.c b/ossfuzz/round_trip_frame_uncompressed_fuzzer.c index 631d149..cf9cbb9 100644 --- a/ossfuzz/round_trip_frame_uncompressed_fuzzer.c +++ b/ossfuzz/round_trip_frame_uncompressed_fuzzer.c @@ -15,7 +15,7 @@ #include "lz4frame.h" #include "lz4frame_static.h" -static void decompress_data(LZ4F_dctx *dctx, void *src, void *dst, +static void decompress(LZ4F_dctx *dctx, void *src, void *dst, size_t dstCapacity, size_t readSize) { size_t ret = 1; const void *srcPtr = (const char *)src; @@ -40,9 +40,8 @@ static void decompress_data(LZ4F_dctx *dctx, void *src, void *dst, } } -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(data, size); - LZ4F_preferences_t const prefs = FUZZ_dataProducer_preferences(producer); +static void compress_round_trip(const uint8_t* data, size_t size, + FUZZ_dataProducer_t *producer, LZ4F_preferences_t const prefs) { size = FUZZ_dataProducer_remainingBytes(producer); uint8_t *uncompressedData = malloc(size); @@ -103,7 +102,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION); FUZZ_ASSERT(dctx); - decompress_data(dctx, dst, rt, rtCapacity, compressedSize); + decompress(dctx, dst, rt, rtCapacity, compressedSize); LZ4F_freeDecompressionContext(dctx); @@ -123,5 +122,25 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { FUZZ_dataProducer_free(producer); FUZZ_dataProducer_free(uncompressedProducer); LZ4F_freeCompressionContext(ctx); +} + +static void compress_linked_block_mode(const uint8_t* data, size_t size) { + FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(data, size); + LZ4F_preferences_t prefs = FUZZ_dataProducer_preferences(producer); + prefs.frameInfo.blockMode = LZ4F_blockLinked; + compress_round_trip(data, size, producer, prefs); +} + +static void compress_independent_block_mode(const uint8_t* data, size_t size) { + FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(data, size); + LZ4F_preferences_t prefs = FUZZ_dataProducer_preferences(producer); + prefs.frameInfo.blockMode = LZ4F_blockIndependent; + compress_round_trip(data, size, producer, prefs); +} + + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + compress_linked_block_mode(data, size); + compress_independent_block_mode(data, size); return 0; } -- cgit v0.12 From e595150bafff318f6f66e4b364193b32701449e3 Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Mon, 4 Jul 2022 08:51:37 +0200 Subject: lz4frame: correct start and size after flush when the block mode changes a flush is executed, to prevent mixing compressed and uncompressed data. Prior to this commit dstStart, dstPtr, dstCapacity where not updated to include the offset from bytesWritten. For inputs > blockSize this meant the flushed data was overwritten. Signed-off-by: Alexander Mohr --- lib/lz4frame.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/lz4frame.c b/lib/lz4frame.c index 251521e..f32ed1d 100644 --- a/lib/lz4frame.c +++ b/lib/lz4frame.c @@ -886,7 +886,7 @@ static size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr, size_t const blockSize = cctxPtr->maxBlockSize; const BYTE* srcPtr = (const BYTE*)srcBuffer; const BYTE* const srcEnd = srcPtr + srcSize; - BYTE* const dstStart = (BYTE*)dstBuffer; + BYTE* dstStart = (BYTE*)dstBuffer; BYTE* dstPtr = dstStart; LZ4F_lastBlockStatus lastBlockCompressed = notDone; compressFunc_t const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel); @@ -896,6 +896,9 @@ static size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr, /* flush currently written block, to continue with new block compression */ if (cctxPtr->blockCompression != blockCompression) { bytesWritten = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr); + dstStart = (BYTE*)dstBuffer + bytesWritten; + dstPtr = dstStart; + dstCapacity -= bytesWritten; cctxPtr->blockCompression = blockCompression; } -- cgit v0.12 From 42eb47d42f041054140b8e08ffc6ba85e9f092f2 Mon Sep 17 00:00:00 2001 From: Alexander Mohr Date: Tue, 5 Jul 2022 11:35:58 +0200 Subject: uncompressed-api: allow uncompressed_update only for independent blocks Signed-off-by: Alexander Mohr --- doc/lz4_manual.html | 12 ---- lib/lz4.c | 24 ++------ lib/lz4.h | 11 ---- lib/lz4frame.c | 172 ++++++++++++++++++++++------------------------------ lib/lz4frame.h | 1 + lib/lz4hc.c | 27 ++------- 6 files changed, 84 insertions(+), 163 deletions(-) diff --git a/doc/lz4_manual.html b/doc/lz4_manual.html index 700cb84..037cfc0 100644 --- a/doc/lz4_manual.html +++ b/doc/lz4_manual.html @@ -391,18 +391,6 @@ int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
-LZ4LIB_STATIC_API int LZ4_getDictSize (const LZ4_stream_t* LZ4_dict, int dictSize); -Get the size of the dictionary. This can be used for adding data without - compression to the LZ4 archive. If linked blocked mode is used the memory - of the dictionary is kept free. - This way uncompressed data does not influence the effectiveness of the - dictionary. - @param LZ4_dict Pointer to the dictionary to get the size of. - @param dictSize The maximum dictionary size. (Normally 64 KB). - @return The size of the dictionary. - -
-It's possible to have input and output sharing the same buffer, for highly constrained memory environments. diff --git a/lib/lz4.c b/lib/lz4.c index 289162d..a2272cf 100644 --- a/lib/lz4.c +++ b/lib/lz4.c @@ -1679,25 +1679,6 @@ int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* return result; } -/*! LZ4_getDictSize(): - * Get the size of the dictionary. This can be used for adding data without - * compression to the LZ4 archive. If linked blocked mode is used the memory - * of the dictionary is kept free. - * This way uncompressed data does not influence the effectiveness of the - * dictionary. - * @param LZ4_dict Pointer to the dictionary to get the size of. - * @param dictSize The maximum dictionary size. (Normally 64 KB). - * @return The size of the dictionary. - */ -int LZ4_getDictSize (const LZ4_stream_t* LZ4_dict, int dictSize) -{ - const LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; - - if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ - if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } - - return dictSize; -} /*! LZ4_saveDict() : * If previously compressed data block is not guaranteed to remain available at its memory location, @@ -1709,9 +1690,12 @@ int LZ4_getDictSize (const LZ4_stream_t* LZ4_dict, int dictSize) int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) { LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; - dictSize = LZ4_getDictSize(LZ4_dict, dictSize); + DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer); + if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ + if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } + if (safeBuffer == NULL) assert(dictSize == 0); if (dictSize > 0) { const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; diff --git a/lib/lz4.h b/lib/lz4.h index ce2288e..6c068c6 100644 --- a/lib/lz4.h +++ b/lib/lz4.h @@ -509,17 +509,6 @@ LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const c */ LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream); -/*! LZ4_getDictSize(): - * Get the size of the dictionary. This can be used for adding data without - * compression to the LZ4 archive. If linked blocked mode is used the memory - * of the dictionary is kept free. - * This way uncompressed data does not influence the effectiveness of the - * dictionary. - * @param LZ4_dict Pointer to the dictionary to get the size of. - * @param dictSize The maximum dictionary size. (Normally 64 KB). - * @return The size of the dictionary. - */ -LZ4LIB_STATIC_API int LZ4_getDictSize (const LZ4_stream_t* LZ4_dict, int dictSize); /*! In-place compression and decompression * diff --git a/lib/lz4frame.c b/lib/lz4frame.c index f32ed1d..3042d6f 100644 --- a/lib/lz4frame.c +++ b/lib/lz4frame.c @@ -841,23 +841,12 @@ static compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int lev return LZ4F_compressBlockHC_continue; } -static int LZ4F_maxDictSize(void) { - return 64 KB; -} - /* Save history (up to 64KB) into @tmpBuff */ static int LZ4F_localSaveDict(LZ4F_cctx_t* cctxPtr) { if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) - return LZ4_saveDict ((LZ4_stream_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), LZ4F_maxDictSize()); - return LZ4_saveDictHC ((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), LZ4F_maxDictSize()); -} - -static int LZ4F_localDictSize(LZ4F_cctx_t* cctxPtr) -{ - if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) - return LZ4_getDictSize ((LZ4_stream_t*)(cctxPtr->lz4CtxPtr), LZ4F_maxDictSize()); - return LZ4_getDictHCSize ((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), LZ4F_maxDictSize()); + return LZ4_saveDict ((LZ4_stream_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB); + return LZ4_saveDictHC ((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB); } typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus; @@ -886,69 +875,64 @@ static size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr, size_t const blockSize = cctxPtr->maxBlockSize; const BYTE* srcPtr = (const BYTE*)srcBuffer; const BYTE* const srcEnd = srcPtr + srcSize; - BYTE* dstStart = (BYTE*)dstBuffer; + BYTE* const dstStart = (BYTE*)dstBuffer; BYTE* dstPtr = dstStart; LZ4F_lastBlockStatus lastBlockCompressed = notDone; compressFunc_t const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel); - size_t bytesWritten = 0; + size_t bytesWritten; DEBUGLOG(4, "LZ4F_compressUpdate (srcSize=%zu)", srcSize); - /* flush currently written block, to continue with new block compression */ - if (cctxPtr->blockCompression != blockCompression) { - bytesWritten = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr); - dstStart = (BYTE*)dstBuffer + bytesWritten; - dstPtr = dstStart; - dstCapacity -= bytesWritten; - cctxPtr->blockCompression = blockCompression; - } - RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized); /* state must be initialized and waiting for next block */ - - if (blockCompression == LZ4B_COMPRESSED && - dstCapacity < LZ4F_compressBound_internal(srcSize, &(cctxPtr->prefs), cctxPtr->tmpInSize)) - RETURN_ERROR(dstMaxSize_tooSmall); + if (dstCapacity < LZ4F_compressBound_internal(srcSize, &(cctxPtr->prefs), cctxPtr->tmpInSize)) + RETURN_ERROR(dstMaxSize_tooSmall); if (blockCompression == LZ4B_UNCOMPRESSED && dstCapacity < srcSize) - RETURN_ERROR(dstMaxSize_tooSmall); + RETURN_ERROR(dstMaxSize_tooSmall); + + /* flush currently written block, to continue with new block compression */ + if (cctxPtr->blockCompression != blockCompression) { + bytesWritten = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr); + dstPtr += bytesWritten; + cctxPtr->blockCompression = blockCompression; + } if (compressOptionsPtr == NULL) compressOptionsPtr = &k_cOptionsNull; /* complete tmp buffer */ if (cctxPtr->tmpInSize > 0) { /* some data already within tmp buffer */ - size_t const sizeToCopy = blockSize - cctxPtr->tmpInSize; - assert(blockSize > cctxPtr->tmpInSize); - if (sizeToCopy > srcSize) { - /* add src to tmpIn buffer */ - memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize); - srcPtr = srcEnd; - cctxPtr->tmpInSize += srcSize; - /* still needs some CRC */ - } else { - /* complete tmpIn block and then compress it */ - lastBlockCompressed = fromTmpBuffer; - memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, sizeToCopy); - srcPtr += sizeToCopy; + size_t const sizeToCopy = blockSize - cctxPtr->tmpInSize; + assert(blockSize > cctxPtr->tmpInSize); + if (sizeToCopy > srcSize) { + /* add src to tmpIn buffer */ + memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize); + srcPtr = srcEnd; + cctxPtr->tmpInSize += srcSize; + /* still needs some CRC */ + } else { + /* complete tmpIn block and then compress it */ + lastBlockCompressed = fromTmpBuffer; + memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, sizeToCopy); + srcPtr += sizeToCopy; + + dstPtr += LZ4F_makeBlock(dstPtr, + cctxPtr->tmpIn, blockSize, + compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, + cctxPtr->cdict, + cctxPtr->prefs.frameInfo.blockChecksumFlag, blockCompression); + if (cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) cctxPtr->tmpIn += blockSize; + cctxPtr->tmpInSize = 0; + } } + while ((size_t)(srcEnd - srcPtr) >= blockSize) { + /* compress full blocks */ + lastBlockCompressed = fromSrcBuffer; dstPtr += LZ4F_makeBlock(dstPtr, - cctxPtr->tmpIn, blockSize, + srcPtr, blockSize, compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, cctxPtr->cdict, - cctxPtr->prefs.frameInfo.blockChecksumFlag, blockCompression); - - if (cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) cctxPtr->tmpIn += blockSize; - cctxPtr->tmpInSize = 0; - } - } - - while ((size_t)(srcEnd - srcPtr) >= blockSize) { - /* compress full blocks */ - lastBlockCompressed = fromSrcBuffer; - dstPtr += LZ4F_makeBlock(dstPtr, - srcPtr, blockSize, - compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, - cctxPtr->cdict, - cctxPtr->prefs.frameInfo.blockChecksumFlag, blockCompression); - srcPtr += blockSize; + cctxPtr->prefs.frameInfo.blockChecksumFlag, + blockCompression); + srcPtr += blockSize; } if ((cctxPtr->prefs.autoFlush) && (srcPtr < srcEnd)) { @@ -960,27 +944,20 @@ static size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr, cctxPtr->cdict, cctxPtr->prefs.frameInfo.blockChecksumFlag, blockCompression); - srcPtr = srcEnd; + srcPtr = srcEnd; } /* preserve dictionary within @tmpBuff whenever necessary */ if ((cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) && (lastBlockCompressed==fromSrcBuffer)) { - if (compressOptionsPtr->stableSrc) { - cctxPtr->tmpIn = cctxPtr->tmpBuff; /* src is stable : dictionary remains in src across invocations */ - } else { - int realDictSize; - if (blockCompression == LZ4B_COMPRESSED) { - realDictSize = LZ4F_localSaveDict(cctxPtr); + /* linked blocks are only supported in compressed mode, see LZ4F_uncompressedUpdate */ + assert(blockCompression == LZ4B_COMPRESSED); + if (compressOptionsPtr->stableSrc) { + cctxPtr->tmpIn = cctxPtr->tmpBuff; /* src is stable : dictionary remains in src across invocations */ } else { - /* only keep the space of the dictionary, so dict data is kept for the next compressedUpdate - * this is only relevant if linked block mode - * */ - realDictSize = LZ4F_localDictSize(cctxPtr); + int const realDictSize = LZ4F_localSaveDict(cctxPtr); + assert(0 <= realDictSize && realDictSize <= 64 KB); + cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize; } - - assert(0 <= realDictSize && realDictSize <= 64 KB); - cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize; - } } /* keep tmpIn within limits */ @@ -989,30 +966,24 @@ static size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr, { /* only preserve 64KB within internal buffer. Ensures there is enough room for next block. * note: this situation necessarily implies lastBlockCompressed==fromTmpBuffer */ - int realDictSize; - if (blockCompression == LZ4B_COMPRESSED) { - realDictSize = LZ4F_localSaveDict(cctxPtr); - } else { - /* only keep the space of the dictionary, so dict data is kept for the next compressedUpdate*/ - realDictSize = LZ4F_maxDictSize(); - } + int const realDictSize = LZ4F_localSaveDict(cctxPtr); cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize; assert((cctxPtr->tmpIn + blockSize) <= (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)); } /* some input data left, necessarily < blockSize */ if (srcPtr < srcEnd) { - /* fill tmp buffer */ - size_t const sizeToCopy = (size_t)(srcEnd - srcPtr); - memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy); - cctxPtr->tmpInSize = sizeToCopy; + /* fill tmp buffer */ + size_t const sizeToCopy = (size_t)(srcEnd - srcPtr); + memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy); + cctxPtr->tmpInSize = sizeToCopy; } if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled) - (void)XXH32_update(&(cctxPtr->xxh), srcBuffer, srcSize); + (void)XXH32_update(&(cctxPtr->xxh), srcBuffer, srcSize); cctxPtr->totalInSize += srcSize; - return bytesWritten + (size_t)(dstPtr - dstStart); + return (size_t)(dstPtr - dstStart); } /*! LZ4F_compressUpdate() : @@ -1032,10 +1003,10 @@ size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, const void* srcBuffer, size_t srcSize, const LZ4F_compressOptions_t* compressOptionsPtr) { - return LZ4F_compressUpdateImpl(cctxPtr, - dstBuffer, dstCapacity, - srcBuffer, srcSize, - compressOptionsPtr, LZ4B_COMPRESSED); + return LZ4F_compressUpdateImpl(cctxPtr, + dstBuffer, dstCapacity, + srcBuffer, srcSize, + compressOptionsPtr, LZ4B_COMPRESSED); } /*! LZ4F_compressUpdate() : @@ -1044,6 +1015,7 @@ size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, * src data is either buffered or compressed into @dstBuffer. * If previously an uncompressed block was written, buffered data is flushed * before appending compressed data is continued. + * This is only supported when LZ4F_blockIndependent is used * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr). * @compressOptionsPtr is optional : provide NULL to mean "default". * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. @@ -1051,13 +1023,14 @@ size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, * After an error, the state is left in a UB state, and must be re-initialized. */ size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr, - void* dstBuffer, size_t dstCapacity, - const void* srcBuffer, size_t srcSize, - const LZ4F_compressOptions_t* compressOptionsPtr) { - return LZ4F_compressUpdateImpl(cctxPtr, - dstBuffer, dstCapacity, - srcBuffer, srcSize, - compressOptionsPtr, LZ4B_UNCOMPRESSED); + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* compressOptionsPtr) { + assert(cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockIndependent); + return LZ4F_compressUpdateImpl(cctxPtr, + dstBuffer, dstCapacity, + srcBuffer, srcSize, + compressOptionsPtr, LZ4B_UNCOMPRESSED); } @@ -1090,7 +1063,8 @@ size_t LZ4F_flush(LZ4F_cctx* cctxPtr, cctxPtr->tmpIn, cctxPtr->tmpInSize, compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, cctxPtr->cdict, - cctxPtr->prefs.frameInfo.blockChecksumFlag, cctxPtr->blockCompression); + cctxPtr->prefs.frameInfo.blockChecksumFlag, + cctxPtr->blockCompression); assert(((void)"flush overflows dstBuffer!", (size_t)(dstPtr - dstStart) <= dstCapacity)); if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) diff --git a/lib/lz4frame.h b/lib/lz4frame.h index 7d81fa0..691ad50 100644 --- a/lib/lz4frame.h +++ b/lib/lz4frame.h @@ -552,6 +552,7 @@ LZ4FLIB_STATIC_API size_t LZ4F_getBlockSize(unsigned); * After an error, the state is left in a UB state, and must be re-initialized or freed. * If previously a compressed block was written, buffered data is flushed * before appending uncompressed data is continued. + * This is only supported when LZ4F_blockIndependent is used * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). * or an error code if it fails (which can be tested using LZ4F_isError()) diff --git a/lib/lz4hc.c b/lib/lz4hc.c index b5bc880..99650a6 100644 --- a/lib/lz4hc.c +++ b/lib/lz4hc.c @@ -1154,26 +1154,6 @@ int LZ4_compress_HC_continue_destSize (LZ4_streamHC_t* LZ4_streamHCPtr, const ch return LZ4_compressHC_continue_generic(LZ4_streamHCPtr, src, dst, srcSizePtr, targetDestSize, fillOutput); } -/*! LZ4_getDictHCSize(): - * Get the size of the dictionary. This can be used for adding data without - * compression to the LZ4 archive. If linked blocked mode is used the memory - * of the dictionary is kept free. - * This way uncompressed data does not influence the effectiveness of the - * dictionary. - * @param LZ4_dict Pointer to the dictionary to get the size of. - * @param dictSize The maximum dictionary size. (Normally 64 KB). - * @return The size of the dictionary. - */ -int LZ4_getDictHCSize(const LZ4_streamHC_t* LZ4_streamHCPtr, int dictSize) { - const LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse; - int const prefixSize = (int)(streamPtr->end - (streamPtr->base + streamPtr->dictLimit)); - DEBUGLOG(5, "LZ4_saveDictHC(%p, %p, %d)", LZ4_streamHCPtr, safeBuffer, dictSize); - assert(prefixSize >= 0); - if (dictSize > 64 KB) dictSize = 64 KB; - if (dictSize < 4) dictSize = 0; - if (dictSize > prefixSize) dictSize = prefixSize; - return dictSize; -} /* LZ4_saveDictHC : @@ -1184,7 +1164,12 @@ int LZ4_getDictHCSize(const LZ4_streamHC_t* LZ4_streamHCPtr, int dictSize) { int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize) { LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse; - dictSize = LZ4_getDictHCSize(LZ4_streamHCPtr, dictSize); + int const prefixSize = (int)(streamPtr->end - (streamPtr->base + streamPtr->dictLimit)); + DEBUGLOG(5, "LZ4_saveDictHC(%p, %p, %d)", LZ4_streamHCPtr, safeBuffer, dictSize); + assert(prefixSize >= 0); + if (dictSize > 64 KB) dictSize = 64 KB; + if (dictSize < 4) dictSize = 0; + if (dictSize > prefixSize) dictSize = prefixSize; if (safeBuffer == NULL) assert(dictSize == 0); if (dictSize > 0) memmove(safeBuffer, streamPtr->end - dictSize, dictSize); -- cgit v0.12 From 0ac3c74de1b6de584c361f3e9485dde35f10c756 Mon Sep 17 00:00:00 2001 From: Alexander Mohr
Date: Tue, 5 Jul 2022 20:52:50 +0200 Subject: review: fix findings * replace assert with test for LZ4F_uncompressedUpdate * update documentation to incldue correct docstring * remove unecessary entry point * remove compress_linked_block_mode from fuzzing test Signed-off-by: Alexander Mohr --- contrib/meson/meson/examples/meson.build | 2 +- contrib/meson/meson/lib/meson.build | 2 +- doc/lz4frame_manual.html | 1 + lib/lz4frame.c | 2 +- lib/lz4hc.h | 12 ------------ ossfuzz/round_trip_frame_uncompressed_fuzzer.c | 8 -------- 6 files changed, 4 insertions(+), 23 deletions(-) diff --git a/contrib/meson/meson/examples/meson.build b/contrib/meson/meson/examples/meson.build index f6b6c41..65f54ca 100644 --- a/contrib/meson/meson/examples/meson.build +++ b/contrib/meson/meson/examples/meson.build @@ -26,7 +26,7 @@ foreach e, src : examples executable( e, lz4_source_root / 'examples' / src, - dependencies: [liblz4_dep, liblz4_internal_dep], + dependencies: [liblz4_internal_dep], install: false ) endforeach diff --git a/contrib/meson/meson/lib/meson.build b/contrib/meson/meson/lib/meson.build index 2ff294d..469cd09 100644 --- a/contrib/meson/meson/lib/meson.build +++ b/contrib/meson/meson/lib/meson.build @@ -43,7 +43,7 @@ liblz4_dep = declare_dependency( include_directories: include_directories(lz4_source_root / 'lib') ) -if get_option('tests') or get_option('programs') +if get_option('tests') or get_option('programs') or get_option('examples') liblz4_internal = static_library( 'lz4-internal', objects: liblz4.extract_all_objects(recursive: true), diff --git a/doc/lz4frame_manual.html b/doc/lz4frame_manual.html index c55c6e9..0ae5150 100644 --- a/doc/lz4frame_manual.html +++ b/doc/lz4frame_manual.html @@ -355,6 +355,7 @@ LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx); After an error, the state is left in a UB state, and must be re-initialized or freed. If previously a compressed block was written, buffered data is flushed before appending uncompressed data is continued. + This is only supported when LZ4F_blockIndependent is used `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). or an error code if it fails (which can be tested using LZ4F_isError()) diff --git a/lib/lz4frame.c b/lib/lz4frame.c index 3042d6f..80e244b 100644 --- a/lib/lz4frame.c +++ b/lib/lz4frame.c @@ -1026,7 +1026,7 @@ size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr, void* dstBuffer, size_t dstCapacity, const void* srcBuffer, size_t srcSize, const LZ4F_compressOptions_t* compressOptionsPtr) { - assert(cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockIndependent); + RETURN_ERROR_IF(cctxPtr->prefs.frameInfo.blockMode != LZ4F_blockIndependent, blockMode_invalid); return LZ4F_compressUpdateImpl(cctxPtr, dstBuffer, dstCapacity, srcBuffer, srcSize, diff --git a/lib/lz4hc.h b/lib/lz4hc.h index 3b31624..f4afc9b 100644 --- a/lib/lz4hc.h +++ b/lib/lz4hc.h @@ -405,18 +405,6 @@ LZ4LIB_STATIC_API void LZ4_attach_HC_dictionary( LZ4_streamHC_t *working_stream, const LZ4_streamHC_t *dictionary_stream); -/*! LZ4_getDictHCSize(): - * Get the size of the dictionary. This can be used for adding data without - * compression to the LZ4 archive. If linked blocked mode is used the memory - * of the dictionary is kept free. - * This way uncompressed data does not influence the effectiveness of the - * dictionary. - * @param LZ4_dict Pointer to the dictionary to get the size of. - * @param dictSize The maximum dictionary size. (Normally 64 KB). - * @return The size of the dictionary. - */ -LZ4LIB_STATIC_API int LZ4_getDictHCSize(const LZ4_streamHC_t* LZ4_streamHCPtr, int dictSize); - #if defined (__cplusplus) } #endif diff --git a/ossfuzz/round_trip_frame_uncompressed_fuzzer.c b/ossfuzz/round_trip_frame_uncompressed_fuzzer.c index cf9cbb9..e578052 100644 --- a/ossfuzz/round_trip_frame_uncompressed_fuzzer.c +++ b/ossfuzz/round_trip_frame_uncompressed_fuzzer.c @@ -124,13 +124,6 @@ static void compress_round_trip(const uint8_t* data, size_t size, LZ4F_freeCompressionContext(ctx); } -static void compress_linked_block_mode(const uint8_t* data, size_t size) { - FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(data, size); - LZ4F_preferences_t prefs = FUZZ_dataProducer_preferences(producer); - prefs.frameInfo.blockMode = LZ4F_blockLinked; - compress_round_trip(data, size, producer, prefs); -} - static void compress_independent_block_mode(const uint8_t* data, size_t size) { FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(data, size); LZ4F_preferences_t prefs = FUZZ_dataProducer_preferences(producer); @@ -140,7 +133,6 @@ static void compress_independent_block_mode(const uint8_t* data, size_t size) { int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - compress_linked_block_mode(data, size); compress_independent_block_mode(data, size); return 0; } -- cgit v0.12