Home | History | Annotate | Line # | Download | only in tests
      1  1.1  christos /*
      2  1.1  christos  * Copyright (c) Meta Platforms, Inc. and affiliates.
      3  1.1  christos  * All rights reserved.
      4  1.1  christos  *
      5  1.1  christos  * This source code is licensed under both the BSD-style license (found in the
      6  1.1  christos  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
      7  1.1  christos  * in the COPYING file in the root directory of this source tree).
      8  1.1  christos  * You may select, at your option, one of the above-listed licenses.
      9  1.1  christos  */
     10  1.1  christos 
     11  1.1  christos /*
     12  1.1  christos   This program takes a file in input,
     13  1.1  christos   performs a zstd round-trip test (compression - decompress)
     14  1.1  christos   compares the result with original
     15  1.1  christos   and generates a crash (double free) on corruption detection.
     16  1.1  christos */
     17  1.1  christos 
     18  1.1  christos /*===========================================
     19  1.1  christos *   Dependencies
     20  1.1  christos *==========================================*/
     21  1.1  christos #include <stddef.h>     /* size_t */
     22  1.1  christos #include <stdlib.h>     /* malloc, free, exit */
     23  1.1  christos #include <stdio.h>      /* fprintf */
     24  1.1  christos #include <string.h>     /* strcmp */
     25  1.1  christos #include <sys/types.h>  /* stat */
     26  1.1  christos #include <sys/stat.h>   /* stat */
     27  1.1  christos #include "xxhash.h"
     28  1.1  christos 
     29  1.1  christos #define ZSTD_STATIC_LINKING_ONLY
     30  1.1  christos #include "zstd.h"
     31  1.1  christos 
     32  1.1  christos /*===========================================
     33  1.1  christos *   Macros
     34  1.1  christos *==========================================*/
     35  1.1  christos #define MIN(a,b)  ( (a) < (b) ? (a) : (b) )
     36  1.1  christos 
     37  1.1  christos static void crash(int errorCode){
     38  1.1  christos     /* abort if AFL/libfuzzer, exit otherwise */
     39  1.1  christos     #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* could also use __AFL_COMPILER */
     40  1.1  christos         abort();
     41  1.1  christos     #else
     42  1.1  christos         exit(errorCode);
     43  1.1  christos     #endif
     44  1.1  christos }
     45  1.1  christos 
     46  1.1  christos #define CHECK_Z(f) {                            \
     47  1.1  christos     size_t const err = f;                       \
     48  1.1  christos     if (ZSTD_isError(err)) {                    \
     49  1.1  christos         fprintf(stderr,                         \
     50  1.1  christos                 "Error=> %s: %s",               \
     51  1.1  christos                 #f, ZSTD_getErrorName(err));    \
     52  1.1  christos         crash(1);                                \
     53  1.1  christos }   }
     54  1.1  christos 
     55  1.1  christos /** roundTripTest() :
     56  1.1  christos *   Compresses `srcBuff` into `compressedBuff`,
     57  1.1  christos *   then decompresses `compressedBuff` into `resultBuff`.
     58  1.1  christos *   Compression level used is derived from first content byte.
     59  1.1  christos *   @return : result of decompression, which should be == `srcSize`
     60  1.1  christos *          or an error code if either compression or decompression fails.
     61  1.1  christos *   Note : `compressedBuffCapacity` should be `>= ZSTD_compressBound(srcSize)`
     62  1.1  christos *          for compression to be guaranteed to work */
     63  1.1  christos static size_t roundTripTest(void* resultBuff, size_t resultBuffCapacity,
     64  1.1  christos                             void* compressedBuff, size_t compressedBuffCapacity,
     65  1.1  christos                       const void* srcBuff, size_t srcBuffSize)
     66  1.1  christos {
     67  1.1  christos     static const int maxClevel = 19;
     68  1.1  christos     size_t const hashLength = MIN(128, srcBuffSize);
     69  1.1  christos     unsigned const h32 = XXH32(srcBuff, hashLength, 0);
     70  1.1  christos     int const cLevel = h32 % maxClevel;
     71  1.1  christos     size_t const cSize = ZSTD_compress(compressedBuff, compressedBuffCapacity, srcBuff, srcBuffSize, cLevel);
     72  1.1  christos     if (ZSTD_isError(cSize)) {
     73  1.1  christos         fprintf(stderr, "Compression error : %s \n", ZSTD_getErrorName(cSize));
     74  1.1  christos         return cSize;
     75  1.1  christos     }
     76  1.1  christos     return ZSTD_decompress(resultBuff, resultBuffCapacity, compressedBuff, cSize);
     77  1.1  christos }
     78  1.1  christos 
     79  1.1  christos /** cctxParamRoundTripTest() :
     80  1.1  christos  *  Same as roundTripTest() except allows experimenting with ZSTD_CCtx_params. */
     81  1.1  christos static size_t cctxParamRoundTripTest(void* resultBuff, size_t resultBuffCapacity,
     82  1.1  christos                             void* compressedBuff, size_t compressedBuffCapacity,
     83  1.1  christos                       const void* srcBuff, size_t srcBuffSize)
     84  1.1  christos {
     85  1.1  christos     ZSTD_CCtx* const cctx = ZSTD_createCCtx();
     86  1.1  christos     ZSTD_CCtx_params* const cctxParams = ZSTD_createCCtxParams();
     87  1.1  christos     ZSTD_inBuffer inBuffer = { srcBuff, srcBuffSize, 0 };
     88  1.1  christos     ZSTD_outBuffer outBuffer = { compressedBuff, compressedBuffCapacity, 0 };
     89  1.1  christos 
     90  1.1  christos     static const int maxClevel = 19;
     91  1.1  christos     size_t const hashLength = MIN(128, srcBuffSize);
     92  1.1  christos     unsigned const h32 = XXH32(srcBuff, hashLength, 0);
     93  1.1  christos     int const cLevel = h32 % maxClevel;
     94  1.1  christos 
     95  1.1  christos     /* Set parameters */
     96  1.1  christos     CHECK_Z( ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_compressionLevel, cLevel) );
     97  1.1  christos     CHECK_Z( ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_nbWorkers, 2) );
     98  1.1  christos     CHECK_Z( ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_overlapLog, 5) );
     99  1.1  christos 
    100  1.1  christos 
    101  1.1  christos     /* Apply parameters */
    102  1.1  christos     CHECK_Z( ZSTD_CCtx_setParametersUsingCCtxParams(cctx, cctxParams) );
    103  1.1  christos 
    104  1.1  christos     CHECK_Z (ZSTD_compressStream2(cctx, &outBuffer, &inBuffer, ZSTD_e_end) );
    105  1.1  christos 
    106  1.1  christos     ZSTD_freeCCtxParams(cctxParams);
    107  1.1  christos     ZSTD_freeCCtx(cctx);
    108  1.1  christos 
    109  1.1  christos     return ZSTD_decompress(resultBuff, resultBuffCapacity, compressedBuff, outBuffer.pos);
    110  1.1  christos }
    111  1.1  christos 
    112  1.1  christos static size_t checkBuffers(const void* buff1, const void* buff2, size_t buffSize)
    113  1.1  christos {
    114  1.1  christos     const char* ip1 = (const char*)buff1;
    115  1.1  christos     const char* ip2 = (const char*)buff2;
    116  1.1  christos     size_t pos;
    117  1.1  christos 
    118  1.1  christos     for (pos=0; pos<buffSize; pos++)
    119  1.1  christos         if (ip1[pos]!=ip2[pos])
    120  1.1  christos             break;
    121  1.1  christos 
    122  1.1  christos     return pos;
    123  1.1  christos }
    124  1.1  christos 
    125  1.1  christos static void roundTripCheck(const void* srcBuff, size_t srcBuffSize, int testCCtxParams)
    126  1.1  christos {
    127  1.1  christos     size_t const cBuffSize = ZSTD_compressBound(srcBuffSize);
    128  1.1  christos     void* cBuff = malloc(cBuffSize);
    129  1.1  christos     void* rBuff = malloc(cBuffSize);
    130  1.1  christos 
    131  1.1  christos     if (!cBuff || !rBuff) {
    132  1.1  christos         fprintf(stderr, "not enough memory ! \n");
    133  1.1  christos         exit (1);
    134  1.1  christos     }
    135  1.1  christos 
    136  1.1  christos     {   size_t const result = testCCtxParams ?
    137  1.1  christos                   cctxParamRoundTripTest(rBuff, cBuffSize, cBuff, cBuffSize, srcBuff, srcBuffSize)
    138  1.1  christos                 : roundTripTest(rBuff, cBuffSize, cBuff, cBuffSize, srcBuff, srcBuffSize);
    139  1.1  christos         if (ZSTD_isError(result)) {
    140  1.1  christos             fprintf(stderr, "roundTripTest error : %s \n", ZSTD_getErrorName(result));
    141  1.1  christos             crash(1);
    142  1.1  christos         }
    143  1.1  christos         if (result != srcBuffSize) {
    144  1.1  christos             fprintf(stderr, "Incorrect regenerated size : %u != %u\n", (unsigned)result, (unsigned)srcBuffSize);
    145  1.1  christos             crash(1);
    146  1.1  christos         }
    147  1.1  christos         if (checkBuffers(srcBuff, rBuff, srcBuffSize) != srcBuffSize) {
    148  1.1  christos             fprintf(stderr, "Silent decoding corruption !!!");
    149  1.1  christos             crash(1);
    150  1.1  christos         }
    151  1.1  christos     }
    152  1.1  christos 
    153  1.1  christos     free(cBuff);
    154  1.1  christos     free(rBuff);
    155  1.1  christos }
    156  1.1  christos 
    157  1.1  christos 
    158  1.1  christos static size_t getFileSize(const char* infilename)
    159  1.1  christos {
    160  1.1  christos     int r;
    161  1.1  christos #if defined(_MSC_VER)
    162  1.1  christos     struct _stat64 statbuf;
    163  1.1  christos     r = _stat64(infilename, &statbuf);
    164  1.1  christos     if (r || !(statbuf.st_mode & S_IFREG)) return 0;   /* No good... */
    165  1.1  christos #else
    166  1.1  christos     struct stat statbuf;
    167  1.1  christos     r = stat(infilename, &statbuf);
    168  1.1  christos     if (r || !S_ISREG(statbuf.st_mode)) return 0;   /* No good... */
    169  1.1  christos #endif
    170  1.1  christos     return (size_t)statbuf.st_size;
    171  1.1  christos }
    172  1.1  christos 
    173  1.1  christos 
    174  1.1  christos static int isDirectory(const char* infilename)
    175  1.1  christos {
    176  1.1  christos     int r;
    177  1.1  christos #if defined(_MSC_VER)
    178  1.1  christos     struct _stat64 statbuf;
    179  1.1  christos     r = _stat64(infilename, &statbuf);
    180  1.1  christos     if (!r && (statbuf.st_mode & _S_IFDIR)) return 1;
    181  1.1  christos #else
    182  1.1  christos     struct stat statbuf;
    183  1.1  christos     r = stat(infilename, &statbuf);
    184  1.1  christos     if (!r && S_ISDIR(statbuf.st_mode)) return 1;
    185  1.1  christos #endif
    186  1.1  christos     return 0;
    187  1.1  christos }
    188  1.1  christos 
    189  1.1  christos 
    190  1.1  christos /** loadFile() :
    191  1.1  christos *   requirement : `buffer` size >= `fileSize` */
    192  1.1  christos static void loadFile(void* buffer, const char* fileName, size_t fileSize)
    193  1.1  christos {
    194  1.1  christos     FILE* const f = fopen(fileName, "rb");
    195  1.1  christos     if (isDirectory(fileName)) {
    196  1.1  christos         fprintf(stderr, "Ignoring %s directory \n", fileName);
    197  1.1  christos         exit(2);
    198  1.1  christos     }
    199  1.1  christos     if (f==NULL) {
    200  1.1  christos         fprintf(stderr, "Impossible to open %s \n", fileName);
    201  1.1  christos         exit(3);
    202  1.1  christos     }
    203  1.1  christos     {   size_t const readSize = fread(buffer, 1, fileSize, f);
    204  1.1  christos         if (readSize != fileSize) {
    205  1.1  christos             fprintf(stderr, "Error reading %s \n", fileName);
    206  1.1  christos             exit(5);
    207  1.1  christos     }   }
    208  1.1  christos     fclose(f);
    209  1.1  christos }
    210  1.1  christos 
    211  1.1  christos 
    212  1.1  christos static void fileCheck(const char* fileName, int testCCtxParams)
    213  1.1  christos {
    214  1.1  christos     size_t const fileSize = getFileSize(fileName);
    215  1.1  christos     void* const buffer = malloc(fileSize + !fileSize /* avoid 0 */);
    216  1.1  christos     if (!buffer) {
    217  1.1  christos         fprintf(stderr, "not enough memory \n");
    218  1.1  christos         exit(4);
    219  1.1  christos     }
    220  1.1  christos     loadFile(buffer, fileName, fileSize);
    221  1.1  christos     roundTripCheck(buffer, fileSize, testCCtxParams);
    222  1.1  christos     free (buffer);
    223  1.1  christos }
    224  1.1  christos 
    225  1.1  christos int main(int argCount, const char** argv) {
    226  1.1  christos     int argNb = 1;
    227  1.1  christos     int testCCtxParams = 0;
    228  1.1  christos     if (argCount < 2) {
    229  1.1  christos         fprintf(stderr, "Error : no argument : need input file \n");
    230  1.1  christos         exit(9);
    231  1.1  christos     }
    232  1.1  christos 
    233  1.1  christos     if (!strcmp(argv[argNb], "--cctxParams")) {
    234  1.1  christos       testCCtxParams = 1;
    235  1.1  christos       argNb++;
    236  1.1  christos     }
    237  1.1  christos 
    238  1.1  christos     fileCheck(argv[argNb], testCCtxParams);
    239  1.1  christos     fprintf(stderr, "no pb detected\n");
    240  1.1  christos     return 0;
    241  1.1  christos }
    242