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

Last change on this file since 4835 was 4835, checked in by Sam Hocevar, 8 years ago

cosmetic: get rid of CRLF line endings.

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