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