Line | Count | Source (jump to first uncovered line) |
1 | | /* minigzip.c -- simulate gzip using the zlib compression library |
2 | | * Copyright (C) 1995-2006, 2010, 2011, 2016 Jean-loup Gailly |
3 | | * For conditions of distribution and use, see copyright notice in zlib.h |
4 | | */ |
5 | | |
6 | | /* |
7 | | * minigzip is a minimal implementation of the gzip utility. This is |
8 | | * only an example of using zlib and isn't meant to replace the |
9 | | * full-featured gzip. No attempt is made to deal with file systems |
10 | | * limiting names to 14 or 8+3 characters, etc... Error checking is |
11 | | * very limited. So use minigzip only for testing; use gzip for the |
12 | | * real thing. |
13 | | */ |
14 | | |
15 | | /* @(#) $Id$ */ |
16 | | |
17 | | #include "zlib.h" |
18 | | #include <stdio.h> |
19 | | #include <assert.h> |
20 | | #include <string.h> |
21 | | #include <stdlib.h> |
22 | | #include <inttypes.h> |
23 | | |
24 | | #ifdef USE_MMAP |
25 | | # include <sys/types.h> |
26 | | # include <sys/mman.h> |
27 | | # include <sys/stat.h> |
28 | | #endif |
29 | | |
30 | | #ifndef UNALIGNED_OK |
31 | | # include <malloc.h> |
32 | | #endif |
33 | | |
34 | | #if defined(WIN32) || defined(__CYGWIN__) |
35 | | # include <fcntl.h> |
36 | | # include <io.h> |
37 | | # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) |
38 | | #else |
39 | | # define SET_BINARY_MODE(file) |
40 | | #endif |
41 | | |
42 | | #if defined(_MSC_VER) && _MSC_VER < 1900 |
43 | | # define snprintf _snprintf |
44 | | #endif |
45 | | |
46 | | #if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE) |
47 | | #ifndef WIN32 /* unlink already in stdio.h for WIN32 */ |
48 | | extern int unlink (const char *); |
49 | | #endif |
50 | | #endif |
51 | | |
52 | | #ifndef GZ_SUFFIX |
53 | 22.4k | # define GZ_SUFFIX ".gz" |
54 | | #endif |
55 | 11.2k | #define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1) |
56 | | |
57 | | #define BUFLEN 16384 /* read buffer size */ |
58 | | #define BUFLENW (BUFLEN * 3) /* write buffer size */ |
59 | | #define MAX_NAME_LEN 1024 |
60 | | |
61 | | #ifdef Z_SOLO |
62 | | /* for Z_SOLO, create simplified gz* functions using deflate and inflate */ |
63 | | |
64 | | #if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE) |
65 | | # include <unistd.h> /* for unlink() */ |
66 | | #endif |
67 | | |
68 | | void *myalloc (void *, unsigned, unsigned); |
69 | | void myfree (void *, void *); |
70 | | |
71 | | void *myalloc(void *q, unsigned n, unsigned m) |
72 | | { |
73 | | (void)q; |
74 | | #ifndef UNALIGNED_OK |
75 | | return memalign(16, n * m); |
76 | | #else |
77 | | return calloc(n, m); |
78 | | #endif |
79 | | } |
80 | | |
81 | | void myfree(void *q, void *p) |
82 | | { |
83 | | (void)q; |
84 | | free(p); |
85 | | } |
86 | | |
87 | | typedef struct gzFile_s { |
88 | | FILE *file; |
89 | | int write; |
90 | | int err; |
91 | | const char *msg; |
92 | | z_stream strm; |
93 | | unsigned char *buf; |
94 | | } *gzFile; |
95 | | |
96 | | gzFile gzopen(const char *, const char *); |
97 | | gzFile gzdopen(int, const char *); |
98 | | gzFile gz_open (const char *, int, const char *); |
99 | | |
100 | | gzFile gzopen(const char *path, const char *mode) |
101 | | { |
102 | | return gz_open(path, -1, mode); |
103 | | } |
104 | | |
105 | | gzFile gzdopen(int fd, const char *mode) |
106 | | { |
107 | | return gz_open(NULL, fd, mode); |
108 | | } |
109 | | |
110 | | gzFile gz_open(const char *path, int fd, const char *mode) |
111 | | { |
112 | | gzFile gz; |
113 | | int ret; |
114 | | int level = Z_DEFAULT_COMPRESSION; |
115 | | const char *plevel = mode; |
116 | | |
117 | | gz = malloc(sizeof(struct gzFile_s)); |
118 | | if (gz == NULL) |
119 | | return NULL; |
120 | | gz->write = strchr(mode, 'w') != NULL; |
121 | | gz->strm.zalloc = myalloc; |
122 | | gz->strm.zfree = myfree; |
123 | | gz->strm.opaque = NULL; |
124 | | gz->buf = malloc(gz->write ? BUFLENW : BUFLEN); |
125 | | |
126 | | if (gz->buf == NULL) { |
127 | | free(gz); |
128 | | return NULL; |
129 | | } |
130 | | |
131 | | while (*plevel) { |
132 | | if (*plevel >= '0' && *plevel <= '9') { |
133 | | level = *plevel - '0'; |
134 | | break; |
135 | | } |
136 | | plevel++; |
137 | | } |
138 | | if (gz->write) |
139 | | ret = deflateInit2(&(gz->strm), level, 8, 15 + 16, 8, 0); |
140 | | else { |
141 | | gz->strm.next_in = NULL; |
142 | | gz->strm.avail_in = 0; |
143 | | ret = inflateInit2(&(gz->strm), 15 + 16); |
144 | | } |
145 | | if (ret != Z_OK) { |
146 | | free(gz); |
147 | | return NULL; |
148 | | } |
149 | | gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") : |
150 | | fopen(path, gz->write ? "wb" : "rb"); |
151 | | if (gz->file == NULL) { |
152 | | gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm)); |
153 | | free(gz); |
154 | | return NULL; |
155 | | } |
156 | | gz->err = 0; |
157 | | gz->msg = ""; |
158 | | return gz; |
159 | | } |
160 | | |
161 | | int gzwrite(gzFile, const void *, unsigned); |
162 | | |
163 | | int gzwrite(gzFile gz, const void *buf, unsigned len) |
164 | | { |
165 | | z_stream *strm; |
166 | | |
167 | | if (gz == NULL || !gz->write) |
168 | | return 0; |
169 | | strm = &(gz->strm); |
170 | | strm->next_in = (void *)buf; |
171 | | strm->avail_in = len; |
172 | | do { |
173 | | strm->next_out = gz->buf; |
174 | | strm->avail_out = BUFLENW; |
175 | | (void)deflate(strm, Z_NO_FLUSH); |
176 | | fwrite(gz->buf, 1, BUFLENW - strm->avail_out, gz->file); |
177 | | } while (strm->avail_out == 0); |
178 | | return len; |
179 | | } |
180 | | |
181 | | int gzread(gzFile, void *, unsigned); |
182 | | |
183 | | int gzread(gzFile gz, void *buf, unsigned len) |
184 | | { |
185 | | z_stream *strm; |
186 | | |
187 | | if (gz == NULL || gz->write || gz->err) |
188 | | return 0; |
189 | | strm = &(gz->strm); |
190 | | strm->next_out = buf; |
191 | | strm->avail_out = len; |
192 | | do { |
193 | | if (strm->avail_in == 0) |
194 | | { |
195 | | strm->next_in = gz->buf; |
196 | | strm->avail_in = (uint32_t)fread(gz->buf, 1, BUFLEN, gz->file); |
197 | | } |
198 | | if (strm->avail_in > 0) |
199 | | { |
200 | | int ret = inflate(strm, Z_NO_FLUSH); |
201 | | if (ret == Z_DATA_ERROR) { |
202 | | gz->err = ret; |
203 | | gz->msg = strm->msg; |
204 | | return 0; |
205 | | } |
206 | | else if (ret == Z_STREAM_END) |
207 | | inflateReset(strm); |
208 | | } |
209 | | else |
210 | | break; |
211 | | } while (strm->avail_out); |
212 | | return len - strm->avail_out; |
213 | | } |
214 | | |
215 | | int gzclose(gzFile); |
216 | | |
217 | | int gzclose(gzFile gz) |
218 | | { |
219 | | z_stream *strm; |
220 | | |
221 | | if (gz == NULL) |
222 | | return Z_STREAM_ERROR; |
223 | | strm = &(gz->strm); |
224 | | if (gz->write) { |
225 | | strm->next_in = NULL; |
226 | | strm->avail_in = 0; |
227 | | do { |
228 | | strm->next_out = gz->buf; |
229 | | strm->avail_out = BUFLENW; |
230 | | (void)deflate(strm, Z_FINISH); |
231 | | fwrite(gz->buf, 1, BUFLENW - strm->avail_out, gz->file); |
232 | | } while (strm->avail_out == 0); |
233 | | deflateEnd(strm); |
234 | | } |
235 | | else |
236 | | inflateEnd(strm); |
237 | | free(gz->buf); |
238 | | fclose(gz->file); |
239 | | free(gz); |
240 | | return Z_OK; |
241 | | } |
242 | | |
243 | | const char *gzerror(gzFile, int *); |
244 | | |
245 | | const char *gzerror(gzFile gz, int *err) |
246 | | { |
247 | | *err = gz->err; |
248 | | return gz->msg; |
249 | | } |
250 | | |
251 | | #endif |
252 | | |
253 | | static char *prog; |
254 | | |
255 | | int error (const char *msg); |
256 | | int gz_compress (FILE *in, gzFile out); |
257 | | #ifdef USE_MMAP |
258 | | int gz_compress_mmap (FILE *in, gzFile out); |
259 | | #endif |
260 | | void gz_uncompress (gzFile in, FILE *out); |
261 | | int file_compress (char *file, char *mode); |
262 | | int file_uncompress (char *file); |
263 | | int main (int argc, char *argv[]); |
264 | | |
265 | | /* =========================================================================== |
266 | | * Display error message and return |
267 | | */ |
268 | | int error(const char *msg) |
269 | 0 | { |
270 | 0 | fprintf(stderr, "%s: %s\n", prog, msg); |
271 | 0 | return 0; |
272 | 0 | } |
273 | | |
274 | | /* =========================================================================== |
275 | | * Compress input to output then close both files. |
276 | | */ |
277 | | |
278 | | int gz_compress(FILE *in, gzFile out) |
279 | 3.74k | { |
280 | 3.74k | char buf[BUFLEN]; |
281 | 3.74k | int len; |
282 | 3.74k | int err; |
283 | | |
284 | | #ifdef USE_MMAP |
285 | | /* Try first compressing with mmap. If mmap fails (minigzip used in a |
286 | | * pipe), use the normal fread loop. |
287 | | */ |
288 | | if (gz_compress_mmap(in, out) == Z_OK) return; |
289 | | #endif |
290 | | /* Clear out the contents of buf before reading from the file to avoid |
291 | | MemorySanitizer: use-of-uninitialized-value warnings. */ |
292 | 3.74k | memset(buf, 0, sizeof(buf)); |
293 | 19.7k | for (;;) { |
294 | 19.7k | len = (int)fread(buf, 1, sizeof(buf), in); |
295 | 19.7k | if (ferror(in)) { |
296 | 0 | perror("fread"); |
297 | 0 | return 0; |
298 | 0 | } |
299 | 19.7k | if (len == 0) break; |
300 | | |
301 | 16.0k | if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err)); |
302 | 16.0k | } |
303 | 3.74k | fclose(in); |
304 | 3.74k | if (gzclose(out) != Z_OK) error("failed gzclose"); |
305 | 3.74k | return 0; |
306 | 3.74k | } |
307 | | |
308 | | #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */ |
309 | | |
310 | | /* Try compressing the input file at once using mmap. Return Z_OK if |
311 | | * if success, Z_ERRNO otherwise. |
312 | | */ |
313 | | int gz_compress_mmap(FILE *in, gzFile out) |
314 | | { |
315 | | int len; |
316 | | int err; |
317 | | int ifd = fileno(in); |
318 | | caddr_t buf; /* mmap'ed buffer for the entire input file */ |
319 | | off_t buf_len; /* length of the input file */ |
320 | | struct stat sb; |
321 | | |
322 | | /* Determine the size of the file, needed for mmap: */ |
323 | | if (fstat(ifd, &sb) < 0) return Z_ERRNO; |
324 | | buf_len = sb.st_size; |
325 | | if (buf_len <= 0) return Z_ERRNO; |
326 | | |
327 | | /* Now do the actual mmap: */ |
328 | | buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0); |
329 | | if (buf == (caddr_t)(-1)) return Z_ERRNO; |
330 | | |
331 | | /* Compress the whole file at once: */ |
332 | | len = gzwrite(out, (char *)buf, (unsigned)buf_len); |
333 | | |
334 | | if (len != (int)buf_len) error(gzerror(out, &err)); |
335 | | |
336 | | munmap(buf, buf_len); |
337 | | fclose(in); |
338 | | if (gzclose(out) != Z_OK) error("failed gzclose"); |
339 | | return Z_OK; |
340 | | } |
341 | | #endif /* USE_MMAP */ |
342 | | |
343 | | /* =========================================================================== |
344 | | * Uncompress input to output then close both files. |
345 | | */ |
346 | | void gz_uncompress(gzFile in, FILE *out) |
347 | 3.74k | { |
348 | 3.74k | char buf[BUFLENW]; |
349 | 3.74k | int len; |
350 | 3.74k | int err; |
351 | | |
352 | 11.1k | for (;;) { |
353 | 11.1k | len = gzread(in, buf, sizeof(buf)); |
354 | 11.1k | if (len < 0) error (gzerror(in, &err)); |
355 | 11.1k | if (len == 0) break; |
356 | | |
357 | 7.38k | if ((int)fwrite(buf, 1, (unsigned)len, out) != len) { |
358 | 0 | error("failed fwrite"); |
359 | 0 | } |
360 | 7.38k | } |
361 | 3.74k | if (fclose(out)) error("failed fclose"); |
362 | | |
363 | 3.74k | if (gzclose(in) != Z_OK) error("failed gzclose"); |
364 | 3.74k | } |
365 | | |
366 | | |
367 | | /* =========================================================================== |
368 | | * Compress the given file: create a corresponding .gz file and remove the |
369 | | * original. |
370 | | */ |
371 | | int file_compress(char *file, char *mode) |
372 | 3.74k | { |
373 | 3.74k | char outfile[MAX_NAME_LEN]; |
374 | 3.74k | FILE *in; |
375 | 3.74k | gzFile out; |
376 | | |
377 | 3.74k | if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) { |
378 | 0 | fprintf(stderr, "%s: filename too long\n", prog); |
379 | 0 | return 0; |
380 | 0 | } |
381 | | |
382 | 3.74k | snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX); |
383 | | |
384 | 3.74k | in = fopen(file, "rb"); |
385 | 3.74k | if (in == NULL) { |
386 | 0 | perror(file); |
387 | 0 | return 0; |
388 | 0 | } |
389 | 3.74k | out = gzopen(outfile, mode); |
390 | 3.74k | if (out == NULL) { |
391 | 0 | fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile); |
392 | 0 | return 0; |
393 | 0 | } |
394 | 3.74k | gz_compress(in, out); |
395 | | |
396 | 3.74k | unlink(file); |
397 | 3.74k | return 0; |
398 | 3.74k | } |
399 | | |
400 | | |
401 | | /* =========================================================================== |
402 | | * Uncompress the given file and remove the original. |
403 | | */ |
404 | | int file_uncompress(char *file) |
405 | 3.74k | { |
406 | 3.74k | char buf[MAX_NAME_LEN]; |
407 | 3.74k | char *infile, *outfile; |
408 | 3.74k | FILE *out; |
409 | 3.74k | gzFile in; |
410 | 3.74k | size_t len = strlen(file); |
411 | | |
412 | 3.74k | if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) { |
413 | 0 | fprintf(stderr, "%s: filename too long\n", prog); |
414 | 0 | return 0; |
415 | 0 | } |
416 | | |
417 | 3.74k | snprintf(buf, sizeof(buf), "%s", file); |
418 | | |
419 | 3.74k | if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) { |
420 | 3.74k | infile = file; |
421 | 3.74k | outfile = buf; |
422 | 3.74k | outfile[len-3] = '\0'; |
423 | 3.74k | } else { |
424 | 0 | outfile = file; |
425 | 0 | infile = buf; |
426 | 0 | snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX); |
427 | 0 | } |
428 | 3.74k | in = gzopen(infile, "rb"); |
429 | 3.74k | if (in == NULL) { |
430 | 0 | fprintf(stderr, "%s: can't gzopen %s\n", prog, infile); |
431 | 0 | return 0; |
432 | 0 | } |
433 | 3.74k | out = fopen(outfile, "wb"); |
434 | 3.74k | if (out == NULL) { |
435 | 0 | perror(file); |
436 | 0 | return 0; |
437 | 0 | } |
438 | | |
439 | 3.74k | gz_uncompress(in, out); |
440 | | |
441 | 3.74k | unlink(infile); |
442 | 3.74k | return 0; |
443 | 3.74k | } |
444 | | |
445 | 3.74k | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataLen) { |
446 | 3.74k | char *inFileName = "/tmp/minigzip_fuzzer.out"; |
447 | 3.74k | char *outFileName = "/tmp/minigzip_fuzzer.out.gz"; |
448 | 3.74k | char outmode[20]; |
449 | 3.74k | FILE *in; |
450 | 3.74k | char buf[BUFLEN]; |
451 | 3.74k | uint32_t offset = 0; |
452 | | |
453 | | /* Discard inputs larger than 1Mb. */ |
454 | 3.74k | static size_t kMaxSize = 1024 * 1024; |
455 | 3.74k | if (dataLen < 1 || dataLen > kMaxSize) |
456 | 0 | return 0; |
457 | | |
458 | 3.74k | in = fopen(inFileName, "w"); |
459 | 3.74k | if (fwrite(data, 1, (unsigned)dataLen, in) != dataLen) |
460 | 0 | error("failed fwrite"); |
461 | 3.74k | if (fclose(in)) |
462 | 0 | error("failed fclose"); |
463 | | |
464 | 3.74k | memset(outmode, 0, sizeof(outmode)); |
465 | 3.74k | snprintf(outmode, sizeof(outmode), "%s", "wb"); |
466 | | |
467 | | /* Compression level: [0..9]. */ |
468 | 3.74k | outmode[2] = data[0] % 10; |
469 | | |
470 | 3.74k | switch (data[0] % 4) { |
471 | 0 | default: |
472 | 1.12k | case 0: |
473 | 1.12k | outmode[3] = 0; |
474 | 1.12k | break; |
475 | 729 | case 1: |
476 | | /* compress with Z_FILTERED */ |
477 | 729 | outmode[3] = 'f'; |
478 | 729 | break; |
479 | 959 | case 2: |
480 | | /* compress with Z_HUFFMAN_ONLY */ |
481 | 959 | outmode[3] = 'h'; |
482 | 959 | break; |
483 | 932 | case 3: |
484 | | /* compress with Z_RLE */ |
485 | 932 | outmode[3] = 'R'; |
486 | 932 | break; |
487 | 3.74k | } |
488 | | |
489 | 3.74k | file_compress(inFileName, outmode); |
490 | 3.74k | file_uncompress(outFileName); |
491 | | |
492 | | /* Check that the uncompressed file matches the input data. */ |
493 | 3.74k | in = fopen(inFileName, "rb"); |
494 | 3.74k | if (in == NULL) { |
495 | 0 | perror(inFileName); |
496 | 0 | return 0; |
497 | 0 | } |
498 | | |
499 | 3.74k | memset(buf, 0, sizeof(buf)); |
500 | 19.7k | for (;;) { |
501 | 19.7k | int len = (int)fread(buf, 1, sizeof(buf), in); |
502 | 19.7k | if (ferror(in)) { |
503 | 0 | perror("fread"); |
504 | 0 | return 0; |
505 | 0 | } |
506 | 19.7k | if (len == 0) |
507 | 3.74k | break; |
508 | 16.0k | assert(0 == memcmp(data + offset, buf, len)); |
509 | 16.0k | offset += len; |
510 | 16.0k | } |
511 | | |
512 | 3.74k | if (fclose(in)) |
513 | 0 | error("failed fclose"); |
514 | | |
515 | | /* This function must return 0. */ |
516 | 3.74k | return 0; |
517 | 3.74k | } |