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

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

Append "/" to our LT_OBJDIR override.

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