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

Last change on this file since 4683 was 4683, checked in by Sam Hocevar, 11 years ago

Get rid of the getopt reimplementation and depend on libcaca instead.

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