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

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

New operating mode "copy". It uses temporary files instead of preloading
libzzuf into the process.

  • Property svn:keywords set to Id
File size: 33.6 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 = optind + 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            fdout = mkstemp(tmpname);
716            if (fdout < 0)
717            {
718                fclose(fpin);
719                continue;
720            }
721
722            opts->child[i].newargv[j - optind] = strdup(tmpname);
723
724            _zz_register(k);
725            while(!feof(fpin))
726            {
727                uint8_t buf[BUFSIZ];
728                size_t n = fread(buf, 1, BUFSIZ, fpin);
729                if (n <= 0)
730                    break;
731                _zz_fuzz(k, buf, n);
732                _zz_addpos(k, n);
733                write(fdout, buf, n);
734            }
735            _zz_unregister(k);
736
737            fclose(fpin);
738            close(fdout);
739
740            k++;
741        }
742    }
743
744    /* Launch process */
745    if (myfork(&opts->child[i], opts) < 0)
746    {
747        fprintf(stderr, "error launching `%s'\n", opts->child[i].newargv[0]);
748        opts->seed++;
749        /* FIXME: clean up OPMODE_COPY files here */
750        return;
751    }
752
753    /* We’re the parent, acknowledge spawn */
754    opts->child[i].date = now;
755    opts->child[i].bytes = 0;
756    opts->child[i].seed = opts->seed;
757    opts->child[i].ratio = _zz_getratio();
758    opts->child[i].status = STATUS_RUNNING;
759    if(opts->md5)
760        opts->child[i].ctx = _zz_md5_init();
761
762    if(opts->verbose)
763    {
764        finfo(stderr, opts, opts->child[i].seed);
765        fprintf(stderr, "launched `%s'\n", opts->child[i].newargv[0]);
766    }
767
768    opts->lastlaunch = now;
769    opts->nchild++;
770    opts->seed++;
771
772    _zz_setseed(opts->seed);
773}
774
775static void clean_children(struct opts *opts)
776{
777#if defined HAVE_KILL
778    int64_t now = _zz_time();
779#endif
780    int i, j;
781
782#if defined HAVE_KILL
783    /* Terminate children if necessary */
784    for(i = 0; i < opts->maxchild; i++)
785    {
786        if(opts->child[i].status == STATUS_RUNNING
787            && opts->maxbytes >= 0
788            && opts->child[i].bytes > opts->maxbytes)
789        {
790            if(opts->verbose)
791            {
792                finfo(stderr, opts, opts->child[i].seed);
793                fprintf(stderr, "data output exceeded, sending SIGTERM\n");
794            }
795            kill(opts->child[i].pid, SIGTERM);
796            opts->child[i].date = now;
797            opts->child[i].status = STATUS_SIGTERM;
798        }
799
800        if(opts->child[i].status == STATUS_RUNNING
801            && opts->maxusertime >= 0
802            && now > opts->child[i].date + opts->maxusertime)
803        {
804            if(opts->verbose)
805            {
806                finfo(stderr, opts, opts->child[i].seed);
807                fprintf(stderr, "running time exceeded, sending SIGTERM\n");
808            }
809            kill(opts->child[i].pid, SIGTERM);
810            opts->child[i].date = now;
811            opts->child[i].status = STATUS_SIGTERM;
812        }
813    }
814
815    /* Kill children if necessary (still there after 2 seconds) */
816    for(i = 0; i < opts->maxchild; i++)
817    {
818        if(opts->child[i].status == STATUS_SIGTERM
819            && now > opts->child[i].date + 2000000)
820        {
821            if(opts->verbose)
822            {
823                finfo(stderr, opts, opts->child[i].seed);
824                fprintf(stderr, "not responding, sending SIGKILL\n");
825            }
826            kill(opts->child[i].pid, SIGKILL);
827            opts->child[i].status = STATUS_SIGKILL;
828        }
829    }
830#endif
831
832    /* Collect dead children */
833    for(i = 0; i < opts->maxchild; i++)
834    {
835        uint8_t md5sum[16];
836#if defined HAVE_WAITPID
837        int status;
838        pid_t pid;
839#endif
840
841        if(opts->child[i].status != STATUS_SIGKILL
842            && opts->child[i].status != STATUS_SIGTERM
843            && opts->child[i].status != STATUS_EOF)
844            continue;
845
846#if defined HAVE_WAITPID
847        pid = waitpid(opts->child[i].pid, &status, WNOHANG);
848        if(pid <= 0)
849            continue;
850
851        if(opts->checkexit && WIFEXITED(status) && WEXITSTATUS(status))
852        {
853            finfo(stderr, opts, opts->child[i].seed);
854            fprintf(stderr, "exit %i\n", WEXITSTATUS(status));
855            opts->crashes++;
856        }
857        else if(WIFSIGNALED(status)
858                 && !(WTERMSIG(status) == SIGTERM
859                       && opts->child[i].status == STATUS_SIGTERM))
860        {
861            char const *message = "";
862
863            if(WTERMSIG(status) == SIGKILL && opts->maxmem >= 0)
864                message = " (memory exceeded?)";
865#   if defined SIGXCPU
866            else if(WTERMSIG(status) == SIGXCPU && opts->maxcpu >= 0)
867                message = " (CPU time exceeded?)";
868#   endif
869            else if(WTERMSIG(status) == SIGKILL && opts->maxcpu >= 0)
870                message = " (CPU time exceeded?)";
871
872            finfo(stderr, opts, opts->child[i].seed);
873            fprintf(stderr, "signal %i%s%s\n",
874                    WTERMSIG(status), sig2name(WTERMSIG(status)), message);
875            opts->crashes++;
876        }
877        else if (opts->verbose)
878        {
879            finfo(stderr, opts, opts->child[i].seed);
880            if (WIFSIGNALED(status))
881                fprintf(stderr, "signal %i%s\n",
882                        WTERMSIG(status), sig2name(WTERMSIG(status)));
883            else
884                fprintf(stderr, "exit %i\n", WEXITSTATUS(status));
885        }
886#else
887        /* waitpid() is not available. Don't kill the process. */
888        continue;
889#endif
890
891        for(j = 0; j < 3; j++)
892            if(opts->child[i].fd[j] >= 0)
893                close(opts->child[i].fd[j]);
894
895        if (opts->opmode == OPMODE_COPY)
896        {
897            for (j = optind + 1; j < opts->oldargc; j++)
898            {
899                if (opts->child[i].newargv[j - optind] != opts->oldargv[j])
900                {
901                    unlink(opts->child[i].newargv[j - optind]);
902                    free(opts->child[i].newargv[j - optind]);
903                    opts->child[i].newargv[j - optind] = opts->oldargv[j];
904                }
905            }
906        }
907
908        if(opts->md5)
909        {
910            _zz_md5_fini(md5sum, opts->child[i].ctx);
911            finfo(stdout, opts, opts->child[i].seed);
912            fprintf(stdout, "%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x"
913                    "%.02x%.02x%.02x%.02x%.02x%.02x%.02x\n", md5sum[0],
914                    md5sum[1], md5sum[2], md5sum[3], md5sum[4], md5sum[5],
915                    md5sum[6], md5sum[7], md5sum[8], md5sum[9], md5sum[10],
916                    md5sum[11], md5sum[12], md5sum[13], md5sum[14], md5sum[15]);
917            fflush(stdout);
918        }
919        opts->child[i].status = STATUS_FREE;
920        opts->nchild--;
921    }
922}
923
924static void read_children(struct opts *opts)
925{
926    struct timeval tv;
927    fd_set fdset;
928    int i, j, ret, maxfd = 0;
929
930    /* Read data from all sockets */
931    FD_ZERO(&fdset);
932    for(i = 0; i < opts->maxchild; i++)
933    {
934        if(opts->child[i].status != STATUS_RUNNING)
935            continue;
936
937        for(j = 0; j < 3; j++)
938            ZZUF_FD_SET(opts->child[i].fd[j], &fdset, maxfd);
939    }
940    tv.tv_sec = 0;
941    tv.tv_usec = 1000;
942
943    ret = select(maxfd + 1, &fdset, NULL, NULL, &tv);
944    if(ret < 0 && errno)
945        perror("select");
946    if(ret <= 0)
947        return;
948
949    /* XXX: cute (i, j) iterating hack */
950    for(i = 0, j = 0; i < opts->maxchild; i += (j == 2), j = (j + 1) % 3)
951    {
952        uint8_t buf[BUFSIZ];
953
954        if(opts->child[i].status != STATUS_RUNNING)
955            continue;
956
957        if(!ZZUF_FD_ISSET(opts->child[i].fd[j], &fdset))
958            continue;
959
960        ret = read(opts->child[i].fd[j], buf, BUFSIZ - 1);
961        if(ret > 0)
962        {
963            /* We got data */
964            if(j != 0)
965                opts->child[i].bytes += ret;
966
967            if(opts->md5 && j == 2)
968                _zz_md5_add(opts->child[i].ctx, buf, ret);
969            else if(!opts->quiet || j == 0)
970                write((j < 2) ? STDERR_FILENO : STDOUT_FILENO, buf, ret);
971        }
972        else if(ret == 0)
973        {
974            /* End of file reached */
975            close(opts->child[i].fd[j]);
976            opts->child[i].fd[j] = -1;
977
978            if(opts->child[i].fd[0] == -1
979                && opts->child[i].fd[1] == -1
980                && opts->child[i].fd[2] == -1)
981                opts->child[i].status = STATUS_EOF;
982        }
983    }
984}
985
986#if !defined HAVE_SETENV
987static void setenv(char const *name, char const *value, int overwrite)
988{
989    char *str;
990
991    if(!overwrite && getenv(name))
992        return;
993
994    str = malloc(strlen(name) + 1 + strlen(value) + 1);
995    sprintf(str, "%s=%s", name, value);
996    putenv(str);
997}
998#endif
999
1000#if defined HAVE_WAITPID
1001static char const *sig2name(int signum)
1002{
1003    switch(signum)
1004    {
1005#ifdef SIGQUIT
1006        case SIGQUIT:  return " (SIGQUIT)"; /* 3 */
1007#endif
1008        case SIGILL:   return " (SIGILL)";  /* 4 */
1009#ifdef SIGTRAP
1010        case SIGTRAP:  return " (SIGTRAP)"; /* 5 */
1011#endif
1012        case SIGABRT:  return " (SIGABRT)"; /* 6 */
1013#ifdef SIGBUS
1014        case SIGBUS:   return " (SIGBUS)";  /* 7 */
1015#endif
1016        case SIGFPE:   return " (SIGFPE)";  /* 8 */
1017        case SIGSEGV:  return " (SIGSEGV)"; /* 11 */
1018        case SIGPIPE:  return " (SIGPIPE)"; /* 13 */
1019#ifdef SIGEMT
1020        case SIGEMT:   return " (SIGEMT)";  /* ? */
1021#endif
1022#ifdef SIGXCPU
1023        case SIGXCPU:  return " (SIGXCPU)"; /* 24 */
1024#endif
1025#ifdef SIGXFSZ
1026        case SIGXFSZ:  return " (SIGXFSZ)"; /* 25 */
1027#endif
1028#ifdef SIGSYS
1029        case SIGSYS:   return " (SIGSYS)";  /* 31 */
1030#endif
1031    }
1032
1033    return "";
1034}
1035#endif
1036
1037static void version(void)
1038{
1039    printf("zzuf %s\n", PACKAGE_VERSION);
1040    printf("Copyright (C) 2002-2010 Sam Hocevar <sam@hocevar.net>\n");
1041    printf("This program is free software. It comes without any warranty, to the extent\n");
1042    printf("permitted by applicable law. You can redistribute it and/or modify it under\n");
1043    printf("the terms of the Do What The Fuck You Want To Public License, Version 2, as\n");
1044    printf("published by Sam Hocevar. See <http://sam.zoy.org/wtfpl/> for more details.\n");
1045    printf("\n");
1046    printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n");
1047}
1048
1049static void usage(void)
1050{
1051#if defined HAVE_REGEX_H
1052    printf("Usage: zzuf [-aAcdimnqSvx] [-s seed|-s start:stop] [-r ratio|-r min:max]\n");
1053#else
1054    printf("Usage: zzuf [-aAdimnqSvx] [-s seed|-s start:stop] [-r ratio|-r min:max]\n");
1055#endif
1056    printf("              [-f mode] [-D delay] [-j jobs] [-C crashes] [-B bytes] [-a list]\n");
1057    printf("              [-t seconds]");
1058#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_CPU
1059    printf(                          " [-T seconds]");
1060#endif
1061#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
1062    printf(                                       " [-M mebibytes]");
1063#endif
1064    printf(                                                      " [-b ranges] [-p ports]\n");
1065    printf("              [-P protect] [-R refuse] [-l list]");
1066#if defined HAVE_REGEX_H
1067    printf(                                                " [-I include] [-E exclude]");
1068#endif
1069    printf("              [-O mode]\n");
1070    printf("\n");
1071    printf("              [PROGRAM [--] [ARGS]...]\n");
1072    printf("       zzuf -h | --help\n");
1073    printf("       zzuf -V | --version\n");
1074    printf("Run PROGRAM with optional arguments ARGS and fuzz its input.\n");
1075    printf("\n");
1076    printf("Mandatory arguments to long options are mandatory for short options too.\n");
1077    printf("  -a, --allow <list>        only fuzz network input for IPs in <list>\n");
1078    printf("          ... !<list>       do not fuzz network input for IPs in <list>\n");
1079    printf("  -A, --autoinc             increment seed each time a new file is opened\n");
1080    printf("  -b, --bytes <ranges>      only fuzz bytes at offsets within <ranges>\n");
1081    printf("  -B, --max-bytes <n>       kill children that output more than <n> bytes\n");
1082#if defined HAVE_REGEX_H
1083    printf("  -c, --cmdline             only fuzz files specified in the command line\n");
1084#endif
1085    printf("  -C, --max-crashes <n>     stop after <n> children have crashed (default 1)\n");
1086    printf("  -d, --debug               print debug messages (twice for more verbosity)\n");
1087    printf("  -D, --delay               delay between forks\n");
1088#if defined HAVE_REGEX_H
1089    printf("  -E, --exclude <regex>     do not fuzz files matching <regex>\n");
1090#endif
1091    printf("  -f, --fuzzing <mode>      use fuzzing mode <mode> ([xor] set unset)\n");
1092    printf("  -i, --stdin               fuzz standard input\n");
1093#if defined HAVE_REGEX_H
1094    printf("  -I, --include <regex>     only fuzz files matching <regex>\n");
1095#endif
1096    printf("  -j, --jobs <n>            number of simultaneous jobs (default 1)\n");
1097    printf("  -l, --list <list>         only fuzz Nth descriptor with N in <list>\n");
1098    printf("  -m, --md5                 compute the output's MD5 hash\n");
1099#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
1100    printf("  -M, --max-memory <n>      maximum child virtual memory in MiB (default %u)\n", DEFAULT_MEM);
1101#endif
1102    printf("  -n, --network             fuzz network input\n");
1103    printf("  -O, --opmode <mode>       use operating mode <mode> ([preload] copy)\n");
1104    printf("  -p, --ports <list>        only fuzz network destination ports in <list>\n");
1105    printf("  -P, --protect <list>      protect bytes and characters in <list>\n");
1106    printf("  -q, --quiet               do not print children's messages\n");
1107    printf("  -r, --ratio <ratio>       bit fuzzing ratio (default %g)\n", DEFAULT_RATIO);
1108    printf("          ... <start:stop>  specify a ratio range\n");
1109    printf("  -R, --refuse <list>       refuse bytes and characters in <list>\n");
1110    printf("  -s, --seed <seed>         random seed (default %i)\n", DEFAULT_SEED);
1111    printf("         ... <start:stop>   specify a seed range\n");
1112    printf("  -S, --signal              prevent children from diverting crashing signals\n");
1113    printf("  -t, --max-time <n>        stop spawning children after <n> seconds\n");
1114    printf("  -T, --max-cputime <n>     kill children that use more than <n> CPU seconds\n");
1115    printf("  -U, --max-usertime <n>    kill children that run for more than <n> seconds\n");
1116    printf("  -v, --verbose             print information during the run\n");
1117    printf("  -x, --check-exit          report processes that exit with a non-zero status\n");
1118    printf("  -h, --help                display this help and exit\n");
1119    printf("  -V, --version             output version information and exit\n");
1120    printf("\n");
1121    printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n");
1122}
1123
Note: See TracBrowser for help on using the repository browser.