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

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