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