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

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

on win32, use a named pipe and IOCP to read stdout, stderr and debugfd correctly.

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