unified alignment test
authorYann Collet <cyan@fb.com>
Fri, 6 Nov 2020 22:46:48 +0000 (14:46 -0800)
committerYann Collet <cyan@fb.com>
Fri, 6 Nov 2020 22:46:48 +0000 (14:46 -0800)
across lz4.c and lz4hc.c

lib/lz4.c
lib/lz4hc.c
tests/fuzzer.c

index 0290ea27435e3ee8532cfa3991de4b3c2cc35dd5..9e6abbaacac7ced4155609b2bf31220850ec55ff 100644 (file)
--- a/lib/lz4.c
+++ b/lib/lz4.c
 #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;
 }
index cf03eba3cec7df2e761ecc188162cc706acfa4eb..29b60731b61cce78f01181e43236fcfb900ef6d7 100644 (file)
@@ -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);
index 84f969372afc6c078e1343453b23d41a45fd2b63..29c6a8a0f49a579b3646f3841e117510ad7da86a 100644 (file)
@@ -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 */