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

Last change on this file since 4833 was 4833, checked in by wisk, 8 years ago

port zzuf to win64 (amd64)

  • Property svn:keywords set to Id
File size: 39.3 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
795    int64_t now = _zz_time();
796#endif
797    int i, j;
798
799#if defined HAVE_KILL
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            kill(opts->child[i].pid, SIGTERM);
813            opts->child[i].date = now;
814            opts->child[i].status = STATUS_SIGTERM;
815        }
816
817        if(opts->child[i].status == STATUS_RUNNING
818            && opts->maxusertime >= 0
819            && now > opts->child[i].date + opts->maxusertime)
820        {
821            if(opts->verbose)
822            {
823                finfo(stderr, opts, opts->child[i].seed);
824                fprintf(stderr, "running time exceeded, sending SIGTERM\n");
825            }
826            kill(opts->child[i].pid, SIGTERM);
827            opts->child[i].date = now;
828            opts->child[i].status = STATUS_SIGTERM;
829        }
830    }
831
832    /* Kill children if necessary (still there after 2 seconds) */
833    for(i = 0; i < opts->maxchild; i++)
834    {
835        if(opts->child[i].status == STATUS_SIGTERM
836            && now > opts->child[i].date + 2000000)
837        {
838            if(opts->verbose)
839            {
840                finfo(stderr, opts, opts->child[i].seed);
841                fprintf(stderr, "not responding, sending SIGKILL\n");
842            }
843            kill(opts->child[i].pid, SIGKILL);
844            opts->child[i].status = STATUS_SIGKILL;
845        }
846    }
847#endif
848
849    /* Collect dead children */
850    for(i = 0; i < opts->maxchild; i++)
851    {
852        uint8_t md5sum[16];
853#if defined HAVE_WAITPID
854        int status;
855        pid_t pid;
856#endif
857
858        if(opts->child[i].status != STATUS_SIGKILL
859            && opts->child[i].status != STATUS_SIGTERM
860            && opts->child[i].status != STATUS_EOF)
861            continue;
862
863#if defined HAVE_WAITPID
864        pid = waitpid(opts->child[i].pid, &status, WNOHANG);
865        if(pid <= 0)
866            continue;
867
868        if(opts->checkexit && WIFEXITED(status) && WEXITSTATUS(status))
869        {
870            finfo(stderr, opts, opts->child[i].seed);
871            fprintf(stderr, "exit %i\n", WEXITSTATUS(status));
872            opts->crashes++;
873        }
874        else if(WIFSIGNALED(status)
875                 && !(WTERMSIG(status) == SIGTERM
876                       && opts->child[i].status == STATUS_SIGTERM))
877        {
878            char const *message = "";
879
880            if(WTERMSIG(status) == SIGKILL && opts->maxmem >= 0)
881                message = " (memory exceeded?)";
882#   if defined SIGXCPU
883            else if(WTERMSIG(status) == SIGXCPU && opts->maxcpu >= 0)
884                message = " (CPU time exceeded?)";
885#   endif
886            else if(WTERMSIG(status) == SIGKILL && opts->maxcpu >= 0)
887                message = " (CPU time exceeded?)";
888
889            finfo(stderr, opts, opts->child[i].seed);
890            fprintf(stderr, "signal %i%s%s\n",
891                    WTERMSIG(status), sig2name(WTERMSIG(status)), message);
892            opts->crashes++;
893        }
894        else if (opts->verbose)
895        {
896            finfo(stderr, opts, opts->child[i].seed);
897            if (WIFSIGNALED(status))
898                fprintf(stderr, "signal %i%s\n",
899                        WTERMSIG(status), sig2name(WTERMSIG(status)));
900            else
901                fprintf(stderr, "exit %i\n", WEXITSTATUS(status));
902        }
903#elif defined _WIN32
904        {
905            DWORD exit_code;
906            if (GetExitCodeProcess(opts->child[i].process_handle, &exit_code))
907            {
908                if (exit_code == STILL_ACTIVE) continue; /* The process is still active, we don't do anything */
909
910                /*
911                 * The main problem with GetExitCodeProcess is it returns either returned parameter value of
912                 * ExitProcess/TerminateProcess, or the unhandled exception (which is what we're looking for)
913                 */
914                switch (exit_code)
915                {
916                case EXCEPTION_ACCESS_VIOLATION: fprintf(stderr, "child(%d) unhandled exception: Access Violation\n", opts->child[i].pid); break;
917                default: fprintf(stderr, "child(%d) exited with code %#08x\n", opts->child[i].pid, exit_code); break;
918                }
919            }
920
921            if (opts->child[i].status != STATUS_RUNNING)
922            {
923                TerminateProcess(opts->child[i].process_handle, 0);
924            }
925        }
926#else
927        /* waitpid() is not available. Don't kill the process. */
928        continue;
929#endif
930
931        for(j = 0; j < 3; j++)
932            if(opts->child[i].fd[j] >= 0)
933                close(opts->child[i].fd[j]);
934
935        if (opts->opmode == OPMODE_COPY)
936        {
937            for (j = caca_optind + 1; j < opts->oldargc; j++)
938            {
939                if (opts->child[i].newargv[j - caca_optind] != opts->oldargv[j])
940                {
941                    unlink(opts->child[i].newargv[j - caca_optind]);
942                    free(opts->child[i].newargv[j - caca_optind]);
943                    opts->child[i].newargv[j - caca_optind] = opts->oldargv[j];
944                }
945            }
946        }
947
948        if(opts->md5)
949        {
950            _zz_md5_fini(md5sum, opts->child[i].ctx);
951            finfo(stdout, opts, opts->child[i].seed);
952            fprintf(stdout, "%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x%.02x"
953                    "%.02x%.02x%.02x%.02x%.02x%.02x%.02x\n", md5sum[0],
954                    md5sum[1], md5sum[2], md5sum[3], md5sum[4], md5sum[5],
955                    md5sum[6], md5sum[7], md5sum[8], md5sum[9], md5sum[10],
956                    md5sum[11], md5sum[12], md5sum[13], md5sum[14], md5sum[15]);
957            fflush(stdout);
958        }
959        opts->child[i].status = STATUS_FREE;
960        opts->nchild--;
961    }
962}
963
964#ifdef _WIN32
965
966/* This structure contains useful information about data sent from fuzzed applications */
967struct child_overlapped
968{
969    OVERLAPPED overlapped;
970    char buf[BUFSIZ];
971    struct opts * opts;
972    int child_no;
973    int fd_no;
974};
975
976/* This callback is called when fuzzed applications write in fd out, err or debug */
977static void _stdcall read_child(DWORD err_code, DWORD nbr_of_bytes_transfered, LPOVERLAPPED overlapped)
978{
979    struct child_overlapped * co = (struct child_overlapped *)overlapped;
980
981    /* TODO: handle more cases like ERROR_MORE_DATA */
982    if (err_code != ERROR_SUCCESS) return;
983
984    EnterCriticalSection(&_zz_pipe_cs);
985    switch (co->fd_no)
986    {
987    case 0: /* debug fd */
988        write(1, "dbg: ", 4);
989    case 1: /* out */
990        write(1, co->buf, nbr_of_bytes_transfered); break;
991    case 2: /* err */
992        write(2, co->buf, nbr_of_bytes_transfered); break;
993    default: break;
994    }
995    LeaveCriticalSection(&_zz_pipe_cs);
996
997    if(co->fd_no != 0) /* either out or err fd */
998        co->opts->child[co->child_no].bytes += nbr_of_bytes_transfered;
999
1000    if(co->opts->md5 && co->fd_no == 2)
1001        _zz_md5_add(co->opts->child[co->child_no].ctx, co->buf, nbr_of_bytes_transfered);
1002
1003    free(co); /* clean up allocated data */
1004}
1005
1006/* Since on windows select doesn't support file HANDLE, we use IOCP */
1007static void read_children(struct opts *opts)
1008{
1009    size_t i, j;
1010    HANDLE *children_handle, * cur_child_handle;
1011    size_t fd_number = opts->maxchild * 3;
1012
1013    cur_child_handle = children_handle = malloc(sizeof(*children_handle) * fd_number);
1014
1015    for(i = 0; i < fd_number; i++)
1016        children_handle[i] = INVALID_HANDLE_VALUE;
1017
1018    /* XXX: cute (i, j) iterating hack */
1019    for(i = 0, j = 0; i < (size_t)opts->maxchild; i += (j == 2), j = (j + 1) % 3)
1020    {
1021        struct child_overlapped * co;
1022        HANDLE h = (opts->child[i].fd[j] == -1) ? INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle(opts->child[i].fd[j]);
1023
1024        if(opts->child[i].status != STATUS_RUNNING
1025        || opts->child[i].fd[j] == -1
1026        || h == INVALID_HANDLE_VALUE)
1027        {
1028            fd_number--;
1029            continue;
1030        }
1031
1032        co = malloc(sizeof(*co));
1033        ZeroMemory(co, sizeof(*co));
1034        *cur_child_handle = co->overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1035        co->child_no = i;
1036        co->fd_no    = j;
1037        co->opts     = opts;
1038
1039        if(!ReadFileEx(h, co->buf, sizeof(co->buf), (LPOVERLAPPED)co, read_child))
1040        {
1041            /* End of file reached */
1042            close(opts->child[i].fd[j]);
1043            opts->child[i].fd[j] = -1;
1044
1045            if(opts->child[i].fd[0] == -1
1046                && opts->child[i].fd[1] == -1
1047                && opts->child[i].fd[2] == -1)
1048                opts->child[i].status = STATUS_EOF;
1049        }
1050        cur_child_handle++;
1051    }
1052
1053    if (fd_number == 0) return;
1054
1055    /* FIXME: handle error */
1056    WaitForMultipleObjectsEx(fd_number, children_handle, FALSE, 1000, TRUE);
1057}
1058
1059#else
1060static void read_children(struct opts *opts)
1061{
1062    struct timeval tv;
1063    fd_set fdset;
1064    int i, j, ret, maxfd = 0;
1065
1066    /* Read data from all sockets */
1067    FD_ZERO(&fdset);
1068    for(i = 0; i < opts->maxchild; i++)
1069    {
1070        if(opts->child[i].status != STATUS_RUNNING)
1071            continue;
1072
1073        for(j = 0; j < 3; j++)
1074            ZZUF_FD_SET(opts->child[i].fd[j], &fdset, maxfd);
1075    }
1076    tv.tv_sec = 0;
1077    tv.tv_usec = 1000;
1078
1079    errno = 0;
1080    ret = select(maxfd + 1, &fdset, NULL, NULL, &tv);
1081    if(ret < 0 && errno)
1082        perror("select");
1083    if(ret <= 0)
1084        return;
1085
1086    /* XXX: cute (i, j) iterating hack */
1087    for(i = 0, j = 0; i < opts->maxchild; i += (j == 2), j = (j + 1) % 3)
1088    {
1089        uint8_t buf[BUFSIZ];
1090
1091        if(opts->child[i].status != STATUS_RUNNING)
1092            continue;
1093
1094        if(!ZZUF_FD_ISSET(opts->child[i].fd[j], &fdset))
1095            continue;
1096
1097        ret = read(opts->child[i].fd[j], buf, BUFSIZ - 1);
1098        if(ret > 0)
1099        {
1100            /* We got data */
1101            if(j != 0)
1102                opts->child[i].bytes += ret;
1103
1104            if(opts->md5 && j == 2)
1105                _zz_md5_add(opts->child[i].ctx, buf, ret);
1106            else if(!opts->quiet || j == 0)
1107                write((j < 2) ? STDERR_FILENO : STDOUT_FILENO, buf, ret);
1108        }
1109        else if(ret == 0)
1110        {
1111            /* End of file reached */
1112            close(opts->child[i].fd[j]);
1113            opts->child[i].fd[j] = -1;
1114
1115            if(opts->child[i].fd[0] == -1
1116                && opts->child[i].fd[1] == -1
1117                && opts->child[i].fd[2] == -1)
1118                opts->child[i].status = STATUS_EOF;
1119        }
1120    }
1121}
1122#endif
1123
1124#if !defined HAVE_SETENV
1125static void setenv(char const *name, char const *value, int overwrite)
1126{
1127    char *str;
1128
1129    if(!overwrite && getenv(name))
1130        return;
1131
1132    str = malloc(strlen(name) + 1 + strlen(value) + 1);
1133    sprintf(str, "%s=%s", name, value);
1134    putenv(str);
1135}
1136#endif
1137
1138#if defined HAVE_WAITPID
1139static char const *sig2name(int signum)
1140{
1141    switch(signum)
1142    {
1143#ifdef SIGQUIT
1144        case SIGQUIT:  return " (SIGQUIT)"; /* 3 */
1145#endif
1146        case SIGILL:   return " (SIGILL)";  /* 4 */
1147#ifdef SIGTRAP
1148        case SIGTRAP:  return " (SIGTRAP)"; /* 5 */
1149#endif
1150        case SIGABRT:  return " (SIGABRT)"; /* 6 */
1151#ifdef SIGBUS
1152        case SIGBUS:   return " (SIGBUS)";  /* 7 */
1153#endif
1154        case SIGFPE:   return " (SIGFPE)";  /* 8 */
1155        case SIGSEGV:  return " (SIGSEGV)"; /* 11 */
1156        case SIGPIPE:  return " (SIGPIPE)"; /* 13 */
1157#ifdef SIGEMT
1158        case SIGEMT:   return " (SIGEMT)";  /* ? */
1159#endif
1160#ifdef SIGXCPU
1161        case SIGXCPU:  return " (SIGXCPU)"; /* 24 */
1162#endif
1163#ifdef SIGXFSZ
1164        case SIGXFSZ:  return " (SIGXFSZ)"; /* 25 */
1165#endif
1166#ifdef SIGSYS
1167        case SIGSYS:   return " (SIGSYS)";  /* 31 */
1168#endif
1169    }
1170
1171    return "";
1172}
1173#endif
1174
1175static void version(void)
1176{
1177    printf("zzuf %s\n", PACKAGE_VERSION);
1178    printf("Copyright (C) 2002-2010 Sam Hocevar <sam@hocevar.net>\n");
1179    printf("This program is free software. It comes without any warranty, to the extent\n");
1180    printf("permitted by applicable law. You can redistribute it and/or modify it under\n");
1181    printf("the terms of the Do What The Fuck You Want To Public License, Version 2, as\n");
1182    printf("published by Sam Hocevar. See <http://sam.zoy.org/wtfpl/> for more details.\n");
1183    printf("\n");
1184    printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n");
1185}
1186
1187static void usage(void)
1188{
1189#if defined HAVE_REGEX_H
1190    printf("Usage: zzuf [-aAcdimnqSvx] [-s seed|-s start:stop] [-r ratio|-r min:max]\n");
1191#else
1192    printf("Usage: zzuf [-aAdimnqSvx] [-s seed|-s start:stop] [-r ratio|-r min:max]\n");
1193#endif
1194    printf("              [-f mode] [-D delay] [-j jobs] [-C crashes] [-B bytes] [-a list]\n");
1195    printf("              [-t seconds]");
1196#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_CPU
1197    printf(                          " [-T seconds]");
1198#endif
1199#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
1200    printf(                                       " [-M mebibytes]");
1201#endif
1202    printf(                                                      " [-b ranges] [-p ports]\n");
1203    printf("              [-P protect] [-R refuse] [-l list]");
1204#if defined HAVE_REGEX_H
1205    printf(                                                " [-I include] [-E exclude]");
1206#endif
1207    printf("              [-O mode]\n");
1208    printf("\n");
1209    printf("              [PROGRAM [--] [ARGS]...]\n");
1210    printf("       zzuf -h | --help\n");
1211    printf("       zzuf -V | --version\n");
1212    printf("Run PROGRAM with optional arguments ARGS and fuzz its input.\n");
1213    printf("\n");
1214    printf("Mandatory arguments to long options are mandatory for short options too.\n");
1215    printf("  -a, --allow <list>        only fuzz network input for IPs in <list>\n");
1216    printf("          ... !<list>       do not fuzz network input for IPs in <list>\n");
1217    printf("  -A, --autoinc             increment seed each time a new file is opened\n");
1218    printf("  -b, --bytes <ranges>      only fuzz bytes at offsets within <ranges>\n");
1219    printf("  -B, --max-bytes <n>       kill children that output more than <n> bytes\n");
1220#if defined HAVE_REGEX_H
1221    printf("  -c, --cmdline             only fuzz files specified in the command line\n");
1222#endif
1223    printf("  -C, --max-crashes <n>     stop after <n> children have crashed (default 1)\n");
1224    printf("  -d, --debug               print debug messages (twice for more verbosity)\n");
1225    printf("  -D, --delay               delay between forks\n");
1226#if defined HAVE_REGEX_H
1227    printf("  -E, --exclude <regex>     do not fuzz files matching <regex>\n");
1228#endif
1229    printf("  -f, --fuzzing <mode>      use fuzzing mode <mode> ([xor] set unset)\n");
1230    printf("  -i, --stdin               fuzz standard input\n");
1231#if defined HAVE_REGEX_H
1232    printf("  -I, --include <regex>     only fuzz files matching <regex>\n");
1233#endif
1234    printf("  -j, --jobs <n>            number of simultaneous jobs (default 1)\n");
1235    printf("  -l, --list <list>         only fuzz Nth descriptor with N in <list>\n");
1236    printf("  -m, --md5                 compute the output's MD5 hash\n");
1237#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
1238    printf("  -M, --max-memory <n>      maximum child virtual memory in MiB (default %u)\n", DEFAULT_MEM);
1239#endif
1240    printf("  -n, --network             fuzz network input\n");
1241    printf("  -O, --opmode <mode>       use operating mode <mode> ([preload] copy)\n");
1242    printf("  -p, --ports <list>        only fuzz network destination ports in <list>\n");
1243    printf("  -P, --protect <list>      protect bytes and characters in <list>\n");
1244    printf("  -q, --quiet               do not print children's messages\n");
1245    printf("  -r, --ratio <ratio>       bit fuzzing ratio (default %g)\n", DEFAULT_RATIO);
1246    printf("          ... <start:stop>  specify a ratio range\n");
1247    printf("  -R, --refuse <list>       refuse bytes and characters in <list>\n");
1248    printf("  -s, --seed <seed>         random seed (default %i)\n", DEFAULT_SEED);
1249    printf("         ... <start:stop>   specify a seed range\n");
1250    printf("  -S, --signal              prevent children from diverting crashing signals\n");
1251    printf("  -t, --max-time <n>        stop spawning children after <n> seconds\n");
1252    printf("  -T, --max-cputime <n>     kill children that use more than <n> CPU seconds\n");
1253    printf("  -U, --max-usertime <n>    kill children that run for more than <n> seconds\n");
1254    printf("  -v, --verbose             print information during the run\n");
1255    printf("  -x, --check-exit          report processes that exit with a non-zero status\n");
1256    printf("  -h, --help                display this help and exit\n");
1257    printf("  -V, --version             output version information and exit\n");
1258    printf("\n");
1259    printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n");
1260}
1261
Note: See TracBrowser for help on using the repository browser.