source: zzuf/trunk/src/zzuf.c @ 1526

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