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