1 /*
2 checkFrame - verify frame headers
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 * Includes
28 **************************************/
29 #include "util.h" /* U32 */
30 #include <stdlib.h> /* malloc, free */
31 #include <stdio.h> /* fprintf */
32 #include <string.h> /* strcmp */
33 #include <time.h> /* clock_t, clock(), CLOCKS_PER_SEC */
34 #include <assert.h>
35 #include "lz4frame.h" /* include multiple times to test correctness/safety */
36 #include "lz4frame.h"
37 #define LZ4F_STATIC_LINKING_ONLY
38 #include "lz4frame.h"
39 #include "lz4frame.h"
40 #include "lz4.h" /* LZ4_VERSION_STRING */
41 #define XXH_STATIC_LINKING_ONLY
42 #include "xxhash.h" /* XXH64 */
43
44
45 /*-************************************
46 * Constants
47 **************************************/
48 #define KB *(1U<<10)
49 #define MB *(1U<<20)
50 #define GB *(1U<<30)
51
52
53 /*-************************************
54 * Macros
55 **************************************/
56 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
57 #define DISPLAYLEVEL(l, ...) if (displayLevel>=l) { DISPLAY(__VA_ARGS__); }
58
59 /**************************************
60 * Exceptions
61 ***************************************/
62 #ifndef DEBUG
63 # define DEBUG 0
64 #endif
65 #define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__);
66 #define EXM_THROW(error, ...) \
67 { \
68 DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \
69 DISPLAYLEVEL(1, "Error %i : ", error); \
70 DISPLAYLEVEL(1, __VA_ARGS__); \
71 DISPLAYLEVEL(1, " \n"); \
72 return(error); \
73 }
74
75
76
77 /*-***************************************
78 * Local Parameters
79 *****************************************/
80 static U32 no_prompt = 0;
81 static U32 displayLevel = 2;
82 static U32 use_pause = 0;
83
84
85 /*-*******************************************************
86 * Fuzzer functions
87 *********************************************************/
88 #define MIN(a,b) ( (a) < (b) ? (a) : (b) )
89 #define MAX(a,b) ( (a) > (b) ? (a) : (b) )
90
91 typedef struct {
92 void* srcBuffer;
93 size_t srcBufferSize;
94 void* dstBuffer;
95 size_t dstBufferSize;
96 LZ4F_decompressionContext_t ctx;
97 } cRess_t;
98
createCResources(cRess_t * ress)99 static int createCResources(cRess_t* ress)
100 {
101 ress->srcBufferSize = 4 MB;
102 ress->srcBuffer = malloc(ress->srcBufferSize);
103 ress->dstBufferSize = 4 MB;
104 ress->dstBuffer = malloc(ress->dstBufferSize);
105
106 if (!ress->srcBuffer || !ress->dstBuffer) {
107 free(ress->srcBuffer);
108 free(ress->dstBuffer);
109 EXM_THROW(20, "Allocation error : not enough memory");
110 }
111
112 if (LZ4F_isError( LZ4F_createDecompressionContext(&(ress->ctx), LZ4F_VERSION) )) {
113 free(ress->srcBuffer);
114 free(ress->dstBuffer);
115 EXM_THROW(21, "Unable to create decompression context");
116 }
117 return 0;
118 }
119
freeCResources(cRess_t ress)120 static void freeCResources(cRess_t ress)
121 {
122 free(ress.srcBuffer);
123 free(ress.dstBuffer);
124
125 (void) LZ4F_freeDecompressionContext(ress.ctx);
126 }
127
frameCheck(cRess_t ress,FILE * const srcFile,unsigned bsid,size_t blockSize)128 int frameCheck(cRess_t ress, FILE* const srcFile, unsigned bsid, size_t blockSize)
129 {
130 LZ4F_errorCode_t nextToLoad = 0;
131 size_t curblocksize = 0;
132 int partialBlock = 0;
133
134 /* Main Loop */
135 for (;;) {
136 size_t readSize;
137 size_t pos = 0;
138 size_t decodedBytes = ress.dstBufferSize;
139 size_t remaining;
140 LZ4F_frameInfo_t frameInfo;
141
142 /* Read input */
143 readSize = fread(ress.srcBuffer, 1, ress.srcBufferSize, srcFile);
144 if (!readSize) break; /* reached end of file or stream */
145
146 while (pos < readSize) { /* still to read */
147 /* Decode Input (at least partially) */
148 if (!nextToLoad) {
149 /* LZ4F_decompress returned 0 : starting new frame */
150 curblocksize = 0;
151 remaining = readSize - pos;
152 nextToLoad = LZ4F_getFrameInfo(ress.ctx, &frameInfo, (char*)(ress.srcBuffer)+pos, &remaining);
153 if (LZ4F_isError(nextToLoad))
154 EXM_THROW(22, "Error getting frame info: %s",
155 LZ4F_getErrorName(nextToLoad));
156 if (frameInfo.blockSizeID != (LZ4F_blockSizeID_t) bsid)
157 EXM_THROW(23, "Block size ID %u != expected %u",
158 frameInfo.blockSizeID, bsid);
159 pos += remaining;
160 /* nextToLoad should be block header size */
161 remaining = nextToLoad;
162 decodedBytes = ress.dstBufferSize;
163 nextToLoad = LZ4F_decompress(ress.ctx, ress.dstBuffer, &decodedBytes, (char*)(ress.srcBuffer)+pos, &remaining, NULL);
164 if (LZ4F_isError(nextToLoad)) EXM_THROW(24, "Decompression error : %s", LZ4F_getErrorName(nextToLoad));
165 pos += remaining;
166 }
167 decodedBytes = ress.dstBufferSize;
168 /* nextToLoad should be just enough to cover the next block */
169 if (nextToLoad > (readSize - pos)) {
170 /* block is not fully contained in current buffer */
171 partialBlock = 1;
172 remaining = readSize - pos;
173 } else {
174 if (partialBlock) {
175 partialBlock = 0;
176 }
177 remaining = nextToLoad;
178 }
179 nextToLoad = LZ4F_decompress(ress.ctx, ress.dstBuffer, &decodedBytes, (char*)(ress.srcBuffer)+pos, &remaining, NULL);
180 if (LZ4F_isError(nextToLoad)) EXM_THROW(24, "Decompression error : %s", LZ4F_getErrorName(nextToLoad));
181 curblocksize += decodedBytes;
182 pos += remaining;
183 if (!partialBlock) {
184 /* detect small block due to end of frame; the final 4-byte frame checksum could be left in the buffer */
185 if ((curblocksize != 0) && (nextToLoad > 4)) {
186 if (curblocksize != blockSize)
187 EXM_THROW(25, "Block size %u != expected %u, pos %u\n",
188 (unsigned)curblocksize, (unsigned)blockSize, (unsigned)pos);
189 }
190 curblocksize = 0;
191 }
192 }
193 }
194 /* can be out because readSize == 0, which could be an fread() error */
195 if (ferror(srcFile)) EXM_THROW(26, "Read error");
196
197 if (nextToLoad!=0) EXM_THROW(27, "Unfinished stream");
198
199 return 0;
200 }
201
FUZ_usage(const char * programName)202 int FUZ_usage(const char* programName)
203 {
204 DISPLAY( "Usage :\n");
205 DISPLAY( " %s [args] filename\n", programName);
206 DISPLAY( "\n");
207 DISPLAY( "Arguments :\n");
208 DISPLAY( " -b# : expected blocksizeID [4-7] (required)\n");
209 DISPLAY( " -B# : expected blocksize [32-4194304] (required)\n");
210 DISPLAY( " -v : verbose\n");
211 DISPLAY( " -h : display help and exit\n");
212 return 0;
213 }
214
215
main(int argc,const char ** argv)216 int main(int argc, const char** argv)
217 {
218 int argNb;
219 unsigned bsid=0;
220 size_t blockSize=0;
221 const char* const programName = argv[0];
222
223 /* Check command line */
224 for (argNb=1; argNb<argc; argNb++) {
225 const char* argument = argv[argNb];
226
227 if(!argument) continue; /* Protection if argument empty */
228
229 /* Decode command (note : aggregated short commands are allowed) */
230 if (argument[0]=='-') {
231 if (!strcmp(argument, "--no-prompt")) {
232 no_prompt=1;
233 displayLevel=1;
234 continue;
235 }
236 argument++;
237
238 while (*argument!=0) {
239 switch(*argument)
240 {
241 case 'h':
242 return FUZ_usage(programName);
243 case 'v':
244 argument++;
245 displayLevel++;
246 break;
247 case 'q':
248 argument++;
249 displayLevel--;
250 break;
251 case 'p': /* pause at the end */
252 argument++;
253 use_pause = 1;
254 break;
255
256 case 'b':
257 argument++;
258 bsid=0;
259 while ((*argument>='0') && (*argument<='9')) {
260 bsid *= 10;
261 bsid += (unsigned)(*argument - '0');
262 argument++;
263 }
264 break;
265
266 case 'B':
267 argument++;
268 blockSize=0;
269 while ((*argument>='0') && (*argument<='9')) {
270 blockSize *= 10;
271 blockSize += (size_t)(*argument - '0');
272 argument++;
273 }
274 break;
275
276 default:
277 ;
278 return FUZ_usage(programName);
279 }
280 }
281 } else {
282 int err;
283 FILE *srcFile;
284 cRess_t ress;
285 if (bsid == 0 || blockSize == 0)
286 return FUZ_usage(programName);
287 DISPLAY("Starting frame checker (%i-bits, %s)\n", (int)(sizeof(size_t)*8), LZ4_VERSION_STRING);
288 err = createCResources(&ress);
289 if (err) return (err);
290 srcFile = fopen(argument, "rb");
291 if ( srcFile==NULL ) {
292 freeCResources(ress);
293 EXM_THROW(1, "%s: %s \n", argument, strerror(errno));
294 }
295 assert (srcFile != NULL);
296 err = frameCheck(ress, srcFile, bsid, blockSize);
297 freeCResources(ress);
298 fclose(srcFile);
299 return (err);
300 }
301 }
302 return 0;
303 }
304