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

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

win32 port starts to fuzz executable (only few functions related to file handling are implemented)

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