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