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