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

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

Filedescriptor 0 is the debug channel, not stdin! Fix that.

  • Property svn:keywords set to Id
File size: 34.1 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        setenv("ZZUF_DEBUGFD", DEBUG_FILENO_STR, 1);
470
471        if(opts->fuzzing)
472            setenv("ZZUF_FUZZING", opts->fuzzing, 1);
473        if(opts->bytes)
474            setenv("ZZUF_BYTES", opts->bytes, 1);
475        if(opts->list)
476            setenv("ZZUF_LIST", opts->list, 1);
477        if(opts->ports)
478            setenv("ZZUF_PORTS", opts->ports, 1);
479        if(opts->allow && opts->allow[0] == '!')
480            setenv("ZZUF_DENY", opts->allow, 1);
481        else if(opts->allow)
482            setenv("ZZUF_ALLOW", opts->allow, 1);
483        if(opts->protect)
484            setenv("ZZUF_PROTECT", opts->protect, 1);
485        if(opts->refuse)
486            setenv("ZZUF_REFUSE", opts->refuse, 1);
487#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
488        if(opts->maxmem >= 0)
489        {
490            char buf[32];
491            snprintf(buf, 32, "%i", opts->maxmem);
492            setenv("ZZUF_MEMORY", buf, 1);
493        }
494#endif
495
496        /* Allocate memory for children handling */
497        opts->child = malloc(opts->maxchild * sizeof(struct child));
498        for(i = 0; i < opts->maxchild; i++)
499            opts->child[i].status = STATUS_FREE;
500        opts->nchild = 0;
501
502        /* Create new argv */
503        opts->oldargc = argc;
504        opts->oldargv = argv;
505        for(i = 0; i < opts->maxchild; i++)
506        {
507            int len = argc - myoptind;
508            opts->child[i].newargv = malloc((len + 1) * sizeof(char *));
509            memcpy(opts->child[i].newargv, argv + myoptind,
510                   len * sizeof(char *));
511            opts->child[i].newargv[len] = (char *)NULL;
512        }
513
514        /* Main loop */
515        while(opts->nchild || opts->seed < opts->endseed)
516        {
517            /* Spawn new children, if necessary */
518            spawn_children(opts);
519
520            /* Cleanup dead or dying children */
521            clean_children(opts);
522
523            /* Read data from children */
524            read_children(opts);
525
526            if(opts->maxcrashes && opts->crashes >= opts->maxcrashes
527                && opts->nchild == 0)
528            {
529                if(opts->verbose)
530                    fprintf(stderr,
531                            "zzuf: maximum crash count reached, exiting\n");
532                break;
533            }
534
535            if(opts->maxtime && _zz_time() - opts->starttime >= opts->maxtime
536                && opts->nchild == 0)
537            {
538                if(opts->verbose)
539                    fprintf(stderr,
540                            "zzuf: maximum running time reached, exiting\n");
541                break;
542            }
543        }
544    }
545
546    /* Clean up */
547    _zz_fd_fini();
548    _zz_opts_fini(opts);
549
550    return opts->crashes ? EXIT_FAILURE : EXIT_SUCCESS;
551}
552
553static void loop_stdin(struct opts *opts)
554{
555    uint8_t md5sum[16];
556    struct md5 *ctx = NULL;
557    int total = 0;
558
559    if(opts->md5)
560        ctx = _zz_md5_init();
561
562    _zz_register(0);
563
564    for(;;)
565    {
566        uint8_t buf[BUFSIZ];
567        int ret, toread = BUFSIZ, off = 0, nw = 0;
568
569        if(opts->maxbytes >= 0)
570        {
571            if(total >= opts->maxbytes)
572                break;
573            if(total + BUFSIZ >= opts->maxbytes)
574                toread = opts->maxbytes - total;
575        }
576
577        ret = read(0, buf, toread);
578        if(ret <= 0)
579            break;
580
581        total += ret;
582
583        _zz_fuzz(0, buf, ret);
584        _zz_addpos(0, ret);
585
586        if(opts->md5)
587            _zz_md5_add(ctx, buf, ret);
588        else while(ret)
589        {
590            if((nw = write(1, buf + off, (unsigned int)ret)) < 0)
591                break;
592            ret -= nw;
593            off += nw;
594        }
595    }
596
597    if(opts->md5)
598    {
599        _zz_md5_fini(md5sum, ctx);
600        finfo(stdout, opts, opts->seed);
601        fprintf(stdout, "%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x"
602                "%.02x%.02x%.02x%.02x%.02x%.02x\n", md5sum[0], md5sum[1],
603                md5sum[2], md5sum[3], md5sum[4], md5sum[5], md5sum[6],
604                md5sum[7], md5sum[8], md5sum[9], md5sum[10], md5sum[11],
605                md5sum[12], md5sum[13], md5sum[14], md5sum[15]);
606        fflush(stdout);
607    }
608
609    _zz_unregister(0);
610}
611
612static void finfo(FILE *fp, struct opts *opts, uint32_t seed)
613{
614    if(opts->minratio == opts->maxratio)
615        fprintf(fp, "zzuf[s=%i,r=%g]: ", seed, opts->minratio);
616    else
617        fprintf(fp, "zzuf[s=%i,r=%g:%g]: ", seed,
618                opts->minratio, opts->maxratio);
619}
620
621#if defined HAVE_REGEX_H
622static char *merge_file(char *regex, char *file)
623{
624    char *newfile = malloc(5 + 2 * strlen(file) + 1 + 1), *tmp = newfile;
625
626    *tmp++ = '(';
627    *tmp++ = '^';
628    *tmp++ = '|';
629    *tmp++ = '/';
630    *tmp++ = ')';
631    while(*file)
632    {
633        if(strchr("^.[$()|*+?{\\", *file))
634            *tmp++ = '\\';
635        *tmp++ = *file++;
636    }
637    *tmp++ = '$';
638    *tmp++ = '\0';
639
640    tmp = merge_regex(regex, newfile);
641    free(newfile);
642    return tmp;
643}
644
645static char *merge_regex(char *regex, char *string)
646{
647    regex_t optre;
648
649    if(regex)
650    {
651        regex = realloc(regex, strlen(regex) + strlen(string) + 1 + 1);
652        sprintf(regex + strlen(regex) - 1, "|%s)", string);
653    }
654    else
655    {
656        regex = malloc(1 + strlen(string) + 1 + 1);
657        sprintf(regex, "(%s)", string);
658    }
659
660    if(regcomp(&optre, regex, REG_EXTENDED) != 0)
661    {
662        free(regex);
663        return NULL;
664    }
665    regfree(&optre);
666
667    return regex;
668}
669#endif
670
671static void spawn_children(struct opts *opts)
672{
673    int64_t now = _zz_time();
674    int i;
675
676    if(opts->nchild == opts->maxchild)
677        return; /* no slot */
678
679    if(opts->seed == opts->endseed)
680        return; /* job finished */
681
682    if(opts->maxcrashes && opts->crashes >= opts->maxcrashes)
683        return; /* all jobs crashed */
684
685    if(opts->maxtime && now - opts->starttime >= opts->maxtime)
686        return; /* run time exceeded */
687
688    if(opts->delay > 0 && opts->lastlaunch + opts->delay > now)
689        return; /* too early */
690
691    /* Find the empty slot */
692    for(i = 0; i < opts->maxchild; i++)
693        if(opts->child[i].status == STATUS_FREE)
694            break;
695
696    /* Prepare required files, if necessary */
697    if (opts->opmode == OPMODE_COPY)
698    {
699        char tmpname[4096];
700        char *tmpdir;
701        FILE *fpin;
702        int j, k = 0, fdout;
703
704        tmpdir = getenv("TEMP");
705        if (!tmpdir || !*tmpdir)
706            tmpdir = "/tmp";
707
708        for (j = myoptind + 1; j < opts->oldargc; j++)
709        {
710            fpin = fopen(opts->oldargv[j], "r");
711            if (!fpin)
712                continue;
713
714            sprintf(tmpname, "%s/zzuf.%i.XXXXXX", tmpdir, (int)getpid());
715#ifdef _WIN32
716            fdout = mktemp(tmpname);
717#else
718            fdout = mkstemp(tmpname);
719#endif
720            if (fdout < 0)
721            {
722                fclose(fpin);
723                continue;
724            }
725
726            opts->child[i].newargv[j - myoptind] = strdup(tmpname);
727
728            _zz_register(k);
729            while(!feof(fpin))
730            {
731                uint8_t buf[BUFSIZ];
732                size_t n = fread(buf, 1, BUFSIZ, fpin);
733                if (n <= 0)
734                    break;
735                _zz_fuzz(k, buf, n);
736                _zz_addpos(k, n);
737                write(fdout, buf, n);
738            }
739            _zz_unregister(k);
740
741            fclose(fpin);
742            close(fdout);
743
744            k++;
745        }
746    }
747
748    /* Launch process */
749    if (myfork(&opts->child[i], opts) < 0)
750    {
751        fprintf(stderr, "error launching `%s'\n", opts->child[i].newargv[0]);
752        opts->seed++;
753        /* FIXME: clean up OPMODE_COPY files here */
754        return;
755    }
756
757    /* We’re the parent, acknowledge spawn */
758    opts->child[i].date = now;
759    opts->child[i].bytes = 0;
760    opts->child[i].seed = opts->seed;
761    opts->child[i].ratio = _zz_getratio();
762    opts->child[i].status = STATUS_RUNNING;
763    if(opts->md5)
764        opts->child[i].ctx = _zz_md5_init();
765
766    if(opts->verbose)
767    {
768        finfo(stderr, opts, opts->child[i].seed);
769        fprintf(stderr, "launched `%s'\n", opts->child[i].newargv[0]);
770    }
771
772    opts->lastlaunch = now;
773    opts->nchild++;
774    opts->seed++;
775
776    _zz_setseed(opts->seed);
777}
778
779static void clean_children(struct opts *opts)
780{
781#if defined HAVE_KILL
782    int64_t now = _zz_time();
783#endif
784    int i, j;
785
786#if defined HAVE_KILL
787    /* Terminate children if necessary */
788    for(i = 0; i < opts->maxchild; i++)
789    {
790        if(opts->child[i].status == STATUS_RUNNING
791            && opts->maxbytes >= 0
792            && opts->child[i].bytes > opts->maxbytes)
793        {
794            if(opts->verbose)
795            {
796                finfo(stderr, opts, opts->child[i].seed);
797                fprintf(stderr, "data output exceeded, sending SIGTERM\n");
798            }
799            kill(opts->child[i].pid, SIGTERM);
800            opts->child[i].date = now;
801            opts->child[i].status = STATUS_SIGTERM;
802        }
803
804        if(opts->child[i].status == STATUS_RUNNING
805            && opts->maxusertime >= 0
806            && now > opts->child[i].date + opts->maxusertime)
807        {
808            if(opts->verbose)
809            {
810                finfo(stderr, opts, opts->child[i].seed);
811                fprintf(stderr, "running time exceeded, sending SIGTERM\n");
812            }
813            kill(opts->child[i].pid, SIGTERM);
814            opts->child[i].date = now;
815            opts->child[i].status = STATUS_SIGTERM;
816        }
817    }
818
819    /* Kill children if necessary (still there after 2 seconds) */
820    for(i = 0; i < opts->maxchild; i++)
821    {
822        if(opts->child[i].status == STATUS_SIGTERM
823            && now > opts->child[i].date + 2000000)
824        {
825            if(opts->verbose)
826            {
827                finfo(stderr, opts, opts->child[i].seed);
828                fprintf(stderr, "not responding, sending SIGKILL\n");
829            }
830            kill(opts->child[i].pid, SIGKILL);
831            opts->child[i].status = STATUS_SIGKILL;
832        }
833    }
834#endif
835
836    /* Collect dead children */
837    for(i = 0; i < opts->maxchild; i++)
838    {
839        uint8_t md5sum[16];
840#if defined HAVE_WAITPID
841        int status;
842        pid_t pid;
843#endif
844
845        if(opts->child[i].status != STATUS_SIGKILL
846            && opts->child[i].status != STATUS_SIGTERM
847            && opts->child[i].status != STATUS_EOF)
848            continue;
849
850#if defined HAVE_WAITPID
851        pid = waitpid(opts->child[i].pid, &status, WNOHANG);
852        if(pid <= 0)
853            continue;
854
855        if(opts->checkexit && WIFEXITED(status) && WEXITSTATUS(status))
856        {
857            finfo(stderr, opts, opts->child[i].seed);
858            fprintf(stderr, "exit %i\n", WEXITSTATUS(status));
859            opts->crashes++;
860        }
861        else if(WIFSIGNALED(status)
862                 && !(WTERMSIG(status) == SIGTERM
863                       && opts->child[i].status == STATUS_SIGTERM))
864        {
865            char const *message = "";
866
867            if(WTERMSIG(status) == SIGKILL && opts->maxmem >= 0)
868                message = " (memory exceeded?)";
869#   if defined SIGXCPU
870            else if(WTERMSIG(status) == SIGXCPU && opts->maxcpu >= 0)
871                message = " (CPU time exceeded?)";
872#   endif
873            else if(WTERMSIG(status) == SIGKILL && opts->maxcpu >= 0)
874                message = " (CPU time exceeded?)";
875
876            finfo(stderr, opts, opts->child[i].seed);
877            fprintf(stderr, "signal %i%s%s\n",
878                    WTERMSIG(status), sig2name(WTERMSIG(status)), message);
879            opts->crashes++;
880        }
881        else if (opts->verbose)
882        {
883            finfo(stderr, opts, opts->child[i].seed);
884            if (WIFSIGNALED(status))
885                fprintf(stderr, "signal %i%s\n",
886                        WTERMSIG(status), sig2name(WTERMSIG(status)));
887            else
888                fprintf(stderr, "exit %i\n", WEXITSTATUS(status));
889        }
890#else
891        /* waitpid() is not available. Don't kill the process. */
892        continue;
893#endif
894
895        for(j = 0; j < 3; j++)
896            if(opts->child[i].fd[j] >= 0)
897                close(opts->child[i].fd[j]);
898
899        if (opts->opmode == OPMODE_COPY)
900        {
901            for (j = myoptind + 1; j < opts->oldargc; j++)
902            {
903                if (opts->child[i].newargv[j - myoptind] != opts->oldargv[j])
904                {
905                    unlink(opts->child[i].newargv[j - myoptind]);
906                    free(opts->child[i].newargv[j - myoptind]);
907                    opts->child[i].newargv[j - myoptind] = opts->oldargv[j];
908                }
909            }
910        }
911
912        if(opts->md5)
913        {
914            _zz_md5_fini(md5sum, opts->child[i].ctx);
915            finfo(stdout, opts, opts->child[i].seed);
916            fprintf(stdout, "%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x"
917                    "%.02x%.02x%.02x%.02x%.02x%.02x%.02x\n", md5sum[0],
918                    md5sum[1], md5sum[2], md5sum[3], md5sum[4], md5sum[5],
919                    md5sum[6], md5sum[7], md5sum[8], md5sum[9], md5sum[10],
920                    md5sum[11], md5sum[12], md5sum[13], md5sum[14], md5sum[15]);
921            fflush(stdout);
922        }
923        opts->child[i].status = STATUS_FREE;
924        opts->nchild--;
925    }
926}
927
928static void read_children(struct opts *opts)
929{
930    struct timeval tv;
931    fd_set fdset;
932    int i, j, ret, maxfd = 0;
933
934    /* Read data from all sockets */
935    FD_ZERO(&fdset);
936    for(i = 0; i < opts->maxchild; i++)
937    {
938        if(opts->child[i].status != STATUS_RUNNING)
939            continue;
940
941        for(j = 0; j < 3; j++)
942            ZZUF_FD_SET(opts->child[i].fd[j], &fdset, maxfd);
943    }
944    tv.tv_sec = 0;
945    tv.tv_usec = 1000;
946
947#if _WIN32
948    for(i = 0; i < opts->maxchild; i++)
949        for (j = 0; j < 3; j++)
950        {
951            char tmpbuf[1025];
952            int tmp = _read(opts->child[i].fd[j], tmpbuf, 1024);
953            if (tmp > 0)
954            {
955                tmpbuf[tmp] = 0;
956                fprintf(stderr, "read %i bytes on fd %i: \"%s\"\n", tmp, j, tmpbuf);
957            }
958        }
959#endif
960
961    errno = 0;
962    ret = select(maxfd + 1, &fdset, NULL, NULL, &tv);
963    if(ret < 0 && errno)
964        perror("select");
965    if(ret <= 0)
966        return;
967
968    /* XXX: cute (i, j) iterating hack */
969    for(i = 0, j = 0; i < opts->maxchild; i += (j == 2), j = (j + 1) % 3)
970    {
971        uint8_t buf[BUFSIZ];
972
973        if(opts->child[i].status != STATUS_RUNNING)
974            continue;
975
976        if(!ZZUF_FD_ISSET(opts->child[i].fd[j], &fdset))
977            continue;
978
979        ret = read(opts->child[i].fd[j], buf, BUFSIZ - 1);
980        if(ret > 0)
981        {
982            /* We got data */
983            if(j != 0)
984                opts->child[i].bytes += ret;
985
986            if(opts->md5 && j == 2)
987                _zz_md5_add(opts->child[i].ctx, buf, ret);
988            else if(!opts->quiet || j == 0)
989                write((j < 2) ? STDERR_FILENO : STDOUT_FILENO, buf, ret);
990        }
991        else if(ret == 0)
992        {
993            /* End of file reached */
994            close(opts->child[i].fd[j]);
995            opts->child[i].fd[j] = -1;
996
997            if(opts->child[i].fd[0] == -1
998                && opts->child[i].fd[1] == -1
999                && opts->child[i].fd[2] == -1)
1000                opts->child[i].status = STATUS_EOF;
1001        }
1002    }
1003}
1004
1005#if !defined HAVE_SETENV
1006static void setenv(char const *name, char const *value, int overwrite)
1007{
1008    char *str;
1009
1010    if(!overwrite && getenv(name))
1011        return;
1012
1013    str = malloc(strlen(name) + 1 + strlen(value) + 1);
1014    sprintf(str, "%s=%s", name, value);
1015    putenv(str);
1016}
1017#endif
1018
1019#if defined HAVE_WAITPID
1020static char const *sig2name(int signum)
1021{
1022    switch(signum)
1023    {
1024#ifdef SIGQUIT
1025        case SIGQUIT:  return " (SIGQUIT)"; /* 3 */
1026#endif
1027        case SIGILL:   return " (SIGILL)";  /* 4 */
1028#ifdef SIGTRAP
1029        case SIGTRAP:  return " (SIGTRAP)"; /* 5 */
1030#endif
1031        case SIGABRT:  return " (SIGABRT)"; /* 6 */
1032#ifdef SIGBUS
1033        case SIGBUS:   return " (SIGBUS)";  /* 7 */
1034#endif
1035        case SIGFPE:   return " (SIGFPE)";  /* 8 */
1036        case SIGSEGV:  return " (SIGSEGV)"; /* 11 */
1037        case SIGPIPE:  return " (SIGPIPE)"; /* 13 */
1038#ifdef SIGEMT
1039        case SIGEMT:   return " (SIGEMT)";  /* ? */
1040#endif
1041#ifdef SIGXCPU
1042        case SIGXCPU:  return " (SIGXCPU)"; /* 24 */
1043#endif
1044#ifdef SIGXFSZ
1045        case SIGXFSZ:  return " (SIGXFSZ)"; /* 25 */
1046#endif
1047#ifdef SIGSYS
1048        case SIGSYS:   return " (SIGSYS)";  /* 31 */
1049#endif
1050    }
1051
1052    return "";
1053}
1054#endif
1055
1056static void version(void)
1057{
1058    printf("zzuf %s\n", PACKAGE_VERSION);
1059    printf("Copyright (C) 2002-2010 Sam Hocevar <sam@hocevar.net>\n");
1060    printf("This program is free software. It comes without any warranty, to the extent\n");
1061    printf("permitted by applicable law. You can redistribute it and/or modify it under\n");
1062    printf("the terms of the Do What The Fuck You Want To Public License, Version 2, as\n");
1063    printf("published by Sam Hocevar. See <http://sam.zoy.org/wtfpl/> for more details.\n");
1064    printf("\n");
1065    printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n");
1066}
1067
1068static void usage(void)
1069{
1070#if defined HAVE_REGEX_H
1071    printf("Usage: zzuf [-aAcdimnqSvx] [-s seed|-s start:stop] [-r ratio|-r min:max]\n");
1072#else
1073    printf("Usage: zzuf [-aAdimnqSvx] [-s seed|-s start:stop] [-r ratio|-r min:max]\n");
1074#endif
1075    printf("              [-f mode] [-D delay] [-j jobs] [-C crashes] [-B bytes] [-a list]\n");
1076    printf("              [-t seconds]");
1077#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_CPU
1078    printf(                          " [-T seconds]");
1079#endif
1080#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
1081    printf(                                       " [-M mebibytes]");
1082#endif
1083    printf(                                                      " [-b ranges] [-p ports]\n");
1084    printf("              [-P protect] [-R refuse] [-l list]");
1085#if defined HAVE_REGEX_H
1086    printf(                                                " [-I include] [-E exclude]");
1087#endif
1088    printf("              [-O mode]\n");
1089    printf("\n");
1090    printf("              [PROGRAM [--] [ARGS]...]\n");
1091    printf("       zzuf -h | --help\n");
1092    printf("       zzuf -V | --version\n");
1093    printf("Run PROGRAM with optional arguments ARGS and fuzz its input.\n");
1094    printf("\n");
1095    printf("Mandatory arguments to long options are mandatory for short options too.\n");
1096    printf("  -a, --allow <list>        only fuzz network input for IPs in <list>\n");
1097    printf("          ... !<list>       do not fuzz network input for IPs in <list>\n");
1098    printf("  -A, --autoinc             increment seed each time a new file is opened\n");
1099    printf("  -b, --bytes <ranges>      only fuzz bytes at offsets within <ranges>\n");
1100    printf("  -B, --max-bytes <n>       kill children that output more than <n> bytes\n");
1101#if defined HAVE_REGEX_H
1102    printf("  -c, --cmdline             only fuzz files specified in the command line\n");
1103#endif
1104    printf("  -C, --max-crashes <n>     stop after <n> children have crashed (default 1)\n");
1105    printf("  -d, --debug               print debug messages (twice for more verbosity)\n");
1106    printf("  -D, --delay               delay between forks\n");
1107#if defined HAVE_REGEX_H
1108    printf("  -E, --exclude <regex>     do not fuzz files matching <regex>\n");
1109#endif
1110    printf("  -f, --fuzzing <mode>      use fuzzing mode <mode> ([xor] set unset)\n");
1111    printf("  -i, --stdin               fuzz standard input\n");
1112#if defined HAVE_REGEX_H
1113    printf("  -I, --include <regex>     only fuzz files matching <regex>\n");
1114#endif
1115    printf("  -j, --jobs <n>            number of simultaneous jobs (default 1)\n");
1116    printf("  -l, --list <list>         only fuzz Nth descriptor with N in <list>\n");
1117    printf("  -m, --md5                 compute the output's MD5 hash\n");
1118#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
1119    printf("  -M, --max-memory <n>      maximum child virtual memory in MiB (default %u)\n", DEFAULT_MEM);
1120#endif
1121    printf("  -n, --network             fuzz network input\n");
1122    printf("  -O, --opmode <mode>       use operating mode <mode> ([preload] copy)\n");
1123    printf("  -p, --ports <list>        only fuzz network destination ports in <list>\n");
1124    printf("  -P, --protect <list>      protect bytes and characters in <list>\n");
1125    printf("  -q, --quiet               do not print children's messages\n");
1126    printf("  -r, --ratio <ratio>       bit fuzzing ratio (default %g)\n", DEFAULT_RATIO);
1127    printf("          ... <start:stop>  specify a ratio range\n");
1128    printf("  -R, --refuse <list>       refuse bytes and characters in <list>\n");
1129    printf("  -s, --seed <seed>         random seed (default %i)\n", DEFAULT_SEED);
1130    printf("         ... <start:stop>   specify a seed range\n");
1131    printf("  -S, --signal              prevent children from diverting crashing signals\n");
1132    printf("  -t, --max-time <n>        stop spawning children after <n> seconds\n");
1133    printf("  -T, --max-cputime <n>     kill children that use more than <n> CPU seconds\n");
1134    printf("  -U, --max-usertime <n>    kill children that run for more than <n> seconds\n");
1135    printf("  -v, --verbose             print information during the run\n");
1136    printf("  -x, --check-exit          report processes that exit with a non-zero status\n");
1137    printf("  -h, --help                display this help and exit\n");
1138    printf("  -V, --version             output version information and exit\n");
1139    printf("\n");
1140    printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n");
1141}
1142
Note: See TracBrowser for help on using the repository browser.