1 | /* |
---|
2 | * zzuf - general purpose fuzzer |
---|
3 | * Copyright (c) 2006 Sam Hocevar <sam@zoy.org> |
---|
4 | * All Rights Reserved |
---|
5 | * |
---|
6 | * $Id: zzuf.c 1525 2006-12-29 16:46:06Z sam $ |
---|
7 | * |
---|
8 | * This program is free software. It comes without any warranty, to |
---|
9 | * the extent permitted by applicable law. You can redistribute it |
---|
10 | * and/or modify it under the terms of the Do What The Fuck You Want |
---|
11 | * To Public License, Version 2, as published by Sam Hocevar. See |
---|
12 | * http://sam.zoy.org/wtfpl/COPYING for more details. |
---|
13 | */ |
---|
14 | |
---|
15 | /* |
---|
16 | * main.c: main program |
---|
17 | */ |
---|
18 | |
---|
19 | #include "config.h" |
---|
20 | |
---|
21 | #if defined HAVE_STDINT_H |
---|
22 | # include <stdint.h> |
---|
23 | #elif defined HAVE_INTTYPES_H |
---|
24 | # include <inttypes.h> |
---|
25 | #endif |
---|
26 | #if defined(HAVE_GETOPT_H) |
---|
27 | # include <getopt.h> |
---|
28 | #endif |
---|
29 | #include <stdio.h> |
---|
30 | #include <stdlib.h> |
---|
31 | #include <unistd.h> |
---|
32 | #include <string.h> |
---|
33 | #include <signal.h> |
---|
34 | #include <errno.h> |
---|
35 | #include <sys/time.h> |
---|
36 | #include <time.h> |
---|
37 | #include <sys/wait.h> |
---|
38 | |
---|
39 | #include "random.h" |
---|
40 | |
---|
41 | static void spawn_child(char **); |
---|
42 | static void set_ld_preload(char const *); |
---|
43 | static void version(void); |
---|
44 | #if defined(HAVE_GETOPT_H) |
---|
45 | static void usage(void); |
---|
46 | #endif |
---|
47 | |
---|
48 | enum status |
---|
49 | { |
---|
50 | STATUS_FREE, |
---|
51 | STATUS_RUNNING, |
---|
52 | STATUS_SIGTERM, |
---|
53 | STATUS_SIGKILL, |
---|
54 | STATUS_EOF, |
---|
55 | }; |
---|
56 | |
---|
57 | struct child_list |
---|
58 | { |
---|
59 | enum status status; |
---|
60 | pid_t pid; |
---|
61 | int outfd, errfd; |
---|
62 | int bytes, seed; |
---|
63 | time_t date; |
---|
64 | } |
---|
65 | *child_list; |
---|
66 | int parallel = 1, child_count = 0; |
---|
67 | |
---|
68 | int seed = 0; |
---|
69 | int endseed = 1; |
---|
70 | |
---|
71 | #define ZZUF_FD_SET(fd, p_fdset, maxfd) \ |
---|
72 | if(fd >= 0) \ |
---|
73 | { \ |
---|
74 | FD_SET(fd, p_fdset); \ |
---|
75 | if(fd > maxfd) \ |
---|
76 | maxfd = fd; \ |
---|
77 | } |
---|
78 | |
---|
79 | #define ZZUF_FD_ISSET(fd, p_fdset) \ |
---|
80 | ((fd >= 0) && (FD_ISSET(fd, p_fdset))) |
---|
81 | |
---|
82 | int main(int argc, char *argv[]) |
---|
83 | { |
---|
84 | char **newargv; |
---|
85 | char *parser; |
---|
86 | int i, j, quiet = 0, maxbytes = -1; |
---|
87 | double maxtime = -1.0; |
---|
88 | |
---|
89 | #if defined(HAVE_GETOPT_H) |
---|
90 | for(;;) |
---|
91 | { |
---|
92 | # ifdef HAVE_GETOPT_LONG |
---|
93 | # define MOREINFO "Try `%s --help' for more information.\n" |
---|
94 | int option_index = 0; |
---|
95 | static struct option long_options[] = |
---|
96 | { |
---|
97 | /* Long option, needs arg, flag, short option */ |
---|
98 | { "include", 1, NULL, 'I' }, |
---|
99 | { "exclude", 1, NULL, 'E' }, |
---|
100 | { "seed", 1, NULL, 's' }, |
---|
101 | { "ratio", 1, NULL, 'r' }, |
---|
102 | { "fork", 1, NULL, 'F' }, |
---|
103 | { "max-bytes", 1, NULL, 'B' }, |
---|
104 | { "max-time", 1, NULL, 'T' }, |
---|
105 | { "quiet", 0, NULL, 'q' }, |
---|
106 | { "debug", 0, NULL, 'd' }, |
---|
107 | { "help", 0, NULL, 'h' }, |
---|
108 | { "version", 0, NULL, 'v' }, |
---|
109 | }; |
---|
110 | int c = getopt_long(argc, argv, "I:E:s:r:F:B:T:qdhv", |
---|
111 | long_options, &option_index); |
---|
112 | # else |
---|
113 | # define MOREINFO "Try `%s -h' for more information.\n" |
---|
114 | int c = getopt(argc, argv, "I:E:s:r:F:B:T:qdhv"); |
---|
115 | # endif |
---|
116 | if(c == -1) |
---|
117 | break; |
---|
118 | |
---|
119 | switch(c) |
---|
120 | { |
---|
121 | case 'I': /* --include */ |
---|
122 | setenv("ZZUF_INCLUDE", optarg, 1); |
---|
123 | break; |
---|
124 | case 'E': /* --exclude */ |
---|
125 | setenv("ZZUF_EXCLUDE", optarg, 1); |
---|
126 | break; |
---|
127 | case 's': /* --seed */ |
---|
128 | parser = strchr(optarg, ':'); |
---|
129 | seed = atoi(optarg); |
---|
130 | endseed = parser ? atoi(parser + 1) : seed + 1; |
---|
131 | break; |
---|
132 | case 'r': /* --ratio */ |
---|
133 | setenv("ZZUF_RATIO", optarg, 1); |
---|
134 | break; |
---|
135 | case 'F': /* --fork */ |
---|
136 | parallel = atoi(optarg) > 1 ? atoi(optarg) : 1; |
---|
137 | break; |
---|
138 | case 'B': /* --max-bytes */ |
---|
139 | maxbytes = atoi(optarg); |
---|
140 | break; |
---|
141 | case 'T': /* --max-time */ |
---|
142 | maxtime = atof(optarg); |
---|
143 | break; |
---|
144 | case 'q': /* --quiet */ |
---|
145 | quiet = 1; |
---|
146 | break; |
---|
147 | case 'd': /* --debug */ |
---|
148 | setenv("ZZUF_DEBUG", "1", 1); |
---|
149 | break; |
---|
150 | case 'h': /* --help */ |
---|
151 | usage(); |
---|
152 | return 0; |
---|
153 | case 'v': /* --version */ |
---|
154 | version(); |
---|
155 | return 0; |
---|
156 | default: |
---|
157 | printf("%s: invalid option -- %c\n", argv[0], c); |
---|
158 | printf(MOREINFO, argv[0]); |
---|
159 | return 1; |
---|
160 | } |
---|
161 | } |
---|
162 | #else |
---|
163 | # define MOREINFO "Usage: %s message...\n" |
---|
164 | int optind = 1; |
---|
165 | #endif |
---|
166 | |
---|
167 | if(optind >= argc) |
---|
168 | { |
---|
169 | printf("%s: missing argument\n", argv[0]); |
---|
170 | printf(MOREINFO, argv[0]); |
---|
171 | return EXIT_FAILURE; |
---|
172 | } |
---|
173 | |
---|
174 | /* Allocate memory for children handling */ |
---|
175 | child_list = malloc(parallel * sizeof(struct child_list)); |
---|
176 | for(i = 0; i < parallel; i++) |
---|
177 | child_list[i].status = STATUS_FREE; |
---|
178 | child_count = 0; |
---|
179 | |
---|
180 | /* Preload libzzuf.so */ |
---|
181 | set_ld_preload(argv[0]); |
---|
182 | |
---|
183 | /* Create new argv */ |
---|
184 | newargv = malloc((argc - optind + 1) * sizeof(char *)); |
---|
185 | memcpy(newargv, argv + optind, (argc - optind) * sizeof(char *)); |
---|
186 | newargv[argc - optind] = (char *)NULL; |
---|
187 | |
---|
188 | /* Handle children in our way */ |
---|
189 | signal(SIGCHLD, SIG_DFL); |
---|
190 | |
---|
191 | /* Main loop */ |
---|
192 | while(child_count || seed < endseed) |
---|
193 | { |
---|
194 | struct timeval tv; |
---|
195 | time_t now = time(NULL); |
---|
196 | fd_set fdset; |
---|
197 | int ret, maxfd = 0; |
---|
198 | |
---|
199 | /* Spawn a new child, if necessary */ |
---|
200 | if(child_count < parallel && seed < endseed) |
---|
201 | spawn_child(newargv); |
---|
202 | |
---|
203 | /* Terminate children if necessary */ |
---|
204 | for(i = 0; i < parallel; i++) |
---|
205 | { |
---|
206 | if(child_list[i].status == STATUS_RUNNING |
---|
207 | && maxbytes >= 0 && child_list[i].bytes > maxbytes) |
---|
208 | { |
---|
209 | fprintf(stderr, "seed %i: data exceeded, sending SIGTERM\n", |
---|
210 | child_list[i].seed); |
---|
211 | kill(child_list[i].pid, SIGTERM); |
---|
212 | child_list[i].date = now; |
---|
213 | child_list[i].status = STATUS_SIGTERM; |
---|
214 | } |
---|
215 | |
---|
216 | if(child_list[i].status == STATUS_RUNNING |
---|
217 | && maxtime >= 0.0 |
---|
218 | && difftime(now, child_list[i].date) > maxtime) |
---|
219 | { |
---|
220 | fprintf(stderr, "seed %i: time exceeded, sending SIGTERM\n", |
---|
221 | child_list[i].seed); |
---|
222 | kill(child_list[i].pid, SIGTERM); |
---|
223 | child_list[i].date = now; |
---|
224 | child_list[i].status = STATUS_SIGTERM; |
---|
225 | } |
---|
226 | } |
---|
227 | |
---|
228 | /* Kill children if necessary */ |
---|
229 | for(i = 0; i < parallel; i++) |
---|
230 | { |
---|
231 | if(child_list[i].status == STATUS_SIGTERM |
---|
232 | && difftime(now, child_list[i].date) > 2.0) |
---|
233 | { |
---|
234 | fprintf(stderr, "seed %i: not responding, sending SIGKILL\n", |
---|
235 | child_list[i].seed); |
---|
236 | kill(child_list[i].pid, SIGKILL); |
---|
237 | child_list[i].status = STATUS_SIGKILL; |
---|
238 | } |
---|
239 | } |
---|
240 | |
---|
241 | /* Collect dead children */ |
---|
242 | for(i = 0; i < parallel; i++) |
---|
243 | { |
---|
244 | int status; |
---|
245 | pid_t pid; |
---|
246 | |
---|
247 | if(child_list[i].status != STATUS_SIGKILL |
---|
248 | && child_list[i].status != STATUS_SIGTERM |
---|
249 | && child_list[i].status != STATUS_EOF) |
---|
250 | continue; |
---|
251 | |
---|
252 | pid = waitpid(child_list[i].pid, &status, WNOHANG); |
---|
253 | if(pid <= 0) |
---|
254 | continue; |
---|
255 | |
---|
256 | if(WIFEXITED(status) && WEXITSTATUS(status)) |
---|
257 | fprintf(stderr, "seed %i: exit %i\n", |
---|
258 | child_list[i].seed, WEXITSTATUS(status)); |
---|
259 | else if(WIFSIGNALED(status)) |
---|
260 | fprintf(stderr, "seed %i: signal %i\n", |
---|
261 | child_list[i].seed, WTERMSIG(status)); |
---|
262 | |
---|
263 | if(child_list[i].outfd >= 0) |
---|
264 | close(child_list[i].outfd); |
---|
265 | |
---|
266 | if(child_list[i].errfd >= 0) |
---|
267 | close(child_list[i].errfd); |
---|
268 | |
---|
269 | child_list[i].status = STATUS_FREE; |
---|
270 | child_count--; |
---|
271 | } |
---|
272 | |
---|
273 | /* Read data from all sockets */ |
---|
274 | FD_ZERO(&fdset); |
---|
275 | for(i = 0; i < parallel; i++) |
---|
276 | { |
---|
277 | if(child_list[i].status != STATUS_RUNNING) |
---|
278 | continue; |
---|
279 | |
---|
280 | ZZUF_FD_SET(child_list[i].outfd, &fdset, maxfd); |
---|
281 | ZZUF_FD_SET(child_list[i].errfd, &fdset, maxfd); |
---|
282 | } |
---|
283 | tv.tv_sec = 0; |
---|
284 | tv.tv_usec = 1000; |
---|
285 | |
---|
286 | ret = select(maxfd + 1, &fdset, NULL, NULL, &tv); |
---|
287 | if(ret < 0) |
---|
288 | perror("select"); |
---|
289 | if(ret <= 0) |
---|
290 | continue; |
---|
291 | |
---|
292 | for(i = 0, j = 0; i < parallel; i += j, j = (j + 1) & 1) |
---|
293 | { |
---|
294 | char buf[BUFSIZ]; |
---|
295 | int fd; |
---|
296 | |
---|
297 | if(child_list[i].status != STATUS_RUNNING) |
---|
298 | continue; |
---|
299 | |
---|
300 | fd = j ? child_list[i].outfd : child_list[i].errfd; |
---|
301 | |
---|
302 | if(!ZZUF_FD_ISSET(fd, &fdset)) |
---|
303 | continue; |
---|
304 | |
---|
305 | ret = read(fd, buf, BUFSIZ - 1); |
---|
306 | if(ret > 0) |
---|
307 | { |
---|
308 | /* We got data */ |
---|
309 | child_list[i].bytes += ret; |
---|
310 | if(!quiet) |
---|
311 | fwrite(buf, ret, 1, j ? stdout : stderr); |
---|
312 | } |
---|
313 | else if(ret == 0) |
---|
314 | { |
---|
315 | /* End of file reached */ |
---|
316 | close(fd); |
---|
317 | if(j) |
---|
318 | child_list[i].outfd = -1; |
---|
319 | else |
---|
320 | child_list[i].errfd = -1; |
---|
321 | |
---|
322 | if(child_list[i].outfd == -1 && child_list[i].errfd == -1) |
---|
323 | child_list[i].status = STATUS_EOF; |
---|
324 | } |
---|
325 | } |
---|
326 | } |
---|
327 | |
---|
328 | return EXIT_SUCCESS; |
---|
329 | } |
---|
330 | |
---|
331 | static void spawn_child(char **argv) |
---|
332 | { |
---|
333 | char buf[BUFSIZ]; |
---|
334 | int outfd[2], errfd[2]; |
---|
335 | pid_t pid; |
---|
336 | int i; |
---|
337 | |
---|
338 | /* Find an empty slot */ |
---|
339 | for(i = 0; i < parallel; i++) |
---|
340 | if(child_list[i].status == STATUS_FREE) |
---|
341 | break; |
---|
342 | |
---|
343 | /* Prepare communication pipe */ |
---|
344 | if(pipe(outfd) == -1 || pipe(errfd) == -1) |
---|
345 | { |
---|
346 | perror("pipe"); |
---|
347 | return; |
---|
348 | } |
---|
349 | |
---|
350 | /* Fork and launch child */ |
---|
351 | pid = fork(); |
---|
352 | switch(pid) |
---|
353 | { |
---|
354 | case -1: |
---|
355 | perror("fork"); |
---|
356 | return; |
---|
357 | case 0: |
---|
358 | /* We’re the child */ |
---|
359 | close(outfd[0]); |
---|
360 | close(errfd[0]); |
---|
361 | dup2(outfd[1], STDOUT_FILENO); |
---|
362 | dup2(errfd[1], STDERR_FILENO); |
---|
363 | close(outfd[1]); |
---|
364 | close(errfd[1]); |
---|
365 | |
---|
366 | /* Set environment variables */ |
---|
367 | sprintf(buf, "%i", seed); |
---|
368 | setenv("ZZUF_SEED", buf, 1); |
---|
369 | |
---|
370 | /* Run our process */ |
---|
371 | if(execvp(argv[0], argv)) |
---|
372 | { |
---|
373 | perror(argv[0]); |
---|
374 | exit(EXIT_FAILURE); |
---|
375 | } |
---|
376 | break; |
---|
377 | default: |
---|
378 | /* We’re the parent, acknowledge spawn */ |
---|
379 | close(outfd[1]); |
---|
380 | close(errfd[1]); |
---|
381 | child_list[i].date = time(NULL); |
---|
382 | child_list[i].pid = pid; |
---|
383 | child_list[i].outfd = outfd[0]; |
---|
384 | child_list[i].errfd = errfd[0]; |
---|
385 | child_list[i].bytes = 0; |
---|
386 | child_list[i].seed = seed; |
---|
387 | child_list[i].status = STATUS_RUNNING; |
---|
388 | child_count++; |
---|
389 | seed++; |
---|
390 | break; |
---|
391 | } |
---|
392 | } |
---|
393 | |
---|
394 | static void set_ld_preload(char const *progpath) |
---|
395 | { |
---|
396 | char *libpath, *tmp; |
---|
397 | int len = strlen(progpath); |
---|
398 | |
---|
399 | libpath = malloc(len + strlen("/.libs/libzzuf.so") + 1); |
---|
400 | strcpy(libpath, progpath); |
---|
401 | tmp = strrchr(libpath, '/'); |
---|
402 | strcpy(tmp ? tmp + 1 : libpath, ".libs/libzzuf.so"); |
---|
403 | if(access(libpath, R_OK) == 0) |
---|
404 | { |
---|
405 | setenv("LD_PRELOAD", libpath, 1); |
---|
406 | return; |
---|
407 | } |
---|
408 | free(libpath); |
---|
409 | |
---|
410 | setenv("LD_PRELOAD", LIBDIR "/libzzuf.so", 1); |
---|
411 | } |
---|
412 | |
---|
413 | static void version(void) |
---|
414 | { |
---|
415 | printf("zzuf %s\n", VERSION); |
---|
416 | printf("Copyright (C) 2006 Sam Hocevar <sam@zoy.org>\n"); |
---|
417 | printf("This is free software. You may redistribute copies of it under the\n"); |
---|
418 | printf("terms of the Do What The Fuck You Want To Public License, Version 2\n"); |
---|
419 | printf("<http://sam.zoy.org/wtfpl/>.\n"); |
---|
420 | printf("There is NO WARRANTY, to the extent permitted by law.\n"); |
---|
421 | printf("\n"); |
---|
422 | printf("Written by Sam Hocevar. Report bugs to <sam@zoy.org>.\n"); |
---|
423 | } |
---|
424 | |
---|
425 | #if defined(HAVE_GETOPT_H) |
---|
426 | static void usage(void) |
---|
427 | { |
---|
428 | printf("Usage: zzuf [ -vqdh ] [ -r ratio ] [ -s seed | -s start:stop]\n"); |
---|
429 | printf(" [ -F children ] [ -B bytes ] [ -T seconds ]\n"); |
---|
430 | printf(" [ -I include ] [ -E exclude ] COMMAND [ARGS]...\n"); |
---|
431 | printf("Run COMMAND and randomly fuzz its input files.\n"); |
---|
432 | printf("\n"); |
---|
433 | printf("Mandatory arguments to long options are mandatory for short options too.\n"); |
---|
434 | # ifdef HAVE_GETOPT_LONG |
---|
435 | printf(" -r, --ratio <ratio> bit fuzzing ratio (default 0.004)\n"); |
---|
436 | printf(" -s, --seed <seed> random seed (default 0)\n"); |
---|
437 | printf(" --seed <start:stop> specify a seed range\n"); |
---|
438 | printf(" -F, --fork <count> number of concurrent children (default 1)\n"); |
---|
439 | printf(" -B, --max-bytes <n> kill children that output more than <n> bytes\n"); |
---|
440 | printf(" -T, --max-time <n> kill children that run for more than <n> seconds\n"); |
---|
441 | printf(" -q, --quiet do not print children's messages\n"); |
---|
442 | printf(" -I, --include <regex> only fuzz files matching <regex>\n"); |
---|
443 | printf(" -E, --exclude <regex> do not fuzz files matching <regex>\n"); |
---|
444 | printf(" -d, --debug print debug messages\n"); |
---|
445 | printf(" -h, --help display this help and exit\n"); |
---|
446 | printf(" -v, --version output version information and exit\n"); |
---|
447 | # else |
---|
448 | printf(" -r <ratio> bit fuzzing ratio (default 0.004)\n"); |
---|
449 | printf(" -s <seed> random seed (default 0)\n"); |
---|
450 | printf(" <start:stop> specify a seed range\n"); |
---|
451 | printf(" -F <count> number of concurrent forks (default 1)\n"); |
---|
452 | printf(" -B <n> kill children that output more than <n> bytes\n"); |
---|
453 | printf(" -T <n> kill children that run for more than <n> seconds\n"); |
---|
454 | printf(" -q do not print the fuzzed application's messages\n"); |
---|
455 | printf(" -I <regex> only fuzz files matching <regex>\n"); |
---|
456 | printf(" -E <regex> do not fuzz files matching <regex>\n"); |
---|
457 | printf(" -d print debug messages\n"); |
---|
458 | printf(" -h display this help and exit\n"); |
---|
459 | printf(" -v output version information and exit\n"); |
---|
460 | # endif |
---|
461 | printf("\n"); |
---|
462 | printf("Written by Sam Hocevar. Report bugs to <sam@zoy.org>.\n"); |
---|
463 | } |
---|
464 | #endif |
---|
465 | |
---|