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

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

Fix a regression in the rlimit handling code caused by the myfork split.

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