From e968a241293fe86c0f8b115a1223d9d78a0eda00 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 6 Nov 2020 14:46:48 -0800 Subject: unified alignment test across lz4.c and lz4hc.c --- lib/lz4.c | 33 +++++++++++++++++++------- lib/lz4hc.c | 24 +++++-------------- tests/fuzzer.c | 74 ++++++++++++++++++++++++++++++++++++---------------------- 3 files changed, 76 insertions(+), 55 deletions(-) diff --git a/lib/lz4.c b/lib/lz4.c index 0290ea2..9e6abba 100644 --- a/lib/lz4.c +++ b/lib/lz4.c @@ -178,6 +178,20 @@ #define unlikely(expr) expect((expr) != 0, 0) #endif +/* for some reason, Visual Studio can fail the aligment test on 32-bit x86 : + * it sometimes report an aligment of 8-bytes (at least in some configurations), + * while only providing a `malloc()` memory area aligned on 4-bytes, + * which is inconsistent with malloc() contract. + * The source of the issue is still unclear. + * Mitigation : made the alignment test optional */ +#ifndef LZ4_ALIGN_TEST /* can be externally provided */ +# if (defined(_MSC_VER) && !defined(_M_X64)) +# define LZ4_ALIGN_TEST 0 /* disable on win32+visual */ +# else +# define LZ4_ALIGN_TEST 1 +# endif +#endif + /*-************************************ * Memory routines @@ -243,6 +257,11 @@ static const int LZ4_minLength = (MFLIMIT+1); # define DEBUGLOG(l, ...) {} /* disabled */ #endif +static int LZ4_isAligned(const void* ptr, size_t alignment) +{ + return ((size_t)ptr & (alignment -1)) == 0; +} + /*-************************************ * Types @@ -1406,26 +1425,22 @@ LZ4_stream_t* LZ4_createStream(void) return lz4s; } -#ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 : - it reports an aligment of 8-bytes, - while actually aligning LZ4_stream_t on 4 bytes. */ static size_t LZ4_stream_t_alignment(void) { +#if LZ4_ALIGN_TEST typedef struct { char c; LZ4_stream_t t; } t_a; return sizeof(t_a) - sizeof(LZ4_stream_t); -} +#else + return 1; /* effectively disabled */ #endif +} LZ4_stream_t* LZ4_initStream (void* buffer, size_t size) { DEBUGLOG(5, "LZ4_initStream"); if (buffer == NULL) { return NULL; } if (size < sizeof(LZ4_stream_t)) { return NULL; } -#ifndef _MSC_VER /* for some reason, Visual fails the aligment test on 32-bit x86 : - it reports an aligment of 8-bytes, - while actually aligning LZ4_stream_t on 4 bytes. */ - if (((size_t)buffer) & (LZ4_stream_t_alignment() - 1)) { return NULL; } /* alignment check */ -#endif + if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL; MEM_INIT(buffer, 0, sizeof(LZ4_stream_t)); return (LZ4_stream_t*)buffer; } diff --git a/lib/lz4hc.c b/lib/lz4hc.c index cf03eba..29b6073 100644 --- a/lib/lz4hc.c +++ b/lib/lz4hc.c @@ -75,15 +75,6 @@ typedef enum { noDictCtx, usingDictCtxHc } dictCtx_directive; #define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH) #define LZ4_OPT_NUM (1<<12) -/* for some reason, Visual Studio fails the aligment test on 32-bit x86 : - * it reports an aligment of 8-bytes, - * while LZ4_streamHC_t only requires alignment of 4-bytes - * resulting in initialization error when allocating state with malloc() */ -#if (defined(_MSC_VER) && !defined(_M_X64)) -# define LZ4_ALIGN_TEST 0 -#else -# define LZ4_ALIGN_TEST 1 -#endif /*=== Macros ===*/ #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) @@ -921,23 +912,22 @@ LZ4HC_compress_generic ( int LZ4_sizeofStateHC(void) { return (int)sizeof(LZ4_streamHC_t); } -#if LZ4_ALIGN_TEST static size_t LZ4_streamHC_t_alignment(void) { +#if LZ4_ALIGN_TEST typedef struct { char c; LZ4_streamHC_t t; } t_a; return sizeof(t_a) - sizeof(LZ4_streamHC_t); -} +#else + return 1; /* effectively disabled */ #endif +} /* state is presumed correctly initialized, * in which case its size and alignment have already been validate */ int LZ4_compress_HC_extStateHC_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) { LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)state)->internal_donotuse; -#if LZ4_ALIGN_TEST - assert(((size_t)state & (LZ4_streamHC_t_alignment() - 1)) == 0); /* check alignment */ -#endif - if (((size_t)(state)&(sizeof(void*)-1)) != 0) return 0; /* Error : state is not aligned for pointers (32 or 64 bits) */ + if (!LZ4_isAligned(state, LZ4_streamHC_t_alignment())) return 0; LZ4_resetStreamHC_fast((LZ4_streamHC_t*)state, compressionLevel); LZ4HC_init_internal (ctx, (const BYTE*)src); if (dstCapacity < LZ4_compressBound(srcSize)) @@ -1008,9 +998,7 @@ LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size) LZ4_streamHC_t* const LZ4_streamHCPtr = (LZ4_streamHC_t*)buffer; if (buffer == NULL) return NULL; if (size < sizeof(LZ4_streamHC_t)) return NULL; -#if LZ4_ALIGN_TEST - if (((size_t)buffer) & (LZ4_streamHC_t_alignment() - 1)) return NULL; /* alignment check */ -#endif + if (!LZ4_isAligned(buffer, LZ4_streamHC_t_alignment())) return NULL; /* if compilation fails here, LZ4_STREAMHCSIZE must be increased */ LZ4_STATIC_ASSERT(sizeof(LZ4HC_CCtx_internal) <= LZ4_STREAMHCSIZE); DEBUGLOG(4, "LZ4_initStreamHC(%p, %u)", LZ4_streamHCPtr, (unsigned)size); diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 84f9693..29c6a8a 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -1125,27 +1125,47 @@ static void FUZ_unitTests(int compressionLevel) } } } } } DISPLAYLEVEL(3, " OK \n"); - /* LZ4 streaming tests */ - { LZ4_stream_t streamingState; - U64 crcOrig; - int result; + DISPLAYLEVEL(3, "LZ4_initStream with multiple valid alignments : "); + { typedef struct { + LZ4_stream_t state1; + LZ4_stream_t state2; + char c; + LZ4_stream_t state3; + } shct; + shct* const shc = (shct*)malloc(sizeof(*shc)); + assert(shc != NULL); + memset(shc, 0, sizeof(*shc)); + DISPLAYLEVEL(3, "state1(%p) state2(%p) state3(%p) size(0x%x): ", + &(shc->state1), &(shc->state2), &(shc->state3), (unsigned)sizeof(LZ4_stream_t)); + FUZ_CHECKTEST( LZ4_initStream(&(shc->state1), sizeof(shc->state1)) == NULL, "state1 (%p) failed init", &(shc->state1) ); + FUZ_CHECKTEST( LZ4_initStream(&(shc->state2), sizeof(shc->state2)) == NULL, "state2 (%p) failed init", &(shc->state2) ); + FUZ_CHECKTEST( LZ4_initStream(&(shc->state3), sizeof(shc->state3)) == NULL, "state3 (%p) failed init", &(shc->state3) ); + FUZ_CHECKTEST( LZ4_initStream((char*)&(shc->state1) + 1, sizeof(shc->state1)) != NULL, + "hc1+1 (%p) init must fail, due to bad alignment", (char*)&(shc->state1) + 1 ); + free(shc); + } + DISPLAYLEVEL(3, "all inits OK \n"); - /* Allocation test */ - { LZ4_stream_t* const statePtr = LZ4_createStream(); - FUZ_CHECKTEST(statePtr==NULL, "LZ4_createStream() allocation failed"); - LZ4_freeStream(statePtr); - } + /* Allocation test */ + { LZ4_stream_t* const statePtr = LZ4_createStream(); + FUZ_CHECKTEST(statePtr==NULL, "LZ4_createStream() allocation failed"); + LZ4_freeStream(statePtr); + } - /* simple compression test */ - crcOrig = XXH64(testInput, testCompressedSize, 0); - LZ4_initStream(&streamingState, sizeof(streamingState)); - result = LZ4_compress_fast_continue(&streamingState, testInput, testCompressed, testCompressedSize, testCompressedSize-1, 1); - FUZ_CHECKTEST(result==0, "LZ4_compress_fast_continue() compression failed!"); + /* LZ4 streaming tests */ + { LZ4_stream_t streamingState; - result = LZ4_decompress_safe(testCompressed, testVerify, result, testCompressedSize); - FUZ_CHECKTEST(result!=(int)testCompressedSize, "LZ4_decompress_safe() decompression failed"); - { U64 const crcNew = XXH64(testVerify, testCompressedSize, 0); - FUZ_CHECKTEST(crcOrig!=crcNew, "LZ4_decompress_safe() decompression corruption"); } + /* simple compression test */ + { U64 const crcOrig = XXH64(testInput, testCompressedSize, 0); + LZ4_initStream(&streamingState, sizeof(streamingState)); + { int const cs = LZ4_compress_fast_continue(&streamingState, testInput, testCompressed, testCompressedSize, testCompressedSize-1, 1); + FUZ_CHECKTEST(cs==0, "LZ4_compress_fast_continue() compression failed!"); + { int const r = LZ4_decompress_safe(testCompressed, testVerify, cs, testCompressedSize); + FUZ_CHECKTEST(r!=(int)testCompressedSize, "LZ4_decompress_safe() decompression failed"); + } } + { U64 const crcNew = XXH64(testVerify, testCompressedSize, 0); + FUZ_CHECKTEST(crcOrig!=crcNew, "LZ4_decompress_safe() decompression corruption"); + } } /* ring buffer test */ { XXH64_state_t xxhOrig; @@ -1167,7 +1187,7 @@ static void FUZ_unitTests(int compressionLevel) LZ4_setStreamDecode(&decodeStateFast, NULL, 0); while (iNext + messageSize < testCompressedSize) { - int compressedSize; + int compressedSize; U64 crcOrig; XXH64_update(&xxhOrig, testInput + iNext, messageSize); crcOrig = XXH64_digest(&xxhOrig); @@ -1175,15 +1195,15 @@ static void FUZ_unitTests(int compressionLevel) compressedSize = LZ4_compress_fast_continue(&streamingState, ringBuffer + rNext, testCompressed, (int)messageSize, testCompressedSize-ringBufferSize, 1); FUZ_CHECKTEST(compressedSize==0, "LZ4_compress_fast_continue() compression failed"); - result = LZ4_decompress_safe_continue(&decodeStateSafe, testCompressed, testVerify + dNext, compressedSize, (int)messageSize); - FUZ_CHECKTEST(result!=(int)messageSize, "ringBuffer : LZ4_decompress_safe_continue() test failed"); + { int const r = LZ4_decompress_safe_continue(&decodeStateSafe, testCompressed, testVerify + dNext, compressedSize, (int)messageSize); + FUZ_CHECKTEST(r!=(int)messageSize, "ringBuffer : LZ4_decompress_safe_continue() test failed"); } XXH64_update(&xxhNewSafe, testVerify + dNext, messageSize); { U64 const crcNew = XXH64_digest(&xxhNewSafe); FUZ_CHECKTEST(crcOrig!=crcNew, "LZ4_decompress_safe_continue() decompression corruption"); } - result = LZ4_decompress_fast_continue(&decodeStateFast, testCompressed, testVerify + dNext, (int)messageSize); - FUZ_CHECKTEST(result!=compressedSize, "ringBuffer : LZ4_decompress_fast_continue() test failed"); + { int const r = LZ4_decompress_fast_continue(&decodeStateFast, testCompressed, testVerify + dNext, (int)messageSize); + FUZ_CHECKTEST(r!=compressedSize, "ringBuffer : LZ4_decompress_fast_continue() test failed"); } XXH64_update(&xxhNewFast, testVerify + dNext, messageSize); { U64 const crcNew = XXH64_digest(&xxhNewFast); @@ -1196,16 +1216,14 @@ static void FUZ_unitTests(int compressionLevel) messageSize = (FUZ_rand(&randState) & maxMessageSizeMask) + 1; if (rNext + messageSize > ringBufferSize) rNext = 0; if (dNext + messageSize > dBufferSize) dNext = 0; - } - } + } } } DISPLAYLEVEL(3, "LZ4_initStreamHC with multiple valid alignments : "); { typedef struct { LZ4_streamHC_t hc1; - char c1; LZ4_streamHC_t hc2; - char c2; + char c; LZ4_streamHC_t hc3; } shct; shct* const shc = (shct*)malloc(sizeof(*shc)); @@ -1220,7 +1238,7 @@ static void FUZ_unitTests(int compressionLevel) "hc1+1 (%p) init must fail, due to bad alignment", (char*)&(shc->hc1) + 1 ); free(shc); } - DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "all inits OK \n"); /* LZ4 HC streaming tests */ { LZ4_streamHC_t sHC; /* statically allocated */ -- cgit v0.12