1 /*
2     frameTest - test tool for lz4frame
3     Copyright (C) Yann Collet 2014-2020
4 
5     GPL v2 License
6 
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License along
18     with this program; if not, write to the Free Software Foundation, Inc.,
19     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 
21     You can contact the author at :
22     - LZ4 homepage : http://www.lz4.org
23     - LZ4 source repository : https://github.com/lz4/lz4
24 */
25 
26 /*-************************************
27 *  Compiler specific
28 **************************************/
29 #ifdef _MSC_VER    /* Visual Studio */
30 #  pragma warning(disable : 26451)     /* disable: Arithmetic overflow */
31 #endif
32 
33 
34 /*-************************************
35 *  Includes
36 **************************************/
37 #include "util.h"       /* U32 */
38 #include <stdlib.h>     /* malloc, free */
39 #include <stdio.h>      /* fprintf */
40 #include <string.h>     /* strcmp */
41 #include <time.h>       /* clock_t, clock(), CLOCKS_PER_SEC */
42 #include <assert.h>
43 #include "lz4frame.h"   /* included multiple times to test correctness/safety */
44 #include "lz4frame.h"
45 #define LZ4F_STATIC_LINKING_ONLY
46 #include "lz4frame.h"
47 #include "lz4frame.h"
48 #define LZ4_STATIC_LINKING_ONLY  /* LZ4_DISTANCE_MAX */
49 #include "lz4.h"        /* LZ4_VERSION_STRING */
50 #define XXH_STATIC_LINKING_ONLY
51 #include "xxhash.h"     /* XXH64 */
52 
53 
54 /* unoptimized version; solves endianess & alignment issues */
FUZ_writeLE32(void * dstVoidPtr,U32 value32)55 static void FUZ_writeLE32 (void* dstVoidPtr, U32 value32)
56 {
57     BYTE* dstPtr = (BYTE*)dstVoidPtr;
58     dstPtr[0] = (BYTE) value32;
59     dstPtr[1] = (BYTE)(value32 >> 8);
60     dstPtr[2] = (BYTE)(value32 >> 16);
61     dstPtr[3] = (BYTE)(value32 >> 24);
62 }
63 
64 
65 /*-************************************
66 *  Constants
67 **************************************/
68 #define LZ4F_MAGIC_SKIPPABLE_START 0x184D2A50U
69 
70 #define KB *(1U<<10)
71 #define MB *(1U<<20)
72 #define GB *(1U<<30)
73 
74 static const U32 nbTestsDefault = 256 KB;
75 #define FUZ_COMPRESSIBILITY_DEFAULT 50
76 static const U32 prime1 = 2654435761U;
77 static const U32 prime2 = 2246822519U;
78 
79 
80 /*-************************************
81 *  Macros
82 **************************************/
83 #define DISPLAY(...)          fprintf(stderr, __VA_ARGS__)
84 #define DISPLAYLEVEL(l, ...)  if (displayLevel>=l) { DISPLAY(__VA_ARGS__); }
85 #define DISPLAYUPDATE(l, ...) if (displayLevel>=l) { \
86             if ((FUZ_GetClockSpan(g_clockTime) > refreshRate) || (displayLevel>=4)) \
87             { g_clockTime = clock(); DISPLAY(__VA_ARGS__); \
88             if (displayLevel>=4) fflush(stdout); } }
89 static const clock_t refreshRate = CLOCKS_PER_SEC / 6;
90 static clock_t g_clockTime = 0;
91 
92 
93 /*-***************************************
94 *  Local Parameters
95 *****************************************/
96 static U32 no_prompt = 0;
97 static U32 displayLevel = 2;
98 static U32 use_pause = 0;
99 
100 
101 /*-*******************************************************
102 *  Fuzzer functions
103 *********************************************************/
104 #define MIN(a,b)  ( (a) < (b) ? (a) : (b) )
105 #define MAX(a,b)  ( (a) > (b) ? (a) : (b) )
106 
FUZ_GetClockSpan(clock_t clockStart)107 static clock_t FUZ_GetClockSpan(clock_t clockStart)
108 {
109     return clock() - clockStart;   /* works even if overflow; max span ~ 30 mn */
110 }
111 
112 
113 #define FUZ_rotl32(x,r) ((x << r) | (x >> (32 - r)))
FUZ_rand(unsigned int * src)114 unsigned int FUZ_rand(unsigned int* src)
115 {
116     U32 rand32 = *src;
117     rand32 *= prime1;
118     rand32 += prime2;
119     rand32  = FUZ_rotl32(rand32, 13);
120     *src = rand32;
121     return rand32 >> 5;
122 }
123 
124 
125 #define FUZ_RAND15BITS  (FUZ_rand(seed) & 0x7FFF)
126 #define FUZ_RANDLENGTH  ( (FUZ_rand(seed) & 3) ? (FUZ_rand(seed) % 15) : (FUZ_rand(seed) % 510) + 15)
FUZ_fillCompressibleNoiseBuffer(void * buffer,size_t bufferSize,double proba,U32 * seed)127 static void FUZ_fillCompressibleNoiseBuffer(void* buffer, size_t bufferSize, double proba, U32* seed)
128 {
129     BYTE* BBuffer = (BYTE*)buffer;
130     size_t pos = 0;
131     U32 P32 = (U32)(32768 * proba);
132 
133     /* First Byte */
134     BBuffer[pos++] = (BYTE)(FUZ_rand(seed));
135 
136     while (pos < bufferSize) {
137         /* Select : Literal (noise) or copy (within 64K) */
138         if (FUZ_RAND15BITS < P32) {
139             /* Copy (within 64K) */
140             size_t const lengthRand = FUZ_RANDLENGTH + 4;
141             size_t const length = MIN(lengthRand, bufferSize - pos);
142             size_t const end = pos + length;
143             size_t const offsetRand = FUZ_RAND15BITS + 1;
144             size_t const offset = MIN(offsetRand, pos);
145             size_t match = pos - offset;
146             while (pos < end) BBuffer[pos++] = BBuffer[match++];
147         } else {
148             /* Literal (noise) */
149             size_t const lengthRand = FUZ_RANDLENGTH + 4;
150             size_t const length = MIN(lengthRand, bufferSize - pos);
151             size_t const end = pos + length;
152             while (pos < end) BBuffer[pos++] = (BYTE)(FUZ_rand(seed) >> 5);
153     }   }
154 }
155 
156 
FUZ_highbit(U32 v32)157 static unsigned FUZ_highbit(U32 v32)
158 {
159     unsigned nbBits = 0;
160     if (v32==0) return 0;
161     while (v32) {v32 >>= 1; nbBits ++;}
162     return nbBits;
163 }
164 
165 
166 /*-*******************************************************
167 *  Tests
168 *********************************************************/
169 #define CHECK_V(v,f) v = f; if (LZ4F_isError(v)) { fprintf(stderr, "%s \n", LZ4F_getErrorName(v)); goto _output_error; }
170 #define CHECK(f)   { LZ4F_errorCode_t const CHECK_V(err_ , f); }
171 
basicTests(U32 seed,double compressibility)172 int basicTests(U32 seed, double compressibility)
173 {
174 #define COMPRESSIBLE_NOISE_LENGTH (2 MB)
175     void* const CNBuffer = malloc(COMPRESSIBLE_NOISE_LENGTH);
176     size_t const cBuffSize = LZ4F_compressFrameBound(COMPRESSIBLE_NOISE_LENGTH, NULL);
177     void* const compressedBuffer = malloc(cBuffSize);
178     void* const decodedBuffer = malloc(COMPRESSIBLE_NOISE_LENGTH);
179     U32 randState = seed;
180     size_t cSize, testSize;
181     LZ4F_decompressionContext_t dCtx = NULL;
182     LZ4F_compressionContext_t cctx = NULL;
183     U64 crcOrig;
184     int basicTests_error = 0;
185     LZ4F_preferences_t prefs;
186     memset(&prefs, 0, sizeof(prefs));
187 
188     if (!CNBuffer || !compressedBuffer || !decodedBuffer) {
189         DISPLAY("allocation error, not enough memory to start fuzzer tests \n");
190         goto _output_error;
191     }
192     FUZ_fillCompressibleNoiseBuffer(CNBuffer, COMPRESSIBLE_NOISE_LENGTH, compressibility, &randState);
193     crcOrig = XXH64(CNBuffer, COMPRESSIBLE_NOISE_LENGTH, 1);
194 
195     /* LZ4F_compressBound() : special case : srcSize == 0 */
196     DISPLAYLEVEL(3, "LZ4F_compressBound(0) = ");
197     {   size_t const cBound = LZ4F_compressBound(0, NULL);
198         if (cBound < 64 KB) goto _output_error;
199         DISPLAYLEVEL(3, " %u \n", (U32)cBound);
200     }
201 
202     /* LZ4F_compressBound() : special case : automatic flushing enabled */
203     DISPLAYLEVEL(3, "LZ4F_compressBound(1 KB, autoFlush=1) = ");
204     {   size_t cBound;
205         LZ4F_preferences_t autoFlushPrefs;
206         memset(&autoFlushPrefs, 0, sizeof(autoFlushPrefs));
207         autoFlushPrefs.autoFlush = 1;
208         cBound = LZ4F_compressBound(1 KB, &autoFlushPrefs);
209         if (cBound > 64 KB) goto _output_error;
210         DISPLAYLEVEL(3, " %u \n", (U32)cBound);
211     }
212 
213     /* LZ4F_compressBound() : special case : automatic flushing disabled */
214     DISPLAYLEVEL(3, "LZ4F_compressBound(1 KB, autoFlush=0) = ");
215     {   size_t const cBound = LZ4F_compressBound(1 KB, &prefs);
216         if (cBound < 64 KB) goto _output_error;
217         DISPLAYLEVEL(3, " %u \n", (U32)cBound);
218     }
219 
220     /* Special case : null-content frame */
221     testSize = 0;
222     DISPLAYLEVEL(3, "LZ4F_compressFrame, compress null content : ");
223     CHECK_V(cSize, LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, NULL), CNBuffer, testSize, NULL));
224     DISPLAYLEVEL(3, "null content encoded into a %u bytes frame \n", (unsigned)cSize);
225 
226     DISPLAYLEVEL(3, "LZ4F_createDecompressionContext \n");
227     CHECK ( LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION) );
228 
229     DISPLAYLEVEL(3, "LZ4F_getFrameInfo on null-content frame (#157) \n");
230     assert(cSize >= LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH);
231     {   LZ4F_frameInfo_t frame_info;
232         size_t const fhs = LZ4F_headerSize(compressedBuffer, LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH);
233         size_t avail_in = fhs;
234         CHECK( fhs );
235         CHECK( LZ4F_getFrameInfo(dCtx, &frame_info, compressedBuffer, &avail_in) );
236         if (avail_in != fhs) goto _output_error;  /* must consume all, since header size is supposed to be exact */
237     }
238 
239     DISPLAYLEVEL(3, "LZ4F_freeDecompressionContext \n");
240     CHECK( LZ4F_freeDecompressionContext(dCtx) );
241     dCtx = NULL;
242 
243     /* test one-pass frame compression */
244     testSize = COMPRESSIBLE_NOISE_LENGTH;
245 
246     DISPLAYLEVEL(3, "LZ4F_compressFrame, using fast level -3 : ");
247     {   LZ4F_preferences_t fastCompressPrefs;
248         memset(&fastCompressPrefs, 0, sizeof(fastCompressPrefs));
249         fastCompressPrefs.compressionLevel = -3;
250         CHECK_V(cSize, LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, NULL), CNBuffer, testSize, &fastCompressPrefs));
251         DISPLAYLEVEL(3, "Compressed %u bytes into a %u bytes frame \n", (U32)testSize, (U32)cSize);
252     }
253 
254     DISPLAYLEVEL(3, "LZ4F_compressFrame, using default preferences : ");
255     CHECK_V(cSize, LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, NULL), CNBuffer, testSize, NULL));
256     DISPLAYLEVEL(3, "Compressed %u bytes into a %u bytes frame \n", (U32)testSize, (U32)cSize);
257 
258     DISPLAYLEVEL(3, "Decompression test : \n");
259     {   size_t decodedBufferSize = COMPRESSIBLE_NOISE_LENGTH;
260         size_t compressedBufferSize = cSize;
261 
262         CHECK( LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION) );
263 
264         DISPLAYLEVEL(3, "Single Pass decompression : ");
265         CHECK( LZ4F_decompress(dCtx, decodedBuffer, &decodedBufferSize, compressedBuffer, &compressedBufferSize, NULL) );
266         { U64 const crcDest = XXH64(decodedBuffer, decodedBufferSize, 1);
267           if (crcDest != crcOrig) goto _output_error; }
268         DISPLAYLEVEL(3, "Regenerated %u bytes \n", (U32)decodedBufferSize);
269 
270         DISPLAYLEVEL(3, "Reusing decompression context \n");
271         {   size_t const missingBytes = 4;
272             size_t iSize = compressedBufferSize - missingBytes;
273             const BYTE* cBuff = (const BYTE*) compressedBuffer;
274             BYTE* const ostart = (BYTE*)decodedBuffer;
275             BYTE* op = ostart;
276             BYTE* const oend = (BYTE*)decodedBuffer + COMPRESSIBLE_NOISE_LENGTH;
277             size_t decResult, oSize = COMPRESSIBLE_NOISE_LENGTH;
278             DISPLAYLEVEL(3, "Missing last %u bytes : ", (U32)missingBytes);
279             CHECK_V(decResult, LZ4F_decompress(dCtx, op, &oSize, cBuff, &iSize, NULL));
280             if (decResult != missingBytes) {
281                 DISPLAY("%u bytes missing != %u bytes requested \n", (U32)missingBytes, (U32)decResult);
282                 goto _output_error;
283             }
284             DISPLAYLEVEL(3, "indeed, requests %u bytes \n", (unsigned)decResult);
285             cBuff += iSize;
286             iSize = decResult;
287             op += oSize;
288             oSize = (size_t)(oend-op);
289             decResult = LZ4F_decompress(dCtx, op, &oSize, cBuff, &iSize, NULL);
290             if (decResult != 0) goto _output_error;   /* should finish now */
291             op += oSize;
292             if (op>oend) { DISPLAY("decompression write overflow \n"); goto _output_error; }
293             {   U64 const crcDest = XXH64(decodedBuffer, (size_t)(op-ostart), 1);
294                 if (crcDest != crcOrig) goto _output_error;
295         }   }
296 
297         {   size_t oSize = 0;
298             size_t iSize = 0;
299             LZ4F_frameInfo_t fi;
300             const BYTE* ip = (BYTE*)compressedBuffer;
301 
302             DISPLAYLEVEL(3, "Start by feeding 0 bytes, to get next input size : ");
303             CHECK( LZ4F_decompress(dCtx, NULL, &oSize, ip, &iSize, NULL) );
304             //DISPLAYLEVEL(3, " %u  \n", (unsigned)errorCode);
305             DISPLAYLEVEL(3, " OK  \n");
306 
307             DISPLAYLEVEL(3, "LZ4F_getFrameInfo on zero-size input : ");
308             {   size_t nullSize = 0;
309                 size_t const fiError = LZ4F_getFrameInfo(dCtx, &fi, ip, &nullSize);
310                 if (LZ4F_getErrorCode(fiError) != LZ4F_ERROR_frameHeader_incomplete) {
311                     DISPLAYLEVEL(3, "incorrect error : %s != ERROR_frameHeader_incomplete \n",
312                                     LZ4F_getErrorName(fiError));
313                     goto _output_error;
314                 }
315                 DISPLAYLEVEL(3, " correctly failed : %s \n", LZ4F_getErrorName(fiError));
316             }
317 
318             DISPLAYLEVEL(3, "LZ4F_getFrameInfo on not enough input : ");
319             {   size_t inputSize = 6;
320                 size_t const fiError = LZ4F_getFrameInfo(dCtx, &fi, ip, &inputSize);
321                 if (LZ4F_getErrorCode(fiError) != LZ4F_ERROR_frameHeader_incomplete) {
322                     DISPLAYLEVEL(3, "incorrect error : %s != ERROR_frameHeader_incomplete \n", LZ4F_getErrorName(fiError));
323                     goto _output_error;
324                 }
325                 DISPLAYLEVEL(3, " correctly failed : %s \n", LZ4F_getErrorName(fiError));
326             }
327 
328             DISPLAYLEVEL(3, "LZ4F_getFrameInfo on enough input : ");
329             iSize = LZ4F_headerSize(ip, LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH);
330             CHECK( iSize );
331             CHECK( LZ4F_getFrameInfo(dCtx, &fi, ip, &iSize) );
332             DISPLAYLEVEL(3, " correctly decoded \n");
333         }
334 
335         DISPLAYLEVEL(3, "Decode a buggy input : ");
336         assert(COMPRESSIBLE_NOISE_LENGTH > 64);
337         assert(cSize > 48);
338         memcpy(decodedBuffer, (char*)compressedBuffer+16, 32);  /* save correct data */
339         memcpy((char*)compressedBuffer+16, (const char*)decodedBuffer+32, 32);  /* insert noise */
340         {   size_t dbSize = COMPRESSIBLE_NOISE_LENGTH;
341             size_t cbSize = cSize;
342             size_t const decompressError = LZ4F_decompress(dCtx, decodedBuffer, &dbSize,
343                                                                compressedBuffer, &cbSize,
344                                                                NULL);
345             if (!LZ4F_isError(decompressError)) goto _output_error;
346             DISPLAYLEVEL(3, "error detected : %s \n", LZ4F_getErrorName(decompressError));
347         }
348         memcpy((char*)compressedBuffer+16, decodedBuffer, 32);  /* restore correct data */
349 
350         DISPLAYLEVEL(3, "Reset decompression context, since it's left in error state \n");
351         LZ4F_resetDecompressionContext(dCtx);   /* always successful */
352 
353         DISPLAYLEVEL(3, "Byte after byte : ");
354         {   BYTE* const ostart = (BYTE*)decodedBuffer;
355             BYTE* op = ostart;
356             BYTE* const oend = (BYTE*)decodedBuffer + COMPRESSIBLE_NOISE_LENGTH;
357             const BYTE* ip = (const BYTE*) compressedBuffer;
358             const BYTE* const iend = ip + cSize;
359             while (ip < iend) {
360                 size_t oSize = (size_t)(oend-op);
361                 size_t iSize = 1;
362                 CHECK( LZ4F_decompress(dCtx, op, &oSize, ip, &iSize, NULL) );
363                 op += oSize;
364                 ip += iSize;
365             }
366             {   U64 const crcDest = XXH64(decodedBuffer, COMPRESSIBLE_NOISE_LENGTH, 1);
367                 if (crcDest != crcOrig) goto _output_error;
368             }
369             DISPLAYLEVEL(3, "Regenerated %u/%u bytes \n", (unsigned)(op-ostart), (unsigned)COMPRESSIBLE_NOISE_LENGTH);
370         }
371     }
372 
373     DISPLAYLEVEL(3, "Using 64 KB block : ");
374     prefs.frameInfo.blockSizeID = LZ4F_max64KB;
375     prefs.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled;
376     CHECK_V(cSize, LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs));
377     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
378 
379     DISPLAYLEVEL(3, "without checksum : ");
380     prefs.frameInfo.contentChecksumFlag = LZ4F_noContentChecksum;
381     CHECK_V(cSize, LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs));
382     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
383 
384     DISPLAYLEVEL(3, "Using 256 KB block : ");
385     prefs.frameInfo.blockSizeID = LZ4F_max256KB;
386     prefs.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled;
387     CHECK_V(cSize, LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs));
388     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
389 
390     DISPLAYLEVEL(3, "Decompression test : \n");
391     {   size_t const decodedBufferSize = COMPRESSIBLE_NOISE_LENGTH;
392         unsigned const maxBits = FUZ_highbit((U32)decodedBufferSize);
393         BYTE* const ostart = (BYTE*)decodedBuffer;
394         BYTE* op = ostart;
395         BYTE* const oend = ostart + COMPRESSIBLE_NOISE_LENGTH;
396         const BYTE* ip = (const BYTE*)compressedBuffer;
397         const BYTE* const iend = (const BYTE*)compressedBuffer + cSize;
398 
399         DISPLAYLEVEL(3, "random segment sizes : ");
400         while (ip < iend) {
401             unsigned const nbBits = FUZ_rand(&randState) % maxBits;
402             size_t iSize = (FUZ_rand(&randState) & ((1<<nbBits)-1)) + 1;
403             size_t oSize = (size_t)(oend-op);
404             if (iSize > (size_t)(iend-ip)) iSize = (size_t)(iend-ip);
405             CHECK( LZ4F_decompress(dCtx, op, &oSize, ip, &iSize, NULL) );
406             op += oSize;
407             ip += iSize;
408         }
409         {   size_t const decodedSize = (size_t)(op - ostart);
410             U64 const crcDest = XXH64(decodedBuffer, decodedSize, 1);
411             if (crcDest != crcOrig) goto _output_error;
412             DISPLAYLEVEL(3, "Regenerated %u bytes \n", (U32)decodedSize);
413          }
414 
415         CHECK( LZ4F_freeDecompressionContext(dCtx) );
416         dCtx = NULL;
417     }
418 
419     DISPLAYLEVEL(3, "without checksum : ");
420     prefs.frameInfo.contentChecksumFlag = LZ4F_noContentChecksum;
421     CHECK_V(cSize, LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs) );
422     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
423 
424     DISPLAYLEVEL(3, "Using 1 MB block : ");
425     prefs.frameInfo.blockSizeID = LZ4F_max1MB;
426     prefs.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled;
427     CHECK_V(cSize, LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs) );
428     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
429 
430     DISPLAYLEVEL(3, "without frame checksum : ");
431     prefs.frameInfo.contentChecksumFlag = LZ4F_noContentChecksum;
432     CHECK_V(cSize, LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs) );
433     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
434 
435     DISPLAYLEVEL(3, "Using 4 MB block : ");
436     prefs.frameInfo.blockSizeID = LZ4F_max4MB;
437     prefs.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled;
438     {   size_t const dstCapacity = LZ4F_compressFrameBound(testSize, &prefs);
439         DISPLAYLEVEL(4, "dstCapacity = %u  ; ", (U32)dstCapacity)
440         CHECK_V(cSize, LZ4F_compressFrame(compressedBuffer, dstCapacity, CNBuffer, testSize, &prefs) );
441         DISPLAYLEVEL(3, "Compressed %u bytes into a %u bytes frame \n", (U32)testSize, (U32)cSize);
442     }
443 
444     DISPLAYLEVEL(3, "without frame checksum : ");
445     prefs.frameInfo.contentChecksumFlag = LZ4F_noContentChecksum;
446     {   size_t const dstCapacity = LZ4F_compressFrameBound(testSize, &prefs);
447         DISPLAYLEVEL(4, "dstCapacity = %u  ; ", (U32)dstCapacity)
448         CHECK_V(cSize, LZ4F_compressFrame(compressedBuffer, dstCapacity, CNBuffer, testSize, &prefs) );
449         DISPLAYLEVEL(3, "Compressed %u bytes into a %u bytes frame \n", (U32)testSize, (U32)cSize);
450     }
451 
452     DISPLAYLEVEL(3, "LZ4F_compressFrame with block checksum : ");
453     memset(&prefs, 0, sizeof(prefs));
454     prefs.frameInfo.blockChecksumFlag = LZ4F_blockChecksumEnabled;
455     CHECK_V(cSize, LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(testSize, &prefs), CNBuffer, testSize, &prefs) );
456     DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)cSize);
457 
458     DISPLAYLEVEL(3, "Decompress with block checksum : ");
459     {   size_t iSize = cSize;
460         size_t decodedSize = COMPRESSIBLE_NOISE_LENGTH;
461         LZ4F_decompressionContext_t dctx;
462         CHECK( LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION) );
463         CHECK( LZ4F_decompress(dctx, decodedBuffer, &decodedSize, compressedBuffer, &iSize, NULL) );
464         if (decodedSize != testSize) goto _output_error;
465         if (iSize != cSize) goto _output_error;
466         {   U64 const crcDest = XXH64(decodedBuffer, decodedSize, 1);
467             U64 const crcSrc = XXH64(CNBuffer, testSize, 1);
468             if (crcDest != crcSrc) goto _output_error;
469         }
470         DISPLAYLEVEL(3, "Regenerated %u bytes \n", (U32)decodedSize);
471 
472         CHECK( LZ4F_freeDecompressionContext(dctx) );
473     }
474 
475     /* frame content size tests */
476     {   size_t cErr;
477         BYTE* const ostart = (BYTE*)compressedBuffer;
478         BYTE* op = ostart;
479         CHECK( LZ4F_createCompressionContext(&cctx, LZ4F_VERSION) );
480 
481         DISPLAYLEVEL(3, "compress without frameSize : ");
482         memset(&(prefs.frameInfo), 0, sizeof(prefs.frameInfo));
483         CHECK_V(cErr, LZ4F_compressBegin(cctx, compressedBuffer, testSize, &prefs));
484         op += cErr;
485         CHECK_V(cErr, LZ4F_compressUpdate(cctx, op, LZ4F_compressBound(testSize, &prefs), CNBuffer, testSize, NULL));
486         op += cErr;
487         CHECK( LZ4F_compressEnd(cctx, compressedBuffer, testSize, NULL) );
488         DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)(op-ostart));
489 
490         DISPLAYLEVEL(3, "compress with frameSize : ");
491         prefs.frameInfo.contentSize = testSize;
492         op = ostart;
493         CHECK_V(cErr, LZ4F_compressBegin(cctx, compressedBuffer, testSize, &prefs));
494         op += cErr;
495         CHECK_V(cErr, LZ4F_compressUpdate(cctx, op, LZ4F_compressBound(testSize, &prefs), CNBuffer, testSize, NULL));
496         op += cErr;
497         CHECK( LZ4F_compressEnd(cctx, compressedBuffer, testSize, NULL) );
498         DISPLAYLEVEL(3, "Compressed %i bytes into a %i bytes frame \n", (int)testSize, (int)(op-ostart));
499 
500         DISPLAYLEVEL(3, "compress with wrong frameSize : ");
501         prefs.frameInfo.contentSize = testSize+1;
502         op = ostart;
503         CHECK_V(cErr, LZ4F_compressBegin(cctx, compressedBuffer, testSize, &prefs));
504         op += cErr;
505         CHECK_V(cErr, LZ4F_compressUpdate(cctx, op, LZ4F_compressBound(testSize, &prefs), CNBuffer, testSize, NULL));
506         op += cErr;
507         cErr = LZ4F_compressEnd(cctx, op, testSize, NULL);
508         if (!LZ4F_isError(cErr)) goto _output_error;
509         DISPLAYLEVEL(3, "Error correctly detected : %s \n", LZ4F_getErrorName(cErr));
510 
511         CHECK( LZ4F_freeCompressionContext(cctx) );
512         cctx = NULL;
513     }
514 
515     /* dictID tests */
516     {   size_t cErr;
517         U32 const dictID = 0x99;
518         CHECK( LZ4F_createCompressionContext(&cctx, LZ4F_VERSION) );
519 
520         DISPLAYLEVEL(3, "insert a dictID : ");
521         memset(&prefs.frameInfo, 0, sizeof(prefs.frameInfo));
522         prefs.frameInfo.dictID = dictID;
523         CHECK_V(cErr, LZ4F_compressBegin(cctx, compressedBuffer, testSize, &prefs));
524         DISPLAYLEVEL(3, "created frame header of size %i bytes  \n", (int)cErr);
525 
526         DISPLAYLEVEL(3, "read a dictID : ");
527         CHECK( LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION) );
528         memset(&prefs.frameInfo, 0, sizeof(prefs.frameInfo));
529         CHECK( LZ4F_getFrameInfo(dCtx, &prefs.frameInfo, compressedBuffer, &cErr) );
530         if (prefs.frameInfo.dictID != dictID) goto _output_error;
531         DISPLAYLEVEL(3, "%u \n", (U32)prefs.frameInfo.dictID);
532 
533         CHECK( LZ4F_freeDecompressionContext(dCtx) ); dCtx = NULL;
534         CHECK( LZ4F_freeCompressionContext(cctx) ); cctx = NULL;
535     }
536 
537     /* Dictionary compression test */
538     {   size_t const dictSize = 63 KB;
539         size_t const dstCapacity = LZ4F_compressFrameBound(dictSize, NULL);
540         size_t cSizeNoDict, cSizeWithDict;
541         LZ4F_CDict* const cdict = LZ4F_createCDict(CNBuffer, dictSize);
542         if (cdict == NULL) goto _output_error;
543         CHECK( LZ4F_createCompressionContext(&cctx, LZ4F_VERSION) );
544 
545         DISPLAYLEVEL(3, "LZ4F_compressFrame_usingCDict, with NULL dict : ");
546         CHECK_V(cSizeNoDict,
547                 LZ4F_compressFrame_usingCDict(cctx, compressedBuffer, dstCapacity,
548                                               CNBuffer, dictSize,
549                                               NULL, NULL) );
550         DISPLAYLEVEL(3, "%u bytes \n", (unsigned)cSizeNoDict);
551 
552         CHECK( LZ4F_freeCompressionContext(cctx) );
553         CHECK( LZ4F_createCompressionContext(&cctx, LZ4F_VERSION) );
554         DISPLAYLEVEL(3, "LZ4F_compressFrame_usingCDict, with dict : ");
555         CHECK_V(cSizeWithDict,
556                 LZ4F_compressFrame_usingCDict(cctx, compressedBuffer, dstCapacity,
557                                               CNBuffer, dictSize,
558                                               cdict, NULL) );
559         DISPLAYLEVEL(3, "compressed %u bytes into %u bytes \n",
560                         (unsigned)dictSize, (unsigned)cSizeWithDict);
561         if ((LZ4_DISTANCE_MAX > dictSize) && (cSizeWithDict >= cSizeNoDict)) goto _output_error;  /* must be more efficient */
562         crcOrig = XXH64(CNBuffer, dictSize, 0);
563 
564         DISPLAYLEVEL(3, "LZ4F_decompress_usingDict : ");
565         {   LZ4F_dctx* dctx;
566             size_t decodedSize = COMPRESSIBLE_NOISE_LENGTH;
567             size_t compressedSize = cSizeWithDict;
568             CHECK( LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION) );
569             CHECK( LZ4F_decompress_usingDict(dctx,
570                                         decodedBuffer, &decodedSize,
571                                         compressedBuffer, &compressedSize,
572                                         CNBuffer, dictSize,
573                                         NULL) );
574             if (compressedSize != cSizeWithDict) goto _output_error;
575             if (decodedSize != dictSize) goto _output_error;
576             { U64 const crcDest = XXH64(decodedBuffer, decodedSize, 0);
577               if (crcDest != crcOrig) goto _output_error; }
578             DISPLAYLEVEL(3, "Regenerated %u bytes \n", (U32)decodedSize);
579             CHECK( LZ4F_freeDecompressionContext(dctx) );
580         }
581 
582         DISPLAYLEVEL(3, "LZ4F_compressFrame_usingCDict, with dict, negative level : ");
583         {   size_t cSizeLevelMax;
584             LZ4F_preferences_t cParams;
585             memset(&cParams, 0, sizeof(cParams));
586             cParams.compressionLevel = -3;
587             CHECK_V(cSizeLevelMax,
588                 LZ4F_compressFrame_usingCDict(cctx, compressedBuffer, dstCapacity,
589                                               CNBuffer, dictSize,
590                                               cdict, &cParams) );
591             DISPLAYLEVEL(3, "%u bytes \n", (unsigned)cSizeLevelMax);
592         }
593 
594         DISPLAYLEVEL(3, "LZ4F_compressFrame_usingCDict, with dict, level max : ");
595         {   size_t cSizeLevelMax;
596             LZ4F_preferences_t cParams;
597             memset(&cParams, 0, sizeof(cParams));
598             cParams.compressionLevel = LZ4F_compressionLevel_max();
599             CHECK_V(cSizeLevelMax,
600                 LZ4F_compressFrame_usingCDict(cctx, compressedBuffer, dstCapacity,
601                                               CNBuffer, dictSize,
602                                               cdict, &cParams) );
603             DISPLAYLEVEL(3, "%u bytes \n", (unsigned)cSizeLevelMax);
604         }
605 
606         DISPLAYLEVEL(3, "LZ4F_compressFrame_usingCDict, multiple linked blocks : ");
607         {   size_t cSizeContiguous;
608             size_t const inSize = dictSize * 3;
609             size_t const outCapacity = LZ4F_compressFrameBound(inSize, NULL);
610             LZ4F_preferences_t cParams;
611             memset(&cParams, 0, sizeof(cParams));
612             cParams.frameInfo.blockMode = LZ4F_blockLinked;
613             cParams.frameInfo.blockSizeID = LZ4F_max64KB;
614             CHECK_V(cSizeContiguous,
615                 LZ4F_compressFrame_usingCDict(cctx, compressedBuffer, outCapacity,
616                                               CNBuffer, inSize,
617                                               cdict, &cParams) );
618             DISPLAYLEVEL(3, "compressed %u bytes into %u bytes \n",
619                         (unsigned)inSize, (unsigned)cSizeContiguous);
620 
621             DISPLAYLEVEL(3, "LZ4F_decompress_usingDict on multiple linked blocks : ");
622             {   LZ4F_dctx* dctx;
623                 size_t decodedSize = COMPRESSIBLE_NOISE_LENGTH;
624                 size_t compressedSize = cSizeContiguous;
625                 CHECK( LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION) );
626                 CHECK( LZ4F_decompress_usingDict(dctx,
627                                             decodedBuffer, &decodedSize,
628                                             compressedBuffer, &compressedSize,
629                                             CNBuffer, dictSize,
630                                             NULL) );
631                 if (compressedSize != cSizeContiguous) goto _output_error;
632                 if (decodedSize != inSize) goto _output_error;
633                 crcOrig = XXH64(CNBuffer, inSize, 0);
634                 { U64 const crcDest = XXH64(decodedBuffer, decodedSize, 0);
635                   if (crcDest != crcOrig) goto _output_error; }
636                 DISPLAYLEVEL(3, "Regenerated %u bytes \n", (U32)decodedSize);
637                 CHECK( LZ4F_freeDecompressionContext(dctx) );
638             }
639         }
640 
641 
642         DISPLAYLEVEL(3, "LZ4F_compressFrame_usingCDict, multiple independent blocks : ");
643         {   size_t cSizeIndep;
644             size_t const inSize = dictSize * 3;
645             size_t const outCapacity = LZ4F_compressFrameBound(inSize, NULL);
646             LZ4F_preferences_t cParams;
647             memset(&cParams, 0, sizeof(cParams));
648             cParams.frameInfo.blockMode = LZ4F_blockIndependent;
649             cParams.frameInfo.blockSizeID = LZ4F_max64KB;
650             CHECK_V(cSizeIndep,
651                 LZ4F_compressFrame_usingCDict(cctx, compressedBuffer, outCapacity,
652                                               CNBuffer, inSize,
653                                               cdict, &cParams) );
654             DISPLAYLEVEL(3, "compressed %u bytes into %u bytes \n",
655                         (unsigned)inSize, (unsigned)cSizeIndep);
656 
657             DISPLAYLEVEL(3, "LZ4F_decompress_usingDict on multiple independent blocks : ");
658             {   LZ4F_dctx* dctx;
659                 size_t decodedSize = COMPRESSIBLE_NOISE_LENGTH;
660                 size_t compressedSize = cSizeIndep;
661                 CHECK( LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION) );
662                 CHECK( LZ4F_decompress_usingDict(dctx,
663                                             decodedBuffer, &decodedSize,
664                                             compressedBuffer, &compressedSize,
665                                             CNBuffer, dictSize,
666                                             NULL) );
667                 if (compressedSize != cSizeIndep) goto _output_error;
668                 if (decodedSize != inSize) goto _output_error;
669                 crcOrig = XXH64(CNBuffer, inSize, 0);
670                 { U64 const crcDest = XXH64(decodedBuffer, decodedSize, 0);
671                   if (crcDest != crcOrig) goto _output_error; }
672                 DISPLAYLEVEL(3, "Regenerated %u bytes \n", (U32)decodedSize);
673                 CHECK( LZ4F_freeDecompressionContext(dctx) );
674             }
675         }
676 
677         LZ4F_freeCDict(cdict);
678         CHECK( LZ4F_freeCompressionContext(cctx) ); cctx = NULL;
679     }
680 
681     DISPLAYLEVEL(3, "getBlockSize test: \n");
682     { size_t result;
683       unsigned blockSizeID;
684       for (blockSizeID = 4; blockSizeID < 8; ++blockSizeID) {
685         result = LZ4F_getBlockSize(blockSizeID);
686         CHECK(result);
687         DISPLAYLEVEL(3, "Returned block size of %u bytes for blockID %u \n",
688                          (unsigned)result, blockSizeID);
689       }
690 
691       /* Test an invalid input that's too large */
692       result = LZ4F_getBlockSize(8);
693       if(!LZ4F_isError(result) ||
694           LZ4F_getErrorCode(result) != LZ4F_ERROR_maxBlockSize_invalid)
695         goto _output_error;
696 
697       /* Test an invalid input that's too small */
698       result = LZ4F_getBlockSize(3);
699       if(!LZ4F_isError(result) ||
700           LZ4F_getErrorCode(result) != LZ4F_ERROR_maxBlockSize_invalid)
701         goto _output_error;
702     }
703 
704 
705     DISPLAYLEVEL(3, "Skippable frame test : \n");
706     {   size_t decodedBufferSize = COMPRESSIBLE_NOISE_LENGTH;
707         unsigned maxBits = FUZ_highbit((U32)decodedBufferSize);
708         BYTE* op = (BYTE*)decodedBuffer;
709         BYTE* const oend = (BYTE*)decodedBuffer + COMPRESSIBLE_NOISE_LENGTH;
710         BYTE* ip = (BYTE*)compressedBuffer;
711         BYTE* iend = (BYTE*)compressedBuffer + cSize + 8;
712 
713         CHECK( LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION) );
714 
715         /* generate skippable frame */
716         FUZ_writeLE32(ip, LZ4F_MAGIC_SKIPPABLE_START);
717         FUZ_writeLE32(ip+4, (U32)cSize);
718 
719         DISPLAYLEVEL(3, "random segment sizes : \n");
720         while (ip < iend) {
721             unsigned nbBits = FUZ_rand(&randState) % maxBits;
722             size_t iSize = (FUZ_rand(&randState) & ((1<<nbBits)-1)) + 1;
723             size_t oSize = (size_t)(oend-op);
724             if (iSize > (size_t)(iend-ip)) iSize = (size_t)(iend-ip);
725             CHECK( LZ4F_decompress(dCtx, op, &oSize, ip, &iSize, NULL) );
726             op += oSize;
727             ip += iSize;
728         }
729         DISPLAYLEVEL(3, "Skipped %i bytes \n", (int)decodedBufferSize);
730 
731         /* generate zero-size skippable frame */
732         DISPLAYLEVEL(3, "zero-size skippable frame\n");
733         ip = (BYTE*)compressedBuffer;
734         op = (BYTE*)decodedBuffer;
735         FUZ_writeLE32(ip, LZ4F_MAGIC_SKIPPABLE_START+1);
736         FUZ_writeLE32(ip+4, 0);
737         iend = ip+8;
738 
739         while (ip < iend) {
740             unsigned const nbBits = FUZ_rand(&randState) % maxBits;
741             size_t iSize = (FUZ_rand(&randState) & ((1<<nbBits)-1)) + 1;
742             size_t oSize = (size_t)(oend-op);
743             if (iSize > (size_t)(iend-ip)) iSize = (size_t)(iend-ip);
744             CHECK( LZ4F_decompress(dCtx, op, &oSize, ip, &iSize, NULL) );
745             op += oSize;
746             ip += iSize;
747         }
748         DISPLAYLEVEL(3, "Skipped %i bytes \n", (int)(ip - (BYTE*)compressedBuffer - 8));
749 
750         DISPLAYLEVEL(3, "Skippable frame header complete in first call \n");
751         ip = (BYTE*)compressedBuffer;
752         op = (BYTE*)decodedBuffer;
753         FUZ_writeLE32(ip, LZ4F_MAGIC_SKIPPABLE_START+2);
754         FUZ_writeLE32(ip+4, 10);
755         iend = ip+18;
756         while (ip < iend) {
757             size_t iSize = 10;
758             size_t oSize = 10;
759             if (iSize > (size_t)(iend-ip)) iSize = (size_t)(iend-ip);
760             CHECK( LZ4F_decompress(dCtx, op, &oSize, ip, &iSize, NULL) );
761             op += oSize;
762             ip += iSize;
763         }
764         DISPLAYLEVEL(3, "Skipped %i bytes \n", (int)(ip - (BYTE*)compressedBuffer - 8));
765     }
766 
767     DISPLAY("Basic tests completed \n");
768 _end:
769     free(CNBuffer);
770     free(compressedBuffer);
771     free(decodedBuffer);
772     LZ4F_freeDecompressionContext(dCtx); dCtx = NULL;
773     LZ4F_freeCompressionContext(cctx); cctx = NULL;
774     return basicTests_error;
775 
776 _output_error:
777     basicTests_error = 1;
778     DISPLAY("Error detected ! \n");
779     goto _end;
780 }
781 
782 
783 typedef enum { o_contiguous, o_noncontiguous, o_overwrite } o_scenario_e;
784 
locateBuffDiff(const void * buff1,const void * buff2,size_t size,o_scenario_e o_scenario)785 static void locateBuffDiff(const void* buff1, const void* buff2, size_t size, o_scenario_e o_scenario)
786 {
787     if (displayLevel >= 2) {
788         size_t p=0;
789         const BYTE* b1=(const BYTE*)buff1;
790         const BYTE* b2=(const BYTE*)buff2;
791         DISPLAY("locateBuffDiff: looking for error position \n");
792         if (o_scenario != o_contiguous) {
793             DISPLAY("mode %i: non-contiguous output (%u bytes), cannot search \n",
794                     (int)o_scenario, (unsigned)size);
795             return;
796         }
797         while (p < size && b1[p]==b2[p]) p++;
798         if (p != size) {
799             DISPLAY("Error at pos %i/%i : %02X != %02X \n", (int)p, (int)size, b1[p], b2[p]);
800         }
801     }
802 }
803 
804 #   define EXIT_MSG(...) { DISPLAY("Error => "); DISPLAY(__VA_ARGS__); \
805                            DISPLAY(" (seed %u, test nb %u)  \n", seed, testNb); exit(1); }
806 #   undef CHECK
807 #   define CHECK(cond, ...) { if (cond) { EXIT_MSG(__VA_ARGS__); } }
808 
809 
test_lz4f_decompression_wBuffers(const void * cSrc,size_t cSize,void * dst,size_t dstCapacity,o_scenario_e o_scenario,const void * srcRef,size_t decompressedSize,U64 crcOrig,U32 * const randState,LZ4F_dctx * const dCtx,U32 seed,U32 testNb,int findErrorPos)810 size_t test_lz4f_decompression_wBuffers(
811           const void* cSrc, size_t cSize,
812                 void* dst, size_t dstCapacity, o_scenario_e o_scenario,
813           const void* srcRef, size_t decompressedSize,
814                 U64 crcOrig,
815                 U32* const randState,
816                 LZ4F_dctx* const dCtx,
817                 U32 seed, U32 testNb,
818                 int findErrorPos)
819 {
820     const BYTE* ip = (const BYTE*)cSrc;
821     const BYTE* const iend = ip + cSize;
822 
823     BYTE* op = (BYTE*)dst;
824     BYTE* const oend = op + dstCapacity;
825 
826     unsigned const suggestedBits = FUZ_highbit((U32)cSize);
827     unsigned const maxBits = MAX(3, suggestedBits);
828     size_t totalOut = 0;
829     size_t moreToFlush = 0;
830     XXH64_state_t xxh64;
831     XXH64_reset(&xxh64, 1);
832     assert(ip < iend);
833     while (ip < iend) {
834         unsigned const nbBitsI = (FUZ_rand(randState) % (maxBits-1)) + 1;
835         unsigned const nbBitsO = (FUZ_rand(randState) % (maxBits)) + 1;
836         size_t const iSizeCand = (FUZ_rand(randState) & ((1<<nbBitsI)-1)) + 1;
837         size_t const iSizeMax = MIN(iSizeCand, (size_t)(iend-ip));
838         size_t iSize = iSizeMax;
839         size_t const oSizeCand = (FUZ_rand(randState) & ((1<<nbBitsO)-1)) + 2;
840         size_t const oSizeMax = MIN(oSizeCand, (size_t)(oend-op));
841         int const sentinelTest = (op + oSizeMax < oend);
842         size_t oSize = oSizeMax;
843         BYTE const mark = (BYTE)(FUZ_rand(randState) & 255);
844         LZ4F_decompressOptions_t dOptions;
845         memset(&dOptions, 0, sizeof(dOptions));
846         dOptions.stableDst = FUZ_rand(randState) & 1;
847         if (o_scenario == o_overwrite) dOptions.stableDst = 0;  /* overwrite mode */
848         if (sentinelTest) op[oSizeMax] = mark;
849 
850         DISPLAYLEVEL(7, "dstCapacity=%u,  presentedInput=%u \n", (unsigned)oSize, (unsigned)iSize);
851 
852         /* read data from byte-exact buffer to catch out-of-bound reads */
853         {   void* const iBuffer = malloc(iSizeMax);
854             void* const tmpop = (FUZ_rand(randState) & (oSize == 0)) ? NULL : op;
855             const void* const tmpip = (FUZ_rand(randState) & (iSize == 0)) ? NULL : iBuffer;
856             assert(iBuffer != NULL);
857             memcpy(iBuffer, ip, iSizeMax);
858             moreToFlush = LZ4F_decompress(dCtx, tmpop, &oSize, tmpip, &iSize, &dOptions);
859             free(iBuffer);
860         }
861         DISPLAYLEVEL(7, "oSize=%u,  readSize=%u \n", (unsigned)oSize, (unsigned)iSize);
862 
863         if (sentinelTest) {
864             CHECK(op[oSizeMax] != mark, "op[oSizeMax] = %02X != %02X : "
865                     "Decompression overwrites beyond assigned dst size",
866                     op[oSizeMax], mark);
867         }
868         if (LZ4F_getErrorCode(moreToFlush) == LZ4F_ERROR_contentChecksum_invalid) {
869             if (findErrorPos) DISPLAYLEVEL(2, "checksum error detected \n");
870             if (findErrorPos) locateBuffDiff(srcRef, dst, decompressedSize, o_scenario);
871         }
872         if (LZ4F_isError(moreToFlush)) return moreToFlush;
873 
874         XXH64_update(&xxh64, op, oSize);
875         totalOut += oSize;
876         op += oSize;
877         ip += iSize;
878         if (o_scenario == o_noncontiguous) {
879             if (op == oend) return LZ4F_ERROR_GENERIC;  /* can theoretically happen with bogus data */
880             op++; /* create a gap between consecutive output */
881         }
882         if (o_scenario==o_overwrite) op = (BYTE*)dst;   /* overwrite destination */
883         if ( (op == oend) /* no more room for output; can happen with bogus input */
884           && (iSize == 0)) /* no input consumed */
885             break;
886     }
887     if (moreToFlush != 0) return LZ4F_ERROR_decompressionFailed;
888     if (totalOut) {  /* otherwise, it's a skippable frame */
889         U64 const crcDecoded = XXH64_digest(&xxh64);
890         if (crcDecoded != crcOrig) {
891             if (findErrorPos) locateBuffDiff(srcRef, dst, decompressedSize, o_scenario);
892             return LZ4F_ERROR_contentChecksum_invalid;
893     }   }
894     return 0;
895 }
896 
897 
test_lz4f_decompression(const void * cSrc,size_t cSize,const void * srcRef,size_t decompressedSize,U64 crcOrig,U32 * const randState,LZ4F_dctx * const dCtx,U32 seed,U32 testNb,int findErrorPos)898 size_t test_lz4f_decompression(const void* cSrc, size_t cSize,
899                                const void* srcRef, size_t decompressedSize,
900                                U64 crcOrig,
901                                U32* const randState,
902                                LZ4F_dctx* const dCtx,
903                                U32 seed, U32 testNb,
904                                int findErrorPos)
905 {
906     o_scenario_e const o_scenario = (o_scenario_e)(FUZ_rand(randState) % 3);   /* 0 : contiguous; 1 : non-contiguous; 2 : dst overwritten */
907     /* tighten dst buffer conditions */
908     size_t const dstCapacity = (o_scenario == o_noncontiguous) ?
909                                (decompressedSize * 2) + 128 :
910                                decompressedSize;
911     size_t result;
912     void* const dstBuffer = malloc(dstCapacity);
913     assert(dstBuffer != NULL);
914 
915     result = test_lz4f_decompression_wBuffers(cSrc, cSize,
916                                      dstBuffer, dstCapacity, o_scenario,
917                                      srcRef, decompressedSize,
918                                      crcOrig,
919                                      randState,
920                                      dCtx,
921                                      seed, testNb, findErrorPos);
922 
923     free(dstBuffer);
924     return result;
925 }
926 
927 
fuzzerTests(U32 seed,unsigned nbTests,unsigned startTest,double compressibility,U32 duration_s)928 int fuzzerTests(U32 seed, unsigned nbTests, unsigned startTest, double compressibility, U32 duration_s)
929 {
930     unsigned testNb = 0;
931     size_t const CNBufferLength = 9 MB;  /* needs to be > 2x4MB to test large blocks */
932     void* CNBuffer = NULL;
933     size_t const compressedBufferSize = LZ4F_compressFrameBound(CNBufferLength, NULL) + 4 MB;  /* needs some margin */
934     void* compressedBuffer = NULL;
935     void* decodedBuffer = NULL;
936     U32 coreRand = seed;
937     LZ4F_decompressionContext_t dCtx = NULL;
938     LZ4F_decompressionContext_t dCtxNoise = NULL;
939     LZ4F_compressionContext_t cCtx = NULL;
940     clock_t const startClock = clock();
941     clock_t const clockDuration = duration_s * CLOCKS_PER_SEC;
942 
943     /* Create buffers */
944     {   size_t const creationStatus = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION);
945         CHECK(LZ4F_isError(creationStatus), "Allocation failed (error %i)", (int)creationStatus); }
946     {   size_t const creationStatus = LZ4F_createDecompressionContext(&dCtxNoise, LZ4F_VERSION);
947         CHECK(LZ4F_isError(creationStatus), "Allocation failed (error %i)", (int)creationStatus); }
948     {   size_t const creationStatus = LZ4F_createCompressionContext(&cCtx, LZ4F_VERSION);
949         CHECK(LZ4F_isError(creationStatus), "Allocation failed (error %i)", (int)creationStatus); }
950     CNBuffer = malloc(CNBufferLength);
951     CHECK(CNBuffer==NULL, "CNBuffer Allocation failed");
952     compressedBuffer = malloc(compressedBufferSize);
953     CHECK(compressedBuffer==NULL, "compressedBuffer Allocation failed");
954     decodedBuffer = calloc(1, CNBufferLength);   /* calloc avoids decodedBuffer being considered "garbage" by scan-build */
955     CHECK(decodedBuffer==NULL, "decodedBuffer Allocation failed");
956     FUZ_fillCompressibleNoiseBuffer(CNBuffer, CNBufferLength, compressibility, &coreRand);
957 
958     /* jump to requested testNb */
959     for (testNb =0; (testNb < startTest); testNb++) (void)FUZ_rand(&coreRand);   /* sync randomizer */
960 
961     /* main fuzzer test loop */
962     for ( ; (testNb < nbTests) || (clockDuration > FUZ_GetClockSpan(startClock)) ; testNb++) {
963         U32 randState = coreRand ^ prime1;
964         unsigned const srcBits = (FUZ_rand(&randState) % (FUZ_highbit((U32)(CNBufferLength-1)) - 1)) + 1;
965         size_t const srcSize = (FUZ_rand(&randState) & ((1<<srcBits)-1)) + 1;
966         size_t const srcStartId = FUZ_rand(&randState) % (CNBufferLength - srcSize);
967         const BYTE* const srcStart = (const BYTE*)CNBuffer + srcStartId;
968         unsigned const neverFlush = (FUZ_rand(&randState) & 15) == 1;
969         U64 const crcOrig = XXH64(srcStart, srcSize, 1);
970         LZ4F_preferences_t prefs;
971         const LZ4F_preferences_t* prefsPtr = &prefs;
972         size_t cSize;
973 
974         (void)FUZ_rand(&coreRand);   /* update seed */
975         memset(&prefs, 0, sizeof(prefs));
976         prefs.frameInfo.blockMode = (LZ4F_blockMode_t)(FUZ_rand(&randState) & 1);
977         prefs.frameInfo.blockSizeID = (LZ4F_blockSizeID_t)(4 + (FUZ_rand(&randState) & 3));
978         prefs.frameInfo.blockChecksumFlag = (LZ4F_blockChecksum_t)(FUZ_rand(&randState) & 1);
979         prefs.frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)(FUZ_rand(&randState) & 1);
980         prefs.frameInfo.contentSize = ((FUZ_rand(&randState) & 0xF) == 1) ? srcSize : 0;
981         prefs.autoFlush = neverFlush ? 0 : (FUZ_rand(&randState) & 7) == 2;
982         prefs.compressionLevel = -5 + (int)(FUZ_rand(&randState) % 11);
983         if ((FUZ_rand(&randState) & 0xF) == 1) prefsPtr = NULL;
984 
985         DISPLAYUPDATE(2, "\r%5u   ", testNb);
986 
987         if ((FUZ_rand(&randState) & 0xFFF) == 0) {
988             /* create a skippable frame (rare case) */
989             BYTE* op = (BYTE*)compressedBuffer;
990             FUZ_writeLE32(op, LZ4F_MAGIC_SKIPPABLE_START + (FUZ_rand(&randState) & 15));
991             FUZ_writeLE32(op+4, (U32)srcSize);
992             cSize = srcSize+8;
993 
994         } else if ((FUZ_rand(&randState) & 0xF) == 2) {  /* single pass compression (simple) */
995             cSize = LZ4F_compressFrame(compressedBuffer, LZ4F_compressFrameBound(srcSize, prefsPtr), srcStart, srcSize, prefsPtr);
996             CHECK(LZ4F_isError(cSize), "LZ4F_compressFrame failed : error %i (%s)", (int)cSize, LZ4F_getErrorName(cSize));
997 
998         } else {   /* multi-segments compression */
999             const BYTE* ip = srcStart;
1000             const BYTE* const iend = srcStart + srcSize;
1001             BYTE* op = (BYTE*)compressedBuffer;
1002             BYTE* const oend = op + (neverFlush ? LZ4F_compressFrameBound(srcSize, prefsPtr) : compressedBufferSize);  /* when flushes are possible, can't guarantee a max compressed size */
1003             unsigned const maxBits = FUZ_highbit((U32)srcSize);
1004             LZ4F_compressOptions_t cOptions;
1005             memset(&cOptions, 0, sizeof(cOptions));
1006             {   size_t const fhSize = LZ4F_compressBegin(cCtx, op, (size_t)(oend-op), prefsPtr);
1007                 CHECK(LZ4F_isError(fhSize), "Compression header failed (error %i)",
1008                                             (int)fhSize);
1009                 op += fhSize;
1010             }
1011             while (ip < iend) {
1012                 unsigned const nbBitsSeg = FUZ_rand(&randState) % maxBits;
1013                 size_t const sampleMax = (FUZ_rand(&randState) & ((1<<nbBitsSeg)-1)) + 1;
1014                 size_t const iSize = MIN(sampleMax, (size_t)(iend-ip));
1015                 size_t const oSize = LZ4F_compressBound(iSize, prefsPtr);
1016                 size_t flushedSize;
1017                 cOptions.stableSrc = ((FUZ_rand(&randState) & 3) == 1);
1018                 DISPLAYLEVEL(6, "Sending %u bytes to compress (stableSrc:%u) \n",
1019                                 (unsigned)iSize, cOptions.stableSrc);
1020 
1021                 flushedSize = LZ4F_compressUpdate(cCtx, op, oSize, ip, iSize, &cOptions);
1022                 CHECK(LZ4F_isError(flushedSize), "Compression failed (error %i : %s)",
1023                             (int)flushedSize, LZ4F_getErrorName(flushedSize));
1024                 op += flushedSize;
1025                 ip += iSize;
1026 
1027                 {   unsigned const forceFlush = neverFlush ? 0 : ((FUZ_rand(&randState) & 3) == 1);
1028                     if (forceFlush) {
1029                         size_t const flushSize = LZ4F_flush(cCtx, op, (size_t)(oend-op), &cOptions);
1030                         DISPLAYLEVEL(6,"flushing %u bytes \n", (unsigned)flushSize);
1031                         CHECK(LZ4F_isError(flushSize), "Compression failed (error %i)", (int)flushSize);
1032                         op += flushSize;
1033                         if ((FUZ_rand(&randState) % 1024) == 3) {
1034                             /* add an empty block (requires uncompressed flag) */
1035                             op[0] = op[1] = op[2] = 0;
1036                             op[3] = 0x80; /* 0x80000000U in little-endian format */
1037                             op += 4;
1038                             if ((prefsPtr!= NULL) && prefsPtr->frameInfo.blockChecksumFlag) {
1039                                 U32 const bc32 = XXH32(op, 0, 0);
1040                                 op[0] = (BYTE)bc32;  /* little endian format */
1041                                 op[1] = (BYTE)(bc32>>8);
1042                                 op[2] = (BYTE)(bc32>>16);
1043                                 op[3] = (BYTE)(bc32>>24);
1044                                 op += 4;
1045                 }   }   }   }
1046             }  /* while (ip<iend) */
1047             CHECK(op>=oend, "LZ4F_compressFrameBound overflow");
1048             {   size_t const dstEndSafeSize = LZ4F_compressBound(0, prefsPtr);
1049                 int const tooSmallDstEnd = ((FUZ_rand(&randState) & 31) == 3);
1050                 size_t const dstEndTooSmallSize = (FUZ_rand(&randState) % dstEndSafeSize) + 1;
1051                 size_t const dstEndSize = tooSmallDstEnd ? dstEndTooSmallSize : dstEndSafeSize;
1052                 BYTE const canaryByte = (BYTE)(FUZ_rand(&randState) & 255);
1053                 size_t flushedSize;
1054                 DISPLAYLEVEL(7,"canaryByte at pos %u / %u \n",
1055                             (unsigned)((size_t)(op - (BYTE*)compressedBuffer) + dstEndSize),
1056                             (unsigned)compressedBufferSize);
1057                 assert(op + dstEndSize < (BYTE*)compressedBuffer + compressedBufferSize);
1058                 op[dstEndSize] = canaryByte;
1059                 flushedSize = LZ4F_compressEnd(cCtx, op, dstEndSize, &cOptions);
1060                 CHECK(op[dstEndSize] != canaryByte, "LZ4F_compressEnd writes beyond dstCapacity !");
1061                 if (LZ4F_isError(flushedSize)) {
1062                     if (tooSmallDstEnd) /* failure is allowed */ continue;
1063                     CHECK(!tooSmallDstEnd, "Compression completion failed (error %i : %s)",
1064                             (int)flushedSize, LZ4F_getErrorName(flushedSize));
1065                 }
1066                 op += flushedSize;
1067             }
1068             cSize = (size_t)(op - (BYTE*)compressedBuffer);
1069             DISPLAYLEVEL(5, "\nCompressed %u bytes into %u \n", (U32)srcSize, (U32)cSize);
1070         }
1071 
1072 
1073         /* multi-segments decompression */
1074         DISPLAYLEVEL(6, "normal decompression \n");
1075         {   size_t result = test_lz4f_decompression(compressedBuffer, cSize, srcStart, srcSize, crcOrig, &randState, dCtx, seed, testNb, 1 /*findError*/ );
1076             CHECK (LZ4F_isError(result), "multi-segment decompression failed (error %i => %s)",
1077                                         (int)result, LZ4F_getErrorName(result));
1078         }
1079 
1080 #if 1
1081         /* insert noise into src */
1082         {   U32 const maxNbBits = FUZ_highbit((U32)cSize);
1083             size_t pos = 0;
1084             for (;;) {
1085                 /* keep some original src */
1086                 {   U32 const nbBits = FUZ_rand(&randState) % maxNbBits;
1087                     size_t const mask = (1<<nbBits) - 1;
1088                     size_t const skipLength = FUZ_rand(&randState) & mask;
1089                     pos += skipLength;
1090                 }
1091                 if (pos >= cSize) break;
1092                 /* add noise */
1093                 {   U32 const nbBitsCodes = FUZ_rand(&randState) % maxNbBits;
1094                     U32 const nbBits = nbBitsCodes ? nbBitsCodes-1 : 0;
1095                     size_t const mask = (1<<nbBits) - 1;
1096                     size_t const rNoiseLength = (FUZ_rand(&randState) & mask) + 1;
1097                     size_t const noiseLength = MIN(rNoiseLength, cSize-pos);
1098                     size_t const noiseStart = FUZ_rand(&randState) % (CNBufferLength - noiseLength);
1099                     memcpy((BYTE*)compressedBuffer + pos, (const char*)CNBuffer + noiseStart, noiseLength);
1100                     pos += noiseLength;
1101         }   }   }
1102 
1103         /* test decompression on noisy src */
1104         DISPLAYLEVEL(6, "noisy decompression \n");
1105         test_lz4f_decompression(compressedBuffer, cSize, srcStart, srcSize, crcOrig, &randState, dCtxNoise, seed, testNb, 0 /*don't search error Pos*/ );
1106         /* note : we don't analyze result here : it probably failed, which is expected.
1107          * The sole purpose is to catch potential out-of-bound reads and writes. */
1108         LZ4F_resetDecompressionContext(dCtxNoise);  /* context must be reset after an error */
1109 #endif
1110 
1111 }   /* for ( ; (testNb < nbTests) ; ) */
1112 
1113     DISPLAYLEVEL(2, "\rAll tests completed   \n");
1114 
1115     LZ4F_freeDecompressionContext(dCtx);
1116     LZ4F_freeDecompressionContext(dCtxNoise);
1117     LZ4F_freeCompressionContext(cCtx);
1118     free(CNBuffer);
1119     free(compressedBuffer);
1120     free(decodedBuffer);
1121 
1122     if (use_pause) {
1123         DISPLAY("press enter to finish \n");
1124         (void)getchar();
1125     }
1126     return 0;
1127 }
1128 
1129 
FUZ_usage(const char * programName)1130 int FUZ_usage(const char* programName)
1131 {
1132     DISPLAY( "Usage :\n");
1133     DISPLAY( "      %s [args]\n", programName);
1134     DISPLAY( "\n");
1135     DISPLAY( "Arguments :\n");
1136     DISPLAY( " -i#    : Nb of tests (default:%u) \n", nbTestsDefault);
1137     DISPLAY( " -T#    : Duration of tests, in seconds (default: use Nb of tests) \n");
1138     DISPLAY( " -s#    : Select seed (default:prompt user)\n");
1139     DISPLAY( " -t#    : Select starting test number (default:0)\n");
1140     DISPLAY( " -P#    : Select compressibility in %% (default:%i%%)\n", FUZ_COMPRESSIBILITY_DEFAULT);
1141     DISPLAY( " -v     : verbose\n");
1142     DISPLAY( " -h     : display help and exit\n");
1143     return 0;
1144 }
1145 
1146 
main(int argc,const char ** argv)1147 int main(int argc, const char** argv)
1148 {
1149     U32 seed=0;
1150     int seedset=0;
1151     int argNb;
1152     unsigned nbTests = nbTestsDefault;
1153     unsigned testNb = 0;
1154     int proba = FUZ_COMPRESSIBILITY_DEFAULT;
1155     int result=0;
1156     U32 duration=0;
1157     const char* const programName = argv[0];
1158 
1159     /* Check command line */
1160     for (argNb=1; argNb<argc; argNb++) {
1161         const char* argument = argv[argNb];
1162 
1163         if(!argument) continue;   /* Protection if argument empty */
1164 
1165         /* Decode command (note : aggregated short commands are allowed) */
1166         if (argument[0]=='-') {
1167             if (!strcmp(argument, "--no-prompt")) {
1168                 no_prompt=1;
1169                 seedset=1;
1170                 displayLevel=1;
1171                 continue;
1172             }
1173             argument++;
1174 
1175             while (*argument!=0) {
1176                 switch(*argument)
1177                 {
1178                 case 'h':
1179                     return FUZ_usage(programName);
1180                 case 'v':
1181                     argument++;
1182                     displayLevel++;
1183                     break;
1184                 case 'q':
1185                     argument++;
1186                     displayLevel--;
1187                     break;
1188                 case 'p': /* pause at the end */
1189                     argument++;
1190                     use_pause = 1;
1191                     break;
1192 
1193                 case 'i':
1194                     argument++;
1195                     nbTests=0; duration=0;
1196                     while ((*argument>='0') && (*argument<='9')) {
1197                         nbTests *= 10;
1198                         nbTests += (unsigned)(*argument - '0');
1199                         argument++;
1200                     }
1201                     break;
1202 
1203                 case 'T':
1204                     argument++;
1205                     nbTests = 0; duration = 0;
1206                     for (;;) {
1207                         switch(*argument)
1208                         {
1209                             case 'm': duration *= 60; argument++; continue;
1210                             case 's':
1211                             case 'n': argument++; continue;
1212                             case '0':
1213                             case '1':
1214                             case '2':
1215                             case '3':
1216                             case '4':
1217                             case '5':
1218                             case '6':
1219                             case '7':
1220                             case '8':
1221                             case '9': duration *= 10; duration += (U32)(*argument++ - '0'); continue;
1222                         }
1223                         break;
1224                     }
1225                     break;
1226 
1227                 case 's':
1228                     argument++;
1229                     seed=0;
1230                     seedset=1;
1231                     while ((*argument>='0') && (*argument<='9')) {
1232                         seed *= 10;
1233                         seed += (U32)(*argument - '0');
1234                         argument++;
1235                     }
1236                     break;
1237                 case 't':
1238                     argument++;
1239                     testNb=0;
1240                     while ((*argument>='0') && (*argument<='9')) {
1241                         testNb *= 10;
1242                         testNb += (unsigned)(*argument - '0');
1243                         argument++;
1244                     }
1245                     break;
1246                 case 'P':   /* compressibility % */
1247                     argument++;
1248                     proba=0;
1249                     while ((*argument>='0') && (*argument<='9')) {
1250                         proba *= 10;
1251                         proba += *argument - '0';
1252                         argument++;
1253                     }
1254                     if (proba<0) proba=0;
1255                     if (proba>100) proba=100;
1256                     break;
1257                 default:
1258                     ;
1259                     return FUZ_usage(programName);
1260                 }
1261             }
1262         }
1263     }
1264 
1265     /* Get Seed */
1266     DISPLAY("Starting lz4frame tester (%i-bits, %s)\n", (int)(sizeof(size_t)*8), LZ4_VERSION_STRING);
1267 
1268     if (!seedset) {
1269         time_t const t = time(NULL);
1270         U32 const h = XXH32(&t, sizeof(t), 1);
1271         seed = h % 10000;
1272     }
1273     DISPLAY("Seed = %u\n", seed);
1274     if (proba!=FUZ_COMPRESSIBILITY_DEFAULT) DISPLAY("Compressibility : %i%%\n", proba);
1275 
1276     nbTests += (nbTests==0);  /* avoid zero */
1277 
1278     if (testNb==0) result = basicTests(seed, ((double)proba) / 100);
1279     if (result) return 1;
1280     return fuzzerTests(seed, nbTests, testNb, ((double)proba) / 100, duration);
1281 }
1282