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 #include "zbuild.h"
16 #ifdef ZLIB_COMPAT
17 #  include "zlib.h"
18 #else
19 #  include "zlib-ng.h"
20 #endif
21 #include <stdio.h>
22 #include <assert.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 #if defined(_WIN32) || defined(__CYGWIN__)
31 #  include <fcntl.h>
32 #  include <io.h>
33 #  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
34 #else
35 #  define SET_BINARY_MODE(file)
36 #endif
37 
38 #if defined(_MSC_VER) && _MSC_VER < 1900
39 #  define snprintf _snprintf
40 #endif
41 
42 #if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE)
43 #ifndef _WIN32 /* unlink already in stdio.h for Win32 */
44 extern int unlink (const char *);
45 #endif
46 #endif
47 
48 #ifndef GZ_SUFFIX
49 #  define GZ_SUFFIX ".gz"
50 #endif
51 #define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
52 
53 #define BUFLEN      16384        /* read buffer size */
54 #define BUFLENW     (BUFLEN * 3) /* write buffer size */
55 #define MAX_NAME_LEN 1024
56 
57 static const char *prog = "minigzip_fuzzer";
58 
59 void error            (const char *msg);
60 void gz_compress      (FILE *in, gzFile out);
61 #ifdef USE_MMAP
62 int  gz_compress_mmap (FILE *in, gzFile out);
63 #endif
64 void gz_uncompress    (gzFile in, FILE *out);
65 void file_compress    (char *file, char *mode);
66 void file_uncompress  (char *file);
67 int  main             (int argc, char *argv[]);
68 
69 /* ===========================================================================
70  * Display error message and exit
71  */
error(const char * msg)72 void error(const char *msg) {
73     fprintf(stderr, "%s: %s\n", prog, msg);
74     exit(1);
75 }
76 
77 /* ===========================================================================
78  * Compress input to output then close both files.
79  */
80 
gz_compress(FILE * in,gzFile out)81 void gz_compress(FILE *in, gzFile out) {
82     char buf[BUFLEN];
83     int len;
84     int err;
85 
86 #ifdef USE_MMAP
87     /* Try first compressing with mmap. If mmap fails (minigzip used in a
88      * pipe), use the normal fread loop.
89      */
90     if (gz_compress_mmap(in, out) == Z_OK) return;
91 #endif
92     /* Clear out the contents of buf before reading from the file to avoid
93        MemorySanitizer: use-of-uninitialized-value warnings. */
94     memset(buf, 0, sizeof(buf));
95     for (;;) {
96         len = (int)fread(buf, 1, sizeof(buf), in);
97         if (ferror(in)) {
98             perror("fread");
99             exit(1);
100         }
101         if (len == 0) break;
102 
103         if (PREFIX(gzwrite)(out, buf, (unsigned)len) != len) error(PREFIX(gzerror)(out, &err));
104     }
105     fclose(in);
106     if (PREFIX(gzclose)(out) != Z_OK) error("failed gzclose");
107 }
108 
109 #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <[email protected]> */
110 
111 /* Try compressing the input file at once using mmap. Return Z_OK if
112  * if success, Z_ERRNO otherwise.
113  */
gz_compress_mmap(FILE * in,gzFile out)114 int gz_compress_mmap(FILE *in, gzFile out) {
115     int len;
116     int err;
117     int ifd = fileno(in);
118     char *buf;      /* mmap'ed buffer for the entire input file */
119     off_t buf_len;  /* length of the input file */
120     struct stat sb;
121 
122     /* Determine the size of the file, needed for mmap: */
123     if (fstat(ifd, &sb) < 0) return Z_ERRNO;
124     buf_len = sb.st_size;
125     if (buf_len <= 0) return Z_ERRNO;
126 
127     /* Now do the actual mmap: */
128     buf = mmap((void *)0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
129     if (buf == (char *)(-1)) return Z_ERRNO;
130 
131     /* Compress the whole file at once: */
132     len = PREFIX(gzwrite)(out, (char *)buf, (unsigned)buf_len);
133 
134     if (len != (int)buf_len) error(PREFIX(gzerror)(out, &err));
135 
136     munmap(buf, buf_len);
137     fclose(in);
138     if (PREFIX(gzclose)(out) != Z_OK) error("failed gzclose");
139     return Z_OK;
140 }
141 #endif /* USE_MMAP */
142 
143 /* ===========================================================================
144  * Uncompress input to output then close both files.
145  */
gz_uncompress(gzFile in,FILE * out)146 void gz_uncompress(gzFile in, FILE *out) {
147     char buf[BUFLENW];
148     int len;
149     int err;
150 
151     for (;;) {
152         len = PREFIX(gzread)(in, buf, sizeof(buf));
153         if (len < 0) error (PREFIX(gzerror)(in, &err));
154         if (len == 0) break;
155 
156         if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
157             error("failed fwrite");
158         }
159     }
160     if (fclose(out)) error("failed fclose");
161 
162     if (PREFIX(gzclose)(in) != Z_OK) error("failed gzclose");
163 }
164 
165 
166 /* ===========================================================================
167  * Compress the given file: create a corresponding .gz file and remove the
168  * original.
169  */
file_compress(char * file,char * mode)170 void file_compress(char *file, char *mode) {
171     char outfile[MAX_NAME_LEN];
172     FILE *in;
173     gzFile out;
174 
175     if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
176         fprintf(stderr, "%s: filename too long\n", prog);
177         exit(1);
178     }
179 
180     snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX);
181 
182     in = fopen(file, "rb");
183     if (in == NULL) {
184         perror(file);
185         exit(1);
186     }
187     out = PREFIX(gzopen)(outfile, mode);
188     if (out == NULL) {
189         fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
190         exit(1);
191     }
192     gz_compress(in, out);
193 
194     unlink(file);
195 }
196 
197 
198 /* ===========================================================================
199  * Uncompress the given file and remove the original.
200  */
file_uncompress(char * file)201 void file_uncompress(char *file) {
202     char buf[MAX_NAME_LEN];
203     char *infile, *outfile;
204     FILE *out;
205     gzFile in;
206     size_t len = strlen(file);
207 
208     if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
209         fprintf(stderr, "%s: filename too long\n", prog);
210         exit(1);
211     }
212 
213     snprintf(buf, sizeof(buf), "%s", file);
214 
215     if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
216         infile = file;
217         outfile = buf;
218         outfile[len-3] = '\0';
219     } else {
220         outfile = file;
221         infile = buf;
222         snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX);
223     }
224     in = PREFIX(gzopen)(infile, "rb");
225     if (in == NULL) {
226         fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
227         exit(1);
228     }
229     out = fopen(outfile, "wb");
230     if (out == NULL) {
231         perror(file);
232         exit(1);
233     }
234 
235     gz_uncompress(in, out);
236 
237     unlink(infile);
238 }
239 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t dataLen)240 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataLen) {
241     char *inFileName = "minigzip_fuzzer.out";
242     char *outFileName = "minigzip_fuzzer.out.gz";
243     char outmode[20];
244     FILE *in;
245     char buf[BUFLEN];
246     uint32_t offset = 0;
247 
248     /* Discard inputs larger than 1Mb. */
249     static size_t kMaxSize = 1024 * 1024;
250     if (dataLen < 1 || dataLen > kMaxSize)
251         return 0;
252 
253     in = fopen(inFileName, "wb");
254     if (fwrite(data, 1, (unsigned)dataLen, in) != dataLen)
255         error("failed fwrite");
256     if (fclose(in))
257         error("failed fclose");
258 
259     memset(outmode, 0, sizeof(outmode));
260     snprintf(outmode, sizeof(outmode), "%s", "wb");
261 
262     /* Compression level: [0..9]. */
263     outmode[2] = '0' + (data[0] % 10);
264 
265     switch (data[dataLen-1] % 6) {
266     default:
267     case 0:
268         outmode[3] = 0;
269         break;
270     case 1:
271         /* compress with Z_FILTERED */
272         outmode[3] = 'f';
273         break;
274     case 2:
275         /* compress with Z_HUFFMAN_ONLY */
276         outmode[3] = 'h';
277         break;
278     case 3:
279         /* compress with Z_RLE */
280         outmode[3] = 'R';
281         break;
282     case 4:
283         /* compress with Z_FIXED */
284         outmode[3] = 'F';
285         break;
286     case 5:
287         /* direct */
288         outmode[3] = 'T';
289         break;
290     }
291 
292     file_compress(inFileName, outmode);
293 
294     /* gzopen does not support reading in direct mode */
295     if (outmode[3] == 'T')
296         inFileName = outFileName;
297     else
298         file_uncompress(outFileName);
299 
300     /* Check that the uncompressed file matches the input data. */
301     in = fopen(inFileName, "rb");
302     if (in == NULL) {
303         perror(inFileName);
304         exit(1);
305     }
306 
307     memset(buf, 0, sizeof(buf));
308     for (;;) {
309         int len = (int)fread(buf, 1, sizeof(buf), in);
310         if (ferror(in)) {
311             perror("fread");
312             exit(1);
313         }
314         if (len == 0)
315             break;
316         assert(0 == memcmp(data + offset, buf, len));
317         offset += len;
318     }
319 
320     if (fclose(in))
321         error("failed fclose");
322 
323     /* This function must return 0. */
324     return 0;
325 }
326