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

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

Reorganise source code to better separate zzuf and libzzuf. Note: the Win32
build is now broken.

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