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

Last change on this file since 4667 was 4667, checked in by Sam Hocevar, 10 years ago

Make it easier to dynamically allocate the debug filedescriptor later.

  • Property svn:keywords set to Id
File size: 34.0 KB
Line 
1/*
2 *  zzuf - general purpose fuzzer
3 *  Copyright (c) 2002-2010 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  This program is free software. It comes without any warranty, to
7 *  the extent permitted by applicable law. You can redistribute it
8 *  and/or modify it under the terms of the Do What The Fuck You Want
9 *  To Public License, Version 2, as published by Sam Hocevar. See
10 *  http://sam.zoy.org/wtfpl/COPYING for more details.
11 */
12
13/*
14 *  main.c: main program
15 */
16
17#include "config.h"
18
19#define _INCLUDE_POSIX_SOURCE /* for STDERR_FILENO on HP-UX */
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_LONG
27#   include "mygetopt.h"
28#elif defined HAVE_GETOPT_H
29#   include <getopt.h>
30#endif
31#include <stdio.h>
32#include <stdlib.h>
33#if defined HAVE_UNISTD_H
34#   include <unistd.h> /* for read(), write(), close() */
35#endif
36#if defined HAVE_REGEX_H
37#   include <regex.h>
38#endif
39#if defined HAVE_WINSOCK2_H
40#   include <winsock2.h> /* for fd_set */
41#endif
42#if defined HAVE_IO_H
43#   include <io.h>
44#endif
45#include <string.h>
46#include <errno.h>
47#include <signal.h>
48#if defined HAVE_SYS_TIME_H
49#   include <sys/time.h>
50#endif
51#if defined HAVE_SYS_WAIT_H
52#   include <sys/wait.h>
53#endif
54#if defined HAVE_SYS_RESOURCE_H
55#   include <sys/resource.h> /* for RLIMIT_AS */
56#endif
57
58#include "common.h"
59#include "opts.h"
60#include "random.h"
61#include "fd.h"
62#include "fuzz.h"
63#include "myfork.h"
64#include "md5.h"
65#include "timer.h"
66
67#if defined HAVE_GETOPT_LONG
68#   define mygetopt getopt_long
69#   define myoptind optind
70#   define myoptarg optarg
71#   define myoption option
72#endif
73
74#if !defined SIGKILL
75#   define SIGKILL 9
76#endif
77
78#if defined RLIMIT_AS
79#   define ZZUF_RLIMIT_MEM RLIMIT_AS
80#elif defined RLIMIT_VMEM
81#   define ZZUF_RLIMIT_MEM RLIMIT_VMEM
82#elif defined RLIMIT_DATA
83#   define ZZUF_RLIMIT_MEM RLIMIT_DATA
84#else
85#   undef ZZUF_RLIMIT_MEM
86#endif
87
88#if defined RLIMIT_CPU
89#   define ZZUF_RLIMIT_CPU RLIMIT_CPU
90#else
91#   undef ZZUF_RLIMIT_CPU
92#endif
93
94static void loop_stdin(struct opts *);
95
96static void spawn_children(struct opts *);
97static void clean_children(struct opts *);
98static void read_children(struct opts *);
99
100#if !defined HAVE_SETENV
101static void setenv(char const *, char const *, int);
102#endif
103#if defined HAVE_WAITPID
104static char const *sig2name(int);
105#endif
106static void finfo(FILE *, struct opts *, uint32_t);
107#if defined HAVE_REGEX_H
108static char *merge_regex(char *, char *);
109static char *merge_file(char *, char *);
110#endif
111static void version(void);
112static void usage(void);
113
114#define ZZUF_FD_SET(fd, p_fdset, maxfd) \
115    if(fd >= 0) \
116    { \
117        FD_SET((unsigned int)fd, p_fdset); \
118        if(fd > maxfd) \
119            maxfd = fd; \
120    }
121
122#define ZZUF_FD_ISSET(fd, p_fdset) \
123    ((fd >= 0) && (FD_ISSET(fd, p_fdset)))
124
125int main(int argc, char *argv[])
126{
127    struct opts _opts, *opts = &_opts;
128    char *tmp;
129#if defined HAVE_REGEX_H
130    char *include = NULL, *exclude = NULL;
131    int cmdline = 0;
132#endif
133    int debug = 0, network = 0;
134    int i;
135
136    _zz_opts_init(opts);
137
138    for(;;)
139    {
140#if defined HAVE_REGEX_H
141#   define OPTSTR_REGEX "cE:I:"
142#else
143#   define OPTSTR_REGEX ""
144#endif
145#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
146#   define OPTSTR_RLIMIT_MEM "M:"
147#else
148#   define OPTSTR_RLIMIT_MEM ""
149#endif
150#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_CPU
151#   define OPTSTR_RLIMIT_CPU "T:"
152#else
153#   define OPTSTR_RLIMIT_CPU ""
154#endif
155#define OPTSTR "+" OPTSTR_REGEX OPTSTR_RLIMIT_MEM OPTSTR_RLIMIT_CPU \
156                "a:Ab:B:C:dD:e:f:F:ij:l:mnO:p:P:qr:R:s:St:U:vxhV"
157#define MOREINFO "Try `%s --help' for more information.\n"
158        int option_index = 0;
159        static struct myoption long_options[] =
160        {
161            /* Long option, needs arg, flag, short option */
162            { "allow",        1, NULL, 'a' },
163            { "autoinc",      0, NULL, 'A' },
164            { "bytes",        1, NULL, 'b' },
165            { "max-bytes",    1, NULL, 'B' },
166#if defined HAVE_REGEX_H
167            { "cmdline",      0, NULL, 'c' },
168#endif
169            { "max-crashes",  1, NULL, 'C' },
170            { "debug",        0, NULL, 'd' },
171            { "delay",        1, NULL, 'D' },
172#if defined HAVE_REGEX_H
173            { "exclude",      1, NULL, 'E' },
174#endif
175            { "fuzzing",      1, NULL, 'f' },
176            { "stdin",        0, NULL, 'i' },
177#if defined HAVE_REGEX_H
178            { "include",      1, NULL, 'I' },
179#endif
180            { "jobs",         1, NULL, 'j' },
181            { "list",         1, NULL, 'l' },
182            { "md5",          0, NULL, 'm' },
183            { "max-memory",   1, NULL, 'M' },
184            { "network",      0, NULL, 'n' },
185            { "opmode",       1, NULL, 'O' },
186            { "ports",        1, NULL, 'p' },
187            { "protect",      1, NULL, 'P' },
188            { "quiet",        0, NULL, 'q' },
189            { "ratio",        1, NULL, 'r' },
190            { "refuse",       1, NULL, 'R' },
191            { "seed",         1, NULL, 's' },
192            { "signal",       0, NULL, 'S' },
193            { "max-time",     1, NULL, 't' },
194            { "max-cputime",  1, NULL, 'T' },
195            { "max-usertime", 1, NULL, 'U' },
196            { "verbose",      0, NULL, 'v' },
197            { "check-exit",   0, NULL, 'x' },
198            { "help",         0, NULL, 'h' },
199            { "version",      0, NULL, 'V' },
200            { NULL,           0, NULL,  0  }
201        };
202        int c = mygetopt(argc, argv, OPTSTR, long_options, &option_index);
203
204        if(c == -1)
205            break;
206
207        switch(c)
208        {
209        case 'a': /* --allow */
210            opts->allow = myoptarg;
211            break;
212        case 'A': /* --autoinc */
213            setenv("ZZUF_AUTOINC", "1", 1);
214            break;
215        case 'b': /* --bytes */
216            opts->bytes = myoptarg;
217            break;
218        case 'B': /* --max-bytes */
219            if(myoptarg[0] == '=')
220                myoptarg++;
221            opts->maxbytes = atoi(myoptarg);
222            break;
223#if defined HAVE_REGEX_H
224        case 'c': /* --cmdline */
225            cmdline = 1;
226            break;
227#endif
228        case 'C': /* --max-crashes */
229            if(myoptarg[0] == '=')
230                myoptarg++;
231            opts->maxcrashes = atoi(myoptarg);
232            if(opts->maxcrashes <= 0)
233                opts->maxcrashes = 0;
234            break;
235        case 'd': /* --debug */
236            debug++;
237            break;
238        case 'D': /* --delay */
239            if(myoptarg[0] == '=')
240                myoptarg++;
241            opts->delay = (int64_t)(atof(myoptarg) * 1000000.0);
242            break;
243#if defined HAVE_REGEX_H
244        case 'E': /* --exclude */
245            exclude = merge_regex(exclude, myoptarg);
246            if(!exclude)
247            {
248                fprintf(stderr, "%s: invalid regex -- `%s'\n",
249                        argv[0], myoptarg);
250                _zz_opts_fini(opts);
251                return EXIT_FAILURE;
252            }
253            break;
254#endif
255        case 'f': /* --fuzzing */
256            opts->fuzzing = myoptarg;
257            break;
258        case 'F':
259            fprintf(stderr, "%s: `-F' is deprecated, use `-j'\n", argv[0]);
260            _zz_opts_fini(opts);
261            return EXIT_FAILURE;
262        case 'i': /* --stdin */
263            setenv("ZZUF_STDIN", "1", 1);
264            break;
265#if defined HAVE_REGEX_H
266        case 'I': /* --include */
267            include = merge_regex(include, myoptarg);
268            if(!include)
269            {
270                fprintf(stderr, "%s: invalid regex -- `%s'\n",
271                        argv[0], myoptarg);
272                _zz_opts_fini(opts);
273                return EXIT_FAILURE;
274            }
275            break;
276#endif
277        case 'j': /* --jobs */
278            if(myoptarg[0] == '=')
279                myoptarg++;
280            opts->maxchild = atoi(myoptarg) > 1 ? atoi(myoptarg) : 1;
281            break;
282        case 'l': /* --list */
283            opts->list = myoptarg;
284            break;
285        case 'm': /* --md5 */
286            opts->md5 = 1;
287            break;
288#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
289        case 'M': /* --max-memory */
290            if(myoptarg[0] == '=')
291                myoptarg++;
292            opts->maxmem = atoi(myoptarg);
293            break;
294#endif
295        case 'n': /* --network */
296            setenv("ZZUF_NETWORK", "1", 1);
297            network = 1;
298            break;
299        case 'O': /* --opmode */
300            if(myoptarg[0] == '=')
301                myoptarg++;
302            if (!strcmp(myoptarg, "preload"))
303                opts->opmode = OPMODE_PRELOAD;
304            else if (!strcmp(myoptarg, "copy"))
305                opts->opmode = OPMODE_COPY;
306            else
307            {
308                fprintf(stderr, "%s: invalid operating mode -- `%s'\n",
309                        argv[0], myoptarg);
310                _zz_opts_fini(opts);
311                return EXIT_FAILURE;
312            }
313            break;
314        case 'p': /* --ports */
315            opts->ports = myoptarg;
316            break;
317        case 'P': /* --protect */
318            opts->protect = myoptarg;
319            break;
320        case 'q': /* --quiet */
321            opts->quiet = 1;
322            break;
323        case 'r': /* --ratio */
324            if(myoptarg[0] == '=')
325                myoptarg++;
326            tmp = strchr(myoptarg, ':');
327            opts->minratio = atof(myoptarg);
328            opts->maxratio = tmp ? atof(tmp + 1) : opts->minratio;
329            break;
330        case 'R': /* --refuse */
331            opts->refuse = myoptarg;
332            break;
333        case 's': /* --seed */
334            if(myoptarg[0] == '=')
335                myoptarg++;
336            tmp = strchr(myoptarg, ':');
337            opts->seed = atol(myoptarg);
338            opts->endseed = tmp ? tmp[1] ? (uint32_t)atol(tmp + 1)
339                                         : (uint32_t)-1L
340                                : opts->seed + 1;
341            break;
342        case 'S': /* --signal */
343            setenv("ZZUF_SIGNAL", "1", 1);
344            break;
345        case 't': /* --max-time */
346            if(myoptarg[0] == '=')
347                myoptarg++;
348            opts->maxtime = (int64_t)atoi(myoptarg) * 1000000;
349            break;
350#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_CPU
351        case 'T': /* --max-cputime */
352            if(myoptarg[0] == '=')
353                myoptarg++;
354            opts->maxcpu = (int)(atof(myoptarg) + 0.5);
355            break;
356#endif
357        case 'U': /* --max-usertime */
358            if(myoptarg[0] == '=')
359                myoptarg++;
360            opts->maxusertime = (int64_t)(atof(myoptarg) * 1000000.0);
361            break;
362        case 'x': /* --check-exit */
363            opts->checkexit = 1;
364            break;
365        case 'v': /* --verbose */
366            opts->verbose = 1;
367            break;
368        case 'h': /* --help */
369            usage();
370            _zz_opts_fini(opts);
371            return 0;
372        case 'V': /* --version */
373            version();
374            _zz_opts_fini(opts);
375            return 0;
376        default:
377            fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c);
378            printf(MOREINFO, argv[0]);
379            _zz_opts_fini(opts);
380            return EXIT_FAILURE;
381        }
382    }
383
384    if(opts->ports && !network)
385    {
386        fprintf(stderr, "%s: port option (-p) requires network fuzzing (-n)\n",
387                argv[0]);
388        printf(MOREINFO, argv[0]);
389        _zz_opts_fini(opts);
390        return EXIT_FAILURE;
391    }
392
393    if (opts->allow && !network)
394    {
395        fprintf(stderr, "%s: allow option (-a) requires network fuzzing (-n)\n",
396                argv[0]);
397        printf(MOREINFO, argv[0]);
398        _zz_opts_fini(opts);
399        return EXIT_FAILURE;
400    }
401
402    _zz_setratio(opts->minratio, opts->maxratio);
403    _zz_setseed(opts->seed);
404
405    if(opts->fuzzing)
406        _zz_fuzzing(opts->fuzzing);
407    if(opts->bytes)
408        _zz_bytes(opts->bytes);
409    if(opts->list)
410        _zz_list(opts->list);
411    if(opts->protect)
412        _zz_protect(opts->protect);
413    if(opts->refuse)
414        _zz_refuse(opts->refuse);
415
416    /* Needed for stdin mode and for copy opmode. */
417    _zz_fd_init();
418
419    /*
420     * Mode 1: asked to read from the standard input
421     */
422    if(myoptind >= argc)
423    {
424        if(opts->verbose)
425        {
426            finfo(stderr, opts, opts->seed);
427            fprintf(stderr, "reading from stdin\n");
428        }
429
430        if(opts->endseed != opts->seed + 1)
431        {
432            fprintf(stderr, "%s: seed ranges are incompatible with "
433                            "stdin fuzzing\n", argv[0]);
434            printf(MOREINFO, argv[0]);
435            _zz_opts_fini(opts);
436            return EXIT_FAILURE;
437        }
438
439        loop_stdin(opts);
440    }
441    /*
442     * Mode 2: asked to launch programs
443     */
444    else
445    {
446#if defined HAVE_REGEX_H
447        if(cmdline)
448        {
449            int dashdash = 0;
450
451            for(i = myoptind + 1; i < argc; i++)
452            {
453                if(dashdash)
454                    include = merge_file(include, argv[i]);
455                else if(!strcmp("--", argv[i]))
456                    dashdash = 1;
457                else if(argv[i][0] != '-')
458                    include = merge_file(include, argv[i]);
459            }
460        }
461
462        if(include)
463            setenv("ZZUF_INCLUDE", include, 1);
464        if(exclude)
465            setenv("ZZUF_EXCLUDE", exclude, 1);
466#endif
467
468        setenv("ZZUF_DEBUG", debug ? debug > 1 ? "2" : "1" : "0", 1);
469
470        if(opts->fuzzing)
471            setenv("ZZUF_FUZZING", opts->fuzzing, 1);
472        if(opts->bytes)
473            setenv("ZZUF_BYTES", opts->bytes, 1);
474        if(opts->list)
475            setenv("ZZUF_LIST", opts->list, 1);
476        if(opts->ports)
477            setenv("ZZUF_PORTS", opts->ports, 1);
478        if(opts->allow && opts->allow[0] == '!')
479            setenv("ZZUF_DENY", opts->allow, 1);
480        else if(opts->allow)
481            setenv("ZZUF_ALLOW", opts->allow, 1);
482        if(opts->protect)
483            setenv("ZZUF_PROTECT", opts->protect, 1);
484        if(opts->refuse)
485            setenv("ZZUF_REFUSE", opts->refuse, 1);
486#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
487        if(opts->maxmem >= 0)
488        {
489            char buf[32];
490            snprintf(buf, 32, "%i", opts->maxmem);
491            setenv("ZZUF_MEMORY", buf, 1);
492        }
493#endif
494
495        /* Allocate memory for children handling */
496        opts->child = malloc(opts->maxchild * sizeof(struct child));
497        for(i = 0; i < opts->maxchild; i++)
498            opts->child[i].status = STATUS_FREE;
499        opts->nchild = 0;
500
501        /* Create new argv */
502        opts->oldargc = argc;
503        opts->oldargv = argv;
504        for(i = 0; i < opts->maxchild; i++)
505        {
506            int len = argc - myoptind;
507            opts->child[i].newargv = malloc((len + 1) * sizeof(char *));
508            memcpy(opts->child[i].newargv, argv + myoptind,
509                   len * sizeof(char *));
510            opts->child[i].newargv[len] = (char *)NULL;
511        }
512
513        /* Main loop */
514        while(opts->nchild || opts->seed < opts->endseed)
515        {
516            /* Spawn new children, if necessary */
517            spawn_children(opts);
518
519            /* Cleanup dead or dying children */
520            clean_children(opts);
521
522            /* Read data from children */
523            read_children(opts);
524
525            if(opts->maxcrashes && opts->crashes >= opts->maxcrashes
526                && opts->nchild == 0)
527            {
528                if(opts->verbose)
529                    fprintf(stderr,
530                            "zzuf: maximum crash count reached, exiting\n");
531                break;
532            }
533
534            if(opts->maxtime && _zz_time() - opts->starttime >= opts->maxtime
535                && opts->nchild == 0)
536            {
537                if(opts->verbose)
538                    fprintf(stderr,
539                            "zzuf: maximum running time reached, exiting\n");
540                break;
541            }
542        }
543    }
544
545    /* Clean up */
546    _zz_fd_fini();
547    _zz_opts_fini(opts);
548
549    return opts->crashes ? EXIT_FAILURE : EXIT_SUCCESS;
550}
551
552static void loop_stdin(struct opts *opts)
553{
554    uint8_t md5sum[16];
555    struct md5 *ctx = NULL;
556    int total = 0;
557
558    if(opts->md5)
559        ctx = _zz_md5_init();
560
561    _zz_register(0);
562
563    for(;;)
564    {
565        uint8_t buf[BUFSIZ];
566        int ret, toread = BUFSIZ, off = 0, nw = 0;
567
568        if(opts->maxbytes >= 0)
569        {
570            if(total >= opts->maxbytes)
571                break;
572            if(total + BUFSIZ >= opts->maxbytes)
573                toread = opts->maxbytes - total;
574        }
575
576        ret = read(0, buf, toread);
577        if(ret <= 0)
578            break;
579
580        total += ret;
581
582        _zz_fuzz(0, buf, ret);
583        _zz_addpos(0, ret);
584
585        if(opts->md5)
586            _zz_md5_add(ctx, buf, ret);
587        else while(ret)
588        {
589            if((nw = write(1, buf + off, (unsigned int)ret)) < 0)
590                break;
591            ret -= nw;
592            off += nw;
593        }
594    }
595
596    if(opts->md5)
597    {
598        _zz_md5_fini(md5sum, ctx);
599        finfo(stdout, opts, opts->seed);
600        fprintf(stdout, "%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x"
601                "%.02x%.02x%.02x%.02x%.02x%.02x\n", md5sum[0], md5sum[1],
602                md5sum[2], md5sum[3], md5sum[4], md5sum[5], md5sum[6],
603                md5sum[7], md5sum[8], md5sum[9], md5sum[10], md5sum[11],
604                md5sum[12], md5sum[13], md5sum[14], md5sum[15]);
605        fflush(stdout);
606    }
607
608    _zz_unregister(0);
609}
610
611static void finfo(FILE *fp, struct opts *opts, uint32_t seed)
612{
613    if(opts->minratio == opts->maxratio)
614        fprintf(fp, "zzuf[s=%i,r=%g]: ", seed, opts->minratio);
615    else
616        fprintf(fp, "zzuf[s=%i,r=%g:%g]: ", seed,
617                opts->minratio, opts->maxratio);
618}
619
620#if defined HAVE_REGEX_H
621static char *merge_file(char *regex, char *file)
622{
623    char *newfile = malloc(5 + 2 * strlen(file) + 1 + 1), *tmp = newfile;
624
625    *tmp++ = '(';
626    *tmp++ = '^';
627    *tmp++ = '|';
628    *tmp++ = '/';
629    *tmp++ = ')';
630    while(*file)
631    {
632        if(strchr("^.[$()|*+?{\\", *file))
633            *tmp++ = '\\';
634        *tmp++ = *file++;
635    }
636    *tmp++ = '$';
637    *tmp++ = '\0';
638
639    tmp = merge_regex(regex, newfile);
640    free(newfile);
641    return tmp;
642}
643
644static char *merge_regex(char *regex, char *string)
645{
646    regex_t optre;
647
648    if(regex)
649    {
650        regex = realloc(regex, strlen(regex) + strlen(string) + 1 + 1);
651        sprintf(regex + strlen(regex) - 1, "|%s)", string);
652    }
653    else
654    {
655        regex = malloc(1 + strlen(string) + 1 + 1);
656        sprintf(regex, "(%s)", string);
657    }
658
659    if(regcomp(&optre, regex, REG_EXTENDED) != 0)
660    {
661        free(regex);
662        return NULL;
663    }
664    regfree(&optre);
665
666    return regex;
667}
668#endif
669
670static void spawn_children(struct opts *opts)
671{
672    int64_t now = _zz_time();
673    int i;
674
675    if(opts->nchild == opts->maxchild)
676        return; /* no slot */
677
678    if(opts->seed == opts->endseed)
679        return; /* job finished */
680
681    if(opts->maxcrashes && opts->crashes >= opts->maxcrashes)
682        return; /* all jobs crashed */
683
684    if(opts->maxtime && now - opts->starttime >= opts->maxtime)
685        return; /* run time exceeded */
686
687    if(opts->delay > 0 && opts->lastlaunch + opts->delay > now)
688        return; /* too early */
689
690    /* Find the empty slot */
691    for(i = 0; i < opts->maxchild; i++)
692        if(opts->child[i].status == STATUS_FREE)
693            break;
694
695    /* Prepare required files, if necessary */
696    if (opts->opmode == OPMODE_COPY)
697    {
698        char tmpname[4096];
699        char *tmpdir;
700        FILE *fpin;
701        int j, k = 0, fdout;
702
703        tmpdir = getenv("TEMP");
704        if (!tmpdir || !*tmpdir)
705            tmpdir = "/tmp";
706
707        for (j = myoptind + 1; j < opts->oldargc; j++)
708        {
709            fpin = fopen(opts->oldargv[j], "r");
710            if (!fpin)
711                continue;
712
713            sprintf(tmpname, "%s/zzuf.%i.XXXXXX", tmpdir, (int)getpid());
714#ifdef _WIN32
715            fdout = mktemp(tmpname);
716#else
717            fdout = mkstemp(tmpname);
718#endif
719            if (fdout < 0)
720            {
721                fclose(fpin);
722                continue;
723            }
724
725            opts->child[i].newargv[j - myoptind] = strdup(tmpname);
726
727            _zz_register(k);
728            while(!feof(fpin))
729            {
730                uint8_t buf[BUFSIZ];
731                size_t n = fread(buf, 1, BUFSIZ, fpin);
732                if (n <= 0)
733                    break;
734                _zz_fuzz(k, buf, n);
735                _zz_addpos(k, n);
736                write(fdout, buf, n);
737            }
738            _zz_unregister(k);
739
740            fclose(fpin);
741            close(fdout);
742
743            k++;
744        }
745    }
746
747    /* Launch process */
748    if (myfork(&opts->child[i], opts) < 0)
749    {
750        fprintf(stderr, "error launching `%s'\n", opts->child[i].newargv[0]);
751        opts->seed++;
752        /* FIXME: clean up OPMODE_COPY files here */
753        return;
754    }
755
756    /* We’re the parent, acknowledge spawn */
757    opts->child[i].date = now;
758    opts->child[i].bytes = 0;
759    opts->child[i].seed = opts->seed;
760    opts->child[i].ratio = _zz_getratio();
761    opts->child[i].status = STATUS_RUNNING;
762    if(opts->md5)
763        opts->child[i].ctx = _zz_md5_init();
764
765    if(opts->verbose)
766    {
767        finfo(stderr, opts, opts->child[i].seed);
768        fprintf(stderr, "launched `%s'\n", opts->child[i].newargv[0]);
769    }
770
771    opts->lastlaunch = now;
772    opts->nchild++;
773    opts->seed++;
774
775    _zz_setseed(opts->seed);
776}
777
778static void clean_children(struct opts *opts)
779{
780#if defined HAVE_KILL
781    int64_t now = _zz_time();
782#endif
783    int i, j;
784
785#if defined HAVE_KILL
786    /* Terminate children if necessary */
787    for(i = 0; i < opts->maxchild; i++)
788    {
789        if(opts->child[i].status == STATUS_RUNNING
790            && opts->maxbytes >= 0
791            && opts->child[i].bytes > opts->maxbytes)
792        {
793            if(opts->verbose)
794            {
795                finfo(stderr, opts, opts->child[i].seed);
796                fprintf(stderr, "data output exceeded, sending SIGTERM\n");
797            }
798            kill(opts->child[i].pid, SIGTERM);
799            opts->child[i].date = now;
800            opts->child[i].status = STATUS_SIGTERM;
801        }
802
803        if(opts->child[i].status == STATUS_RUNNING
804            && opts->maxusertime >= 0
805            && now > opts->child[i].date + opts->maxusertime)
806        {
807            if(opts->verbose)
808            {
809                finfo(stderr, opts, opts->child[i].seed);
810                fprintf(stderr, "running time exceeded, sending SIGTERM\n");
811            }
812            kill(opts->child[i].pid, SIGTERM);
813            opts->child[i].date = now;
814            opts->child[i].status = STATUS_SIGTERM;
815        }
816    }
817
818    /* Kill children if necessary (still there after 2 seconds) */
819    for(i = 0; i < opts->maxchild; i++)
820    {
821        if(opts->child[i].status == STATUS_SIGTERM
822            && now > opts->child[i].date + 2000000)
823        {
824            if(opts->verbose)
825            {
826                finfo(stderr, opts, opts->child[i].seed);
827                fprintf(stderr, "not responding, sending SIGKILL\n");
828            }
829            kill(opts->child[i].pid, SIGKILL);
830            opts->child[i].status = STATUS_SIGKILL;
831        }
832    }
833#endif
834
835    /* Collect dead children */
836    for(i = 0; i < opts->maxchild; i++)
837    {
838        uint8_t md5sum[16];
839#if defined HAVE_WAITPID
840        int status;
841        pid_t pid;
842#endif
843
844        if(opts->child[i].status != STATUS_SIGKILL
845            && opts->child[i].status != STATUS_SIGTERM
846            && opts->child[i].status != STATUS_EOF)
847            continue;
848
849#if defined HAVE_WAITPID
850        pid = waitpid(opts->child[i].pid, &status, WNOHANG);
851        if(pid <= 0)
852            continue;
853
854        if(opts->checkexit && WIFEXITED(status) && WEXITSTATUS(status))
855        {
856            finfo(stderr, opts, opts->child[i].seed);
857            fprintf(stderr, "exit %i\n", WEXITSTATUS(status));
858            opts->crashes++;
859        }
860        else if(WIFSIGNALED(status)
861                 && !(WTERMSIG(status) == SIGTERM
862                       && opts->child[i].status == STATUS_SIGTERM))
863        {
864            char const *message = "";
865
866            if(WTERMSIG(status) == SIGKILL && opts->maxmem >= 0)
867                message = " (memory exceeded?)";
868#   if defined SIGXCPU
869            else if(WTERMSIG(status) == SIGXCPU && opts->maxcpu >= 0)
870                message = " (CPU time exceeded?)";
871#   endif
872            else if(WTERMSIG(status) == SIGKILL && opts->maxcpu >= 0)
873                message = " (CPU time exceeded?)";
874
875            finfo(stderr, opts, opts->child[i].seed);
876            fprintf(stderr, "signal %i%s%s\n",
877                    WTERMSIG(status), sig2name(WTERMSIG(status)), message);
878            opts->crashes++;
879        }
880        else if (opts->verbose)
881        {
882            finfo(stderr, opts, opts->child[i].seed);
883            if (WIFSIGNALED(status))
884                fprintf(stderr, "signal %i%s\n",
885                        WTERMSIG(status), sig2name(WTERMSIG(status)));
886            else
887                fprintf(stderr, "exit %i\n", WEXITSTATUS(status));
888        }
889#else
890        /* waitpid() is not available. Don't kill the process. */
891        continue;
892#endif
893
894        for(j = 0; j < 3; j++)
895            if(opts->child[i].fd[j] >= 0)
896                close(opts->child[i].fd[j]);
897
898        if (opts->opmode == OPMODE_COPY)
899        {
900            for (j = myoptind + 1; j < opts->oldargc; j++)
901            {
902                if (opts->child[i].newargv[j - myoptind] != opts->oldargv[j])
903                {
904                    unlink(opts->child[i].newargv[j - myoptind]);
905                    free(opts->child[i].newargv[j - myoptind]);
906                    opts->child[i].newargv[j - myoptind] = opts->oldargv[j];
907                }
908            }
909        }
910
911        if(opts->md5)
912        {
913            _zz_md5_fini(md5sum, opts->child[i].ctx);
914            finfo(stdout, opts, opts->child[i].seed);
915            fprintf(stdout, "%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x"
916                    "%.02x%.02x%.02x%.02x%.02x%.02x%.02x\n", md5sum[0],
917                    md5sum[1], md5sum[2], md5sum[3], md5sum[4], md5sum[5],
918                    md5sum[6], md5sum[7], md5sum[8], md5sum[9], md5sum[10],
919                    md5sum[11], md5sum[12], md5sum[13], md5sum[14], md5sum[15]);
920            fflush(stdout);
921        }
922        opts->child[i].status = STATUS_FREE;
923        opts->nchild--;
924    }
925}
926
927static void read_children(struct opts *opts)
928{
929    struct timeval tv;
930    fd_set fdset;
931    int i, j, ret, maxfd = 0;
932
933    /* Read data from all sockets */
934    FD_ZERO(&fdset);
935    for(i = 0; i < opts->maxchild; i++)
936    {
937        if(opts->child[i].status != STATUS_RUNNING)
938            continue;
939
940        for(j = 0; j < 3; j++)
941            ZZUF_FD_SET(opts->child[i].fd[j], &fdset, maxfd);
942    }
943    tv.tv_sec = 0;
944    tv.tv_usec = 1000;
945
946#if _WIN32
947    for(i = 0; i < opts->maxchild; i++)
948        for (j = 0; j < 3; j++)
949        {
950            char tmpbuf[1025];
951            int tmp = _read(opts->child[i].fd[j], tmpbuf, 1024);
952            if (tmp > 0)
953            {
954                tmpbuf[tmp] = 0;
955                fprintf(stderr, "read %i bytes on fd %i: \"%s\"\n", tmp, j, tmpbuf);
956            }
957        }
958#endif
959
960    errno = 0;
961    ret = select(maxfd + 1, &fdset, NULL, NULL, &tv);
962    if(ret < 0 && errno)
963        perror("select");
964    if(ret <= 0)
965        return;
966
967    /* XXX: cute (i, j) iterating hack */
968    for(i = 0, j = 0; i < opts->maxchild; i += (j == 2), j = (j + 1) % 3)
969    {
970        uint8_t buf[BUFSIZ];
971
972        if(opts->child[i].status != STATUS_RUNNING)
973            continue;
974
975        if(!ZZUF_FD_ISSET(opts->child[i].fd[j], &fdset))
976            continue;
977
978        ret = read(opts->child[i].fd[j], buf, BUFSIZ - 1);
979        if(ret > 0)
980        {
981            /* We got data */
982            if(j != 0)
983                opts->child[i].bytes += ret;
984
985            if(opts->md5 && j == 2)
986                _zz_md5_add(opts->child[i].ctx, buf, ret);
987            else if(!opts->quiet || j == 0)
988                write((j < 2) ? STDERR_FILENO : STDOUT_FILENO, buf, ret);
989        }
990        else if(ret == 0)
991        {
992            /* End of file reached */
993            close(opts->child[i].fd[j]);
994            opts->child[i].fd[j] = -1;
995
996            if(opts->child[i].fd[0] == -1
997                && opts->child[i].fd[1] == -1
998                && opts->child[i].fd[2] == -1)
999                opts->child[i].status = STATUS_EOF;
1000        }
1001    }
1002}
1003
1004#if !defined HAVE_SETENV
1005static void setenv(char const *name, char const *value, int overwrite)
1006{
1007    char *str;
1008
1009    if(!overwrite && getenv(name))
1010        return;
1011
1012    str = malloc(strlen(name) + 1 + strlen(value) + 1);
1013    sprintf(str, "%s=%s", name, value);
1014    putenv(str);
1015}
1016#endif
1017
1018#if defined HAVE_WAITPID
1019static char const *sig2name(int signum)
1020{
1021    switch(signum)
1022    {
1023#ifdef SIGQUIT
1024        case SIGQUIT:  return " (SIGQUIT)"; /* 3 */
1025#endif
1026        case SIGILL:   return " (SIGILL)";  /* 4 */
1027#ifdef SIGTRAP
1028        case SIGTRAP:  return " (SIGTRAP)"; /* 5 */
1029#endif
1030        case SIGABRT:  return " (SIGABRT)"; /* 6 */
1031#ifdef SIGBUS
1032        case SIGBUS:   return " (SIGBUS)";  /* 7 */
1033#endif
1034        case SIGFPE:   return " (SIGFPE)";  /* 8 */
1035        case SIGSEGV:  return " (SIGSEGV)"; /* 11 */
1036        case SIGPIPE:  return " (SIGPIPE)"; /* 13 */
1037#ifdef SIGEMT
1038        case SIGEMT:   return " (SIGEMT)";  /* ? */
1039#endif
1040#ifdef SIGXCPU
1041        case SIGXCPU:  return " (SIGXCPU)"; /* 24 */
1042#endif
1043#ifdef SIGXFSZ
1044        case SIGXFSZ:  return " (SIGXFSZ)"; /* 25 */
1045#endif
1046#ifdef SIGSYS
1047        case SIGSYS:   return " (SIGSYS)";  /* 31 */
1048#endif
1049    }
1050
1051    return "";
1052}
1053#endif
1054
1055static void version(void)
1056{
1057    printf("zzuf %s\n", PACKAGE_VERSION);
1058    printf("Copyright (C) 2002-2010 Sam Hocevar <sam@hocevar.net>\n");
1059    printf("This program is free software. It comes without any warranty, to the extent\n");
1060    printf("permitted by applicable law. You can redistribute it and/or modify it under\n");
1061    printf("the terms of the Do What The Fuck You Want To Public License, Version 2, as\n");
1062    printf("published by Sam Hocevar. See <http://sam.zoy.org/wtfpl/> for more details.\n");
1063    printf("\n");
1064    printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n");
1065}
1066
1067static void usage(void)
1068{
1069#if defined HAVE_REGEX_H
1070    printf("Usage: zzuf [-aAcdimnqSvx] [-s seed|-s start:stop] [-r ratio|-r min:max]\n");
1071#else
1072    printf("Usage: zzuf [-aAdimnqSvx] [-s seed|-s start:stop] [-r ratio|-r min:max]\n");
1073#endif
1074    printf("              [-f mode] [-D delay] [-j jobs] [-C crashes] [-B bytes] [-a list]\n");
1075    printf("              [-t seconds]");
1076#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_CPU
1077    printf(                          " [-T seconds]");
1078#endif
1079#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
1080    printf(                                       " [-M mebibytes]");
1081#endif
1082    printf(                                                      " [-b ranges] [-p ports]\n");
1083    printf("              [-P protect] [-R refuse] [-l list]");
1084#if defined HAVE_REGEX_H
1085    printf(                                                " [-I include] [-E exclude]");
1086#endif
1087    printf("              [-O mode]\n");
1088    printf("\n");
1089    printf("              [PROGRAM [--] [ARGS]...]\n");
1090    printf("       zzuf -h | --help\n");
1091    printf("       zzuf -V | --version\n");
1092    printf("Run PROGRAM with optional arguments ARGS and fuzz its input.\n");
1093    printf("\n");
1094    printf("Mandatory arguments to long options are mandatory for short options too.\n");
1095    printf("  -a, --allow <list>        only fuzz network input for IPs in <list>\n");
1096    printf("          ... !<list>       do not fuzz network input for IPs in <list>\n");
1097    printf("  -A, --autoinc             increment seed each time a new file is opened\n");
1098    printf("  -b, --bytes <ranges>      only fuzz bytes at offsets within <ranges>\n");
1099    printf("  -B, --max-bytes <n>       kill children that output more than <n> bytes\n");
1100#if defined HAVE_REGEX_H
1101    printf("  -c, --cmdline             only fuzz files specified in the command line\n");
1102#endif
1103    printf("  -C, --max-crashes <n>     stop after <n> children have crashed (default 1)\n");
1104    printf("  -d, --debug               print debug messages (twice for more verbosity)\n");
1105    printf("  -D, --delay               delay between forks\n");
1106#if defined HAVE_REGEX_H
1107    printf("  -E, --exclude <regex>     do not fuzz files matching <regex>\n");
1108#endif
1109    printf("  -f, --fuzzing <mode>      use fuzzing mode <mode> ([xor] set unset)\n");
1110    printf("  -i, --stdin               fuzz standard input\n");
1111#if defined HAVE_REGEX_H
1112    printf("  -I, --include <regex>     only fuzz files matching <regex>\n");
1113#endif
1114    printf("  -j, --jobs <n>            number of simultaneous jobs (default 1)\n");
1115    printf("  -l, --list <list>         only fuzz Nth descriptor with N in <list>\n");
1116    printf("  -m, --md5                 compute the output's MD5 hash\n");
1117#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
1118    printf("  -M, --max-memory <n>      maximum child virtual memory in MiB (default %u)\n", DEFAULT_MEM);
1119#endif
1120    printf("  -n, --network             fuzz network input\n");
1121    printf("  -O, --opmode <mode>       use operating mode <mode> ([preload] copy)\n");
1122    printf("  -p, --ports <list>        only fuzz network destination ports in <list>\n");
1123    printf("  -P, --protect <list>      protect bytes and characters in <list>\n");
1124    printf("  -q, --quiet               do not print children's messages\n");
1125    printf("  -r, --ratio <ratio>       bit fuzzing ratio (default %g)\n", DEFAULT_RATIO);
1126    printf("          ... <start:stop>  specify a ratio range\n");
1127    printf("  -R, --refuse <list>       refuse bytes and characters in <list>\n");
1128    printf("  -s, --seed <seed>         random seed (default %i)\n", DEFAULT_SEED);
1129    printf("         ... <start:stop>   specify a seed range\n");
1130    printf("  -S, --signal              prevent children from diverting crashing signals\n");
1131    printf("  -t, --max-time <n>        stop spawning children after <n> seconds\n");
1132    printf("  -T, --max-cputime <n>     kill children that use more than <n> CPU seconds\n");
1133    printf("  -U, --max-usertime <n>    kill children that run for more than <n> seconds\n");
1134    printf("  -v, --verbose             print information during the run\n");
1135    printf("  -x, --check-exit          report processes that exit with a non-zero status\n");
1136    printf("  -h, --help                display this help and exit\n");
1137    printf("  -V, --version             output version information and exit\n");
1138    printf("\n");
1139    printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n");
1140}
1141
Note: See TracBrowser for help on using the repository browser.