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

Last change on this file since 1525 was 1525, checked in by Sam Hocevar, 14 years ago
  • Change -i/-e to -I/-E.
  • Property svn:keywords set to Id
File size: 13.9 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 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
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            { "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
331static 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
394static 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
413static 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)
426static 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
Note: See TracBrowser for help on using the repository browser.