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

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

Allow remote network host filtering, courtesy of Corentin Delorme.

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