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