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

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

zzuf.c: use LT_OBJDIR instead of hardcoding ".libs/".

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