/src/wuffs/fuzz/c/fuzzlib/fuzzlib.c
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2018 The Wuffs Authors. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
4 | | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
5 | | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
6 | | // option. This file may not be copied, modified, or distributed |
7 | | // except according to those terms. |
8 | | // |
9 | | // SPDX-License-Identifier: Apache-2.0 OR MIT |
10 | | |
11 | | #include <inttypes.h> |
12 | | #include <stdio.h> |
13 | | #include <stdlib.h> |
14 | | #include <string.h> |
15 | | |
16 | | #ifndef WUFFS_INCLUDE_GUARD |
17 | | #error "Wuffs' .h files need to be included before this file" |
18 | | #endif |
19 | | |
20 | | void // |
21 | 0 | intentional_segfault() { |
22 | 0 | static volatile int* ptr = NULL; |
23 | 0 | *ptr = 0; |
24 | 0 | } |
25 | | |
26 | | static uint32_t // |
27 | 1.10k | popcount32(uint32_t x) { |
28 | 1.10k | static const uint8_t table[256] = { |
29 | 1.10k | 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, // |
30 | 1.10k | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // |
31 | 1.10k | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // |
32 | 1.10k | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // |
33 | 1.10k | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // |
34 | 1.10k | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // |
35 | 1.10k | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // |
36 | 1.10k | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // |
37 | 1.10k | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // |
38 | 1.10k | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // |
39 | 1.10k | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // |
40 | 1.10k | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // |
41 | 1.10k | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // |
42 | 1.10k | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // |
43 | 1.10k | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // |
44 | 1.10k | 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, // |
45 | 1.10k | }; |
46 | 1.10k | return table[0xFF & (x >> 0)] + table[0xFF & (x >> 8)] + |
47 | 1.10k | table[0xFF & (x >> 16)] + table[0xFF & (x >> 24)]; |
48 | 1.10k | } |
49 | | |
50 | | // jenkins_hash_u32 implements |
51 | | // https://en.wikipedia.org/wiki/Jenkins_hash_function |
52 | | static uint32_t // |
53 | 2.20k | jenkins_hash_u32(const uint8_t* data, size_t size) { |
54 | 2.20k | uint32_t hash = 0; |
55 | 2.20k | size_t i = 0; |
56 | 30.1M | while (i != size) { |
57 | 30.1M | hash += data[i++]; |
58 | 30.1M | hash += hash << 10; |
59 | 30.1M | hash ^= hash >> 6; |
60 | 30.1M | } |
61 | 2.20k | hash += hash << 3; |
62 | 2.20k | hash ^= hash >> 11; |
63 | 2.20k | hash += hash << 15; |
64 | 2.20k | return hash; |
65 | 2.20k | } |
66 | | |
67 | | // memrandomize is like memcpy or memset but it writes pseudo-random values. |
68 | | static void* // |
69 | 0 | memrandomize(void* dest, uint64_t seed, size_t n) { |
70 | 0 | unsigned short xsubi[3]; // See "man 3 nrand48". |
71 | 0 | xsubi[0] = (seed >> 0) ^ (seed >> 48); |
72 | 0 | xsubi[1] = (seed >> 16); |
73 | 0 | xsubi[2] = (seed >> 32); |
74 | 0 | for (uint8_t* ptr = (uint8_t*)dest; n--;) { |
75 | 0 | *ptr++ = nrand48(xsubi); |
76 | 0 | } |
77 | 0 | return dest; |
78 | 0 | } |
79 | | |
80 | | const char* // |
81 | | fuzz(wuffs_base__io_buffer* src, uint64_t hash); |
82 | | |
83 | | static const char* // |
84 | 1.10k | llvmFuzzerTestOneInput(const uint8_t* data, size_t size) { |
85 | | // Make a 64-bit hash out of two 32-bit hashes, each on half of the data. |
86 | 1.10k | size_t s2 = size / 2; |
87 | 1.10k | uint32_t hash0 = jenkins_hash_u32(data, s2); |
88 | 1.10k | uint32_t hash1 = jenkins_hash_u32(data + s2, size - s2); |
89 | 1.10k | uint64_t hash = (((uint64_t)hash0) << 32) | ((uint64_t)hash1); |
90 | | |
91 | | // The ! means that closed will be true for (size == 0). |
92 | 1.10k | const bool closed = !(1 & popcount32(hash0)); |
93 | | |
94 | 1.10k | wuffs_base__io_buffer src = |
95 | 1.10k | wuffs_base__ptr_u8__reader((uint8_t*)data, size, closed); |
96 | | |
97 | 1.10k | const char* msg = fuzz(&src, hash); |
98 | 1.10k | if (msg) { |
99 | 1.05k | if (strnlen(msg, 2047) >= 2047) { |
100 | 0 | msg = "fuzzlib: internal error: error message is too long"; |
101 | 0 | } |
102 | 1.05k | if (closed && strstr(msg, "base: short read")) { |
103 | 0 | fprintf(stderr, "short read on a closed io_reader\n"); |
104 | 0 | intentional_segfault(); |
105 | 1.05k | } else if (strstr(msg, "internal error:")) { |
106 | 0 | fprintf(stderr, "internal errors shouldn't occur: \"%s\"\n", msg); |
107 | 0 | intentional_segfault(); |
108 | 0 | } |
109 | 1.05k | } |
110 | 1.10k | return msg; |
111 | 1.10k | } |
112 | | |
113 | | #ifdef __cplusplus |
114 | | extern "C" { |
115 | | #endif |
116 | | |
117 | | int // |
118 | 1.10k | LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
119 | 1.10k | llvmFuzzerTestOneInput(data, size); |
120 | 1.10k | return 0; |
121 | 1.10k | } |
122 | | |
123 | | #ifdef __cplusplus |
124 | | } // extern "C" |
125 | | #endif |
126 | | |
127 | | static wuffs_base__io_buffer // |
128 | 0 | make_limited_reader(wuffs_base__io_buffer b, uint64_t limit) { |
129 | 0 | uint64_t n = b.meta.wi - b.meta.ri; |
130 | 0 | bool closed = b.meta.closed; |
131 | 0 | if (n > limit) { |
132 | 0 | n = limit; |
133 | 0 | closed = false; |
134 | 0 | } |
135 | 0 |
|
136 | 0 | wuffs_base__io_buffer ret; |
137 | 0 | ret.data.ptr = b.data.ptr + b.meta.ri; |
138 | 0 | ret.data.len = n; |
139 | 0 | ret.meta.wi = n; |
140 | 0 | ret.meta.ri = 0; |
141 | 0 | ret.meta.pos = wuffs_base__u64__sat_add(b.meta.pos, b.meta.ri); |
142 | 0 | ret.meta.closed = closed; |
143 | 0 | return ret; |
144 | 0 | } |
145 | | |
146 | | #ifdef WUFFS_CONFIG__FUZZLIB_MAIN |
147 | | |
148 | | #include <dirent.h> |
149 | | #include <errno.h> |
150 | | #include <fcntl.h> |
151 | | #include <stdbool.h> |
152 | | #include <sys/mman.h> |
153 | | #include <sys/stat.h> |
154 | | #include <unistd.h> |
155 | | |
156 | | struct { |
157 | | int remaining_argc; |
158 | | char** remaining_argv; |
159 | | |
160 | | bool color; |
161 | | } g_flags = {0}; |
162 | | |
163 | | const char* // |
164 | | parse_flags(int argc, char** argv) { |
165 | | int c = (argc > 0) ? 1 : 0; // Skip argv[0], the program name. |
166 | | for (; c < argc; c++) { |
167 | | char* arg = argv[c]; |
168 | | if (*arg++ != '-') { |
169 | | break; |
170 | | } |
171 | | |
172 | | // A double-dash "--foo" is equivalent to a single-dash "-foo". As special |
173 | | // cases, a bare "-" is not a flag (some programs may interpret it as |
174 | | // stdin) and a bare "--" means to stop parsing flags. |
175 | | if (*arg == '\x00') { |
176 | | break; |
177 | | } else if (*arg == '-') { |
178 | | arg++; |
179 | | if (*arg == '\x00') { |
180 | | c++; |
181 | | break; |
182 | | } |
183 | | } |
184 | | |
185 | | if (!strcmp(arg, "c") || !strcmp(arg, "color")) { |
186 | | g_flags.color = true; |
187 | | continue; |
188 | | } |
189 | | |
190 | | return "main: unrecognized flag argument"; |
191 | | } |
192 | | |
193 | | g_flags.remaining_argc = argc - c; |
194 | | g_flags.remaining_argv = argv + c; |
195 | | return NULL; |
196 | | } |
197 | | |
198 | | static int g_num_files_processed; |
199 | | |
200 | | static struct { |
201 | | char buf[PATH_MAX]; |
202 | | size_t len; |
203 | | } g_relative_cwd; |
204 | | |
205 | | void // |
206 | | errorf(const char* msg) { |
207 | | if (g_flags.color) { |
208 | | printf("\e[31m%s\e[0m\n", msg); |
209 | | } else { |
210 | | printf("%s\n", msg); |
211 | | } |
212 | | } |
213 | | |
214 | | static int // |
215 | | visit(char* filename); |
216 | | |
217 | | static int // |
218 | | visit_dir(int fd) { |
219 | | int cwd_fd = open(".", O_RDONLY, 0); |
220 | | if (fchdir(fd)) { |
221 | | errorf("failed"); |
222 | | fprintf(stderr, "FAIL: fchdir: %s\n", strerror(errno)); |
223 | | return 1; |
224 | | } |
225 | | |
226 | | DIR* d = fdopendir(fd); |
227 | | if (!d) { |
228 | | errorf("failed"); |
229 | | fprintf(stderr, "FAIL: fdopendir: %s\n", strerror(errno)); |
230 | | return 1; |
231 | | } |
232 | | |
233 | | printf("dir\n"); |
234 | | while (true) { |
235 | | struct dirent* e = readdir(d); |
236 | | if (!e) { |
237 | | break; |
238 | | } |
239 | | if ((e->d_name[0] == '\x00') || (e->d_name[0] == '.')) { |
240 | | continue; |
241 | | } |
242 | | int v = visit(e->d_name); |
243 | | if (v) { |
244 | | return v; |
245 | | } |
246 | | } |
247 | | |
248 | | if (closedir(d)) { |
249 | | fprintf(stderr, "FAIL: closedir: %s\n", strerror(errno)); |
250 | | return 1; |
251 | | } |
252 | | if (fchdir(cwd_fd)) { |
253 | | fprintf(stderr, "FAIL: fchdir: %s\n", strerror(errno)); |
254 | | return 1; |
255 | | } |
256 | | if (close(cwd_fd)) { |
257 | | fprintf(stderr, "FAIL: close: %s\n", strerror(errno)); |
258 | | return 1; |
259 | | } |
260 | | return 0; |
261 | | } |
262 | | |
263 | | static int // |
264 | | visit_reg(int fd, off_t size) { |
265 | | if ((size < 0) || (0x7FFFFFFF < size)) { |
266 | | errorf("failed"); |
267 | | fprintf(stderr, "FAIL: file size out of bounds"); |
268 | | return 1; |
269 | | } |
270 | | |
271 | | void* data = NULL; |
272 | | if (size > 0) { |
273 | | data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); |
274 | | if (data == MAP_FAILED) { |
275 | | errorf("failed"); |
276 | | fprintf(stderr, "FAIL: mmap: %s\n", strerror(errno)); |
277 | | return 1; |
278 | | } |
279 | | } |
280 | | |
281 | | const char* msg = llvmFuzzerTestOneInput((const uint8_t*)(data), size); |
282 | | if (msg) { |
283 | | errorf(msg); |
284 | | } else if (g_flags.color) { |
285 | | printf("\e[32mok\e[0m\n"); |
286 | | } else { |
287 | | printf("ok\n"); |
288 | | } |
289 | | |
290 | | if ((size > 0) && munmap(data, size)) { |
291 | | fprintf(stderr, "FAIL: mmap: %s\n", strerror(errno)); |
292 | | return 1; |
293 | | } |
294 | | if (close(fd)) { |
295 | | fprintf(stderr, "FAIL: close: %s\n", strerror(errno)); |
296 | | return 1; |
297 | | } |
298 | | return 0; |
299 | | } |
300 | | |
301 | | static int // |
302 | | visit(char* filename) { |
303 | | g_num_files_processed++; |
304 | | if (!filename || (filename[0] == '\x00')) { |
305 | | fprintf(stderr, "FAIL: invalid filename\n"); |
306 | | return 1; |
307 | | } |
308 | | int n = printf("- %s%s", g_relative_cwd.buf, filename); |
309 | | printf("%*s", (60 > n) ? (60 - n) : 1, ""); |
310 | | fflush(stdout); |
311 | | |
312 | | struct stat z; |
313 | | int fd = open(filename, O_RDONLY, 0); |
314 | | if (fd == -1) { |
315 | | errorf("failed"); |
316 | | fprintf(stderr, "FAIL: open: %s\n", strerror(errno)); |
317 | | return 1; |
318 | | } |
319 | | if (fstat(fd, &z)) { |
320 | | errorf("failed"); |
321 | | fprintf(stderr, "FAIL: fstat: %s\n", strerror(errno)); |
322 | | return 1; |
323 | | } |
324 | | |
325 | | if (S_ISREG(z.st_mode)) { |
326 | | return visit_reg(fd, z.st_size); |
327 | | } else if (!S_ISDIR(z.st_mode)) { |
328 | | printf("skipped\n"); |
329 | | return 0; |
330 | | } |
331 | | |
332 | | size_t old_len = g_relative_cwd.len; |
333 | | size_t filename_len = strlen(filename); |
334 | | size_t new_len = old_len + strlen(filename); |
335 | | bool slash = filename[filename_len - 1] != '/'; |
336 | | if (slash) { |
337 | | new_len++; |
338 | | } |
339 | | if ((filename_len >= PATH_MAX) || (new_len >= PATH_MAX)) { |
340 | | errorf("failed"); |
341 | | fprintf(stderr, "FAIL: path is too long\n"); |
342 | | return 1; |
343 | | } |
344 | | memcpy(g_relative_cwd.buf + old_len, filename, filename_len); |
345 | | |
346 | | if (slash) { |
347 | | g_relative_cwd.buf[new_len - 1] = '/'; |
348 | | } |
349 | | g_relative_cwd.buf[new_len] = '\x00'; |
350 | | g_relative_cwd.len = new_len; |
351 | | |
352 | | int v = visit_dir(fd); |
353 | | |
354 | | g_relative_cwd.buf[old_len] = '\x00'; |
355 | | g_relative_cwd.len = old_len; |
356 | | return v; |
357 | | } |
358 | | |
359 | | int // |
360 | | main(int argc, char** argv) { |
361 | | g_num_files_processed = 0; |
362 | | g_relative_cwd.len = 0; |
363 | | |
364 | | const char* z = parse_flags(argc, argv); |
365 | | if (z) { |
366 | | fprintf(stderr, "FAIL: %s\n", z); |
367 | | return 1; |
368 | | } |
369 | | for (int i = 0; i < g_flags.remaining_argc; i++) { |
370 | | int v = visit(g_flags.remaining_argv[i]); |
371 | | if (v) { |
372 | | return v; |
373 | | } |
374 | | } |
375 | | |
376 | | printf("PASS: %d files processed\n", g_num_files_processed); |
377 | | return 0; |
378 | | } |
379 | | |
380 | | #endif // WUFFS_CONFIG__FUZZLIB_MAIN |