* Implemented -b/--bytes to restrict fuzzing to specific offsets.

This commit is contained in:
Sam Hocevar 2007-01-25 11:40:27 +00:00 committed by sam
parent 8830524877
commit 68446780ce
7 changed files with 114 additions and 30 deletions

View File

@ -2,13 +2,13 @@
.SH NAME .SH NAME
zzuf \- multiple purpose fuzzer zzuf \- multiple purpose fuzzer
.SH SYNOPSIS .SH SYNOPSIS
\fBzzuf\fR [\fB\-AcdiMnqSvx\fR] [\fB\-s\fR \fIseed\fR|\fB\-s\fR \fIstart:stop\fR] [\fB\-r\fR \fIratio\fR|\fB\-r\fR \fImin:max\fR] \fBzzuf\fR [\fB\-AcdimnqSvx\fR] [\fB\-s\fR \fIseed\fR|\fB\-s\fR \fIstart:stop\fR] [\fB\-r\fR \fIratio\fR|\fB\-r\fR \fImin:max\fR]
.br .br
[\fB\-D\fR \fIdelay\fR] [\fB\-F\fR \fIforks\fR] [\fB\-C\fR \fIcrashes\fR] [\fB\-B\fR \fIbytes\fR] [\fB\-D\fR \fIdelay\fR] [\fB\-F\fR \fIforks\fR] [\fB\-C\fR \fIcrashes\fR] [\fB\-B\fR \fIbytes\fR] [\fB\-T\fR \fIseconds\fR]
.br .br
[\fB\-T\fR \fIseconds\fR] [\fB\-M\fR \fImegabytes\fR] [\fB\-P\fR \fIprotect\fR] [\fB\-R\fR \fIrefuse\fR] [\fB\-M\fR \fImegabytes\fR] [\fB\-b\fR \fIranges\fR] [\fB\-P\fR \fIprotect\fR] [\fB\-R\fR \fIrefuse\fR]
.br .br
[\fB\-I\fR \fIinclude\fR] [\fB\-E\fR \fIexclude\fR] [\fIPROGRAM\fR [\fB\-\-\fR] [\fIARGS\fR]...] [\fB\-I\fR \fIinclude\fR] [\fB\-E\fR \fIexclude\fR] [\fIPROGRAM\fR [\fB\-\-\fR] [\fIARGS\fR]...]
.br .br
\fBzzuf \-h\fR | \fB\-\-help\fR \fBzzuf \-h\fR | \fB\-\-help\fR
.br .br
@ -43,6 +43,17 @@ Increment random seed each time a new file is opened. This is only required
if one instance of the application is expected to open the same file several if one instance of the application is expected to open the same file several
times and you want to test a different seed each time. times and you want to test a different seed each time.
.TP .TP
\fB\-b\fR, \fB\-\-bytes\fR=\fIranges\fR
Restrict fuzzing to bytes whose offsets in the file are within \fIranges\fR.
Range values start at zero and are inclusive. Use dashes between range values
and commas between ranges. If the right-hand part of a range is ommited, it
means end of file. For instance, to restrict fuzzing to bytes 0, 3, 4, 5 and
all bytes after offset 31, use \(oq\fB\-r0,3-5,31-\fR\(cq.
This option is useful to preserve file headers or corrupt only a specific
portion of a file.
.TP
\fB\-B\fR, \fB\-\-max\-bytes\fR=\fIn\fR \fB\-B\fR, \fB\-\-max\-bytes\fR=\fIn\fR
Automatically terminate child processes that output more than \fIn\fR bytes Automatically terminate child processes that output more than \fIn\fR bytes
on the standard output and standard error channels. This is useful to detect on the standard output and standard error channels. This is useful to detect
@ -149,12 +160,12 @@ backslash (\(oq\\\(cq)
.RE .RE
.IP .IP
You can use \(oq\fB\-\fR\(cq to specify ranges. For instance, to protect all You can use \(oq\fB\-\fR\(cq to specify ranges. For instance, to protect all
bytes from \(oq\\001\(cq to \(oq/\(cq, use \(oq\fB\-P\ \(dq\\001\-/\(dq\fR\(cq. bytes from \(oq\\001\(cq to \(oq/\(cq, use \(oq\fB\-P\ \(aq\\001\-/\(aq\fR\(cq.
The statistical outcome of this option should not be overlooked: if characters The statistical outcome of this option should not be overlooked: if characters
are protected, the effect of the \(oq\fB\-r\fR\(cq flag will vary depending are protected, the effect of the \(oq\fB\-r\fR\(cq flag will vary depending
on the data being fuzzed. For instance, asking to fuzz 1% of input bits on the data being fuzzed. For instance, asking to fuzz 1% of input bits
(\fB\-r\ 0.01\fR) and to protect lowercase characters (\fB\-P\ a\-z\fR) will (\fB\-r0.01\fR) and to protect lowercase characters (\fB\-P\ a\-z\fR) will
result in an actual average fuzzing ratio of 0.9% with truly random data, result in an actual average fuzzing ratio of 0.9% with truly random data,
0.3% with random ASCII data and 0.2% with standard English text. 0.3% with random ASCII data and 0.2% with standard English text.
@ -241,7 +252,7 @@ Fuzz the input of the \fBcat\fR program using default settings:
.PP .PP
Fuzz 1% of the input bits of the \fBcat\fR program using seed 94324: Fuzz 1% of the input bits of the \fBcat\fR program using seed 94324:
.PP .PP
\fB zzuf \-s 94324 \-r 0.01 cat /etc/motd\fR \fB zzuf \-s94324 \-r0.01 cat /etc/motd\fR
.PP .PP
Fuzz the input of the \fBcat\fR program but do not fuzz newline characters Fuzz the input of the \fBcat\fR program but do not fuzz newline characters
and prevent non-ASCII characters from appearing in the output: and prevent non-ASCII characters from appearing in the output:
@ -261,21 +272,22 @@ and restricting fuzzing to filenames that appear on the command line
can be read by VLC to reproduce the same behaviour without using can be read by VLC to reproduce the same behaviour without using
\fBzzuf\fR: \fBzzuf\fR:
.PP .PP
\fB zzuf \-c \-s 87423 \-r 0.01 vlc movie.avi\fR \fB zzuf \-c \-s87423 \-r0.01 vlc movie.avi\fR
.br .br
\fB zzuf \-c \-s 87423 \-r 0.01 <movie.avi >fuzzy\-movie.avi\fR \fB zzuf \-c \-s87423 \-r0.01 <movie.avi >fuzzy\-movie.avi\fR
.br .br
\fB vlc fuzzy\-movie.avi\fR \fB vlc fuzzy\-movie.avi\fR
.PP .PP
Fuzz between 0.1% and 2% of MPlayer's input bits (\fB\-r\ 0.001:0.02\fR) Fuzz between 0.1% and 2% of MPlayer's input bits (\fB\-r0.001:0.02\fR)
with seeds 0 to 9999 (\fB\-s\ 0:10000\fR), disabling its standard output with seeds 0 to 9999 (\fB\-s0:10000\fR), preserving the AVI 4-byte header
messages (\fB\-q\fR), launching up to five simultaneous child processes by restricting fuzzing to offsets after 4 (\fB\-b4\-\fR), disabling its
(\fB\-F\ 5\fR) but wait at least half a second between launches standard output messages (\fB\-q\fR), launching up to five simultaneous child
(\fB\-D\ 0.5\fR), killing MPlayer if it takes more than one minute to processes (\fB\-F5\fR) but waiting at least half a second between launches
read the file (\fB\-T\ 60\fR) and disabling its \fBSIGSEGV\fR signal handler (\fB\-D0.5\fR), killing MPlayer if it takes more than one minute to
read the file (\fB\-T60\fR) and disabling its \fBSIGSEGV\fR signal handler
(\fB\-S\fR): (\fB\-S\fR):
.PP .PP
\fB zzuf \-c \-r 0.001:0.02 \-q \-s 0:10000 \-F 5 \-D 0.5 \-T 60 \-S \\\fR \fB zzuf \-c \-r0.001:0.02 \-s0:10000 \-b4\- \-q \-F5 \-D0.5 \-T60 \-S \\\fR
.br .br
\fB mplayer \-\- \-benchmark \-vo null \-fps 1000 movie.avi\fR \fB mplayer \-\- \-benchmark \-vo null \-fps 1000 movie.avi\fR
.PP .PP
@ -286,7 +298,7 @@ and open it in Firefox\(tm in auto-increment mode (\fB\-A\fR):
.br .br
(or: \fBjot -w \(aq<img src="hello.jpg#%d">\(aq 200 1 > hello.html\fR) (or: \fBjot -w \(aq<img src="hello.jpg#%d">\(aq 200 1 > hello.html\fR)
.br .br
\fB zzuf -A -I \(aqhello[.]jpg\(aq -r 0.001 firefox hello.html\fR \fB zzuf -A -I \(aqhello[.]jpg\(aq -r0.001 firefox hello.html\fR
.SH RESTRICTIONS .SH RESTRICTIONS
.PP .PP
Due to \fBzzuf\fR using shared object preloading (\fBLD_PRELOAD\fR, Due to \fBzzuf\fR using shared object preloading (\fBLD_PRELOAD\fR,

View File

@ -24,6 +24,7 @@
# include <inttypes.h> # include <inttypes.h>
#endif #endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "libzzuf.h" #include "libzzuf.h"
@ -35,13 +36,52 @@
#define MAGIC1 0x33ea84f7 #define MAGIC1 0x33ea84f7
#define MAGIC2 0x783bc31f #define MAGIC2 0x783bc31f
/* Fuzzing variables */ /* Per-offset byte protection */
static unsigned int *ranges = NULL;
static unsigned int ranges_static[512];
/* Per-value byte protection */
static int protect[256]; static int protect[256];
static int refuse[256]; static int refuse[256];
/* Local prototypes */ /* Local prototypes */
static void readchars(int *, char const *); static void readchars(int *, char const *);
void _zz_bytes(char const *list)
{
char const *parser;
unsigned int i, chunks;
/* Count commas */
for(parser = list, chunks = 1; *parser; parser++)
if(*parser == ',')
chunks++;
/* TODO: free(ranges) if ranges != ranges_static */
if(chunks >= 256)
ranges = malloc((chunks + 1) * 2 * sizeof(unsigned int));
else
ranges = ranges_static;
/* Fill ranges list */
for(parser = list, i = 0; i < chunks; i++)
{
char const *comma = strchr(parser, ',');
char const *dash = strchr(parser, '-');
ranges[i * 2] = (dash == parser) ? 0 : atoi(parser);
if(dash && (dash + 1 == comma || dash[1] == '\0'))
ranges[i * 2 + 1] = ranges[i * 2]; /* special case */
else if(dash && (!comma || dash < comma))
ranges[i * 2 + 1] = atoi(dash + 1) + 1;
else
ranges[i * 2 + 1] = ranges[i * 2] + 1;
parser = comma + 1;
}
ranges[i * 2] = ranges[i * 2 + 1] = 0;
}
void _zz_protect(char const *list) void _zz_protect(char const *list)
{ {
readchars(protect, list); readchars(protect, list);
@ -65,8 +105,8 @@ void _zz_fuzz(int fd, volatile uint8_t *buf, uint64_t len)
(unsigned long int)pos); (unsigned long int)pos);
#endif #endif
fuzz = _zz_getfuzz(fd);
aligned_buf = buf - pos; aligned_buf = buf - pos;
fuzz = _zz_getfuzz(fd);
for(i = pos / CHUNKBYTES; for(i = pos / CHUNKBYTES;
i < (pos + len + CHUNKBYTES - 1) / CHUNKBYTES; i < (pos + len + CHUNKBYTES - 1) / CHUNKBYTES;
@ -102,7 +142,20 @@ void _zz_fuzz(int fd, volatile uint8_t *buf, uint64_t len)
for(j = start; j < stop; j++) for(j = start; j < stop; j++)
{ {
uint8_t byte = aligned_buf[j]; unsigned int *r;
uint8_t byte;
if(!ranges)
goto range_ok;
for(r = ranges; r[1]; r += 2)
if(j >= r[0] && (r[0] == r[1] || j < r[1]))
goto range_ok;
continue; /* Not in a range */
range_ok:
byte = aligned_buf[j];
if(protect[byte]) if(protect[byte])
continue; continue;

View File

@ -16,6 +16,7 @@
* fuzz.h: fuzz functions * fuzz.h: fuzz functions
*/ */
extern void _zz_bytes(char const *);
extern void _zz_protect(char const *); extern void _zz_protect(char const *);
extern void _zz_refuse(char const *); extern void _zz_refuse(char const *);

View File

@ -78,6 +78,10 @@ void _zz_init(void)
if(tmp && *tmp == '1') if(tmp && *tmp == '1')
_zz_setautoinc(); _zz_setautoinc();
tmp = getenv("ZZUF_BYTES");
if(tmp && *tmp)
_zz_bytes(tmp);
tmp = getenv("ZZUF_PROTECT"); tmp = getenv("ZZUF_PROTECT");
if(tmp && *tmp) if(tmp && *tmp)
_zz_protect(tmp); _zz_protect(tmp);

View File

@ -33,7 +33,7 @@
void _zz_opts_init(struct opts *opts) void _zz_opts_init(struct opts *opts)
{ {
opts->protect = opts->refuse = NULL; opts->bytes = opts->protect = opts->refuse = NULL;
opts->seed = DEFAULT_SEED; opts->seed = DEFAULT_SEED;
opts->endseed = DEFAULT_SEED + 1; opts->endseed = DEFAULT_SEED + 1;
opts->minratio = opts->maxratio = DEFAULT_RATIO; opts->minratio = opts->maxratio = DEFAULT_RATIO;

View File

@ -19,7 +19,7 @@
struct opts struct opts
{ {
char const **newargv; char const **newargv;
char *protect, *refuse; char *bytes, *protect, *refuse;
uint32_t seed; uint32_t seed;
uint32_t endseed; uint32_t endseed;
double minratio; double minratio;

View File

@ -125,9 +125,9 @@ int main(int argc, char *argv[])
for(;;) for(;;)
{ {
# if defined HAVE_REGEX_H # if defined HAVE_REGEX_H
# define OPTSTR "AB:cC:dD:E:F:iI:mM:nP:qr:R:s:ST:vxhV" # define OPTSTR "Ab:B:cC:dD:E:F:iI:mM:nP:qr:R:s:ST:vxhV"
# else # else
# define OPTSTR "AB:C:dD:F:imM:nP:qr:R:s:ST:vxhV" # define OPTSTR "Ab:B:C:dD:F:imM:nP:qr:R:s:ST:vxhV"
# endif # endif
# if defined HAVE_GETOPT_LONG # if defined HAVE_GETOPT_LONG
# define MOREINFO "Try `%s --help' for more information.\n" # define MOREINFO "Try `%s --help' for more information.\n"
@ -136,6 +136,7 @@ int main(int argc, char *argv[])
{ {
/* Long option, needs arg, flag, short option */ /* Long option, needs arg, flag, short option */
{ "autoinc", 0, NULL, 'A' }, { "autoinc", 0, NULL, 'A' },
{ "bytes", 1, NULL, 'b' },
{ "max-bytes", 1, NULL, 'B' }, { "max-bytes", 1, NULL, 'B' },
#if defined HAVE_REGEX_H #if defined HAVE_REGEX_H
{ "cmdline", 0, NULL, 'c' }, { "cmdline", 0, NULL, 'c' },
@ -180,6 +181,9 @@ int main(int argc, char *argv[])
case 'A': /* --autoinc */ case 'A': /* --autoinc */
setenv("ZZUF_AUTOINC", "1", 1); setenv("ZZUF_AUTOINC", "1", 1);
break; break;
case 'b': /* --bytes */
opts->bytes = optarg;
break;
case 'B': /* --max-bytes */ case 'B': /* --max-bytes */
opts->maxbytes = atoi(optarg); opts->maxbytes = atoi(optarg);
break; break;
@ -296,6 +300,11 @@ int main(int argc, char *argv[])
/* If asked to read from the standard input */ /* If asked to read from the standard input */
if(optind >= argc) if(optind >= argc)
{ {
if(opts->bytes)
_zz_bytes(opts->bytes);
/* FIXME: protect and refuse are ignored */
if(opts->endseed != opts->seed + 1) if(opts->endseed != opts->seed + 1)
{ {
printf("%s: seed ranges are incompatible with stdin fuzzing\n", printf("%s: seed ranges are incompatible with stdin fuzzing\n",
@ -334,6 +343,8 @@ int main(int argc, char *argv[])
setenv("ZZUF_EXCLUDE", exclude, 1); setenv("ZZUF_EXCLUDE", exclude, 1);
#endif #endif
if(opts->bytes)
setenv("ZZUF_BYTES", opts->bytes, 1);
if(opts->protect) if(opts->protect)
setenv("ZZUF_PROTECT", opts->protect, 1); setenv("ZZUF_PROTECT", opts->protect, 1);
if(opts->refuse) if(opts->refuse)
@ -1020,14 +1031,15 @@ static void usage(void)
{ {
#if defined HAVE_REGEX_H #if defined HAVE_REGEX_H
printf("Usage: zzuf [-AcdimnqSvx] [-s seed|-s start:stop] [-r ratio|-r min:max]\n"); printf("Usage: zzuf [-AcdimnqSvx] [-s seed|-s start:stop] [-r ratio|-r min:max]\n");
printf(" [-D delay] [-F forks] [-C crashes] [-B bytes]\n");
printf(" [-T seconds] [-M bytes] [-P protect] [-R refuse]\n");
printf(" [-I include] [-E exclude] [PROGRAM [--] [ARGS]...]\n");
#else #else
printf("Usage: zzuf [-AdimnqSvx] [-s seed|-s start:stop] [-r ratio|-r min:max]\n"); printf("Usage: zzuf [-AdimnqSvx] [-s seed|-s start:stop] [-r ratio|-r min:max]\n");
printf(" [-D delay] [-F forks] [-C crashes] [-B bytes]\n"); #endif
printf(" [-T seconds] [-M bytes] [-P protect] [-R refuse]\n"); printf(" [-D delay] [-F forks] [-C crashes] [-B bytes] [-T seconds]\n");
printf(" [PROGRAM [--] [ARGS]...]\n"); printf(" [-M bytes] [-b ranges] [-P protect] [-R refuse]\n");
#if defined HAVE_REGEX_H
printf(" [-I include] [-E exclude] [PROGRAM [--] [ARGS]...]\n");
#else
printf(" [PROGRAM [--] [ARGS]...]\n");
#endif #endif
# if defined HAVE_GETOPT_LONG # if defined HAVE_GETOPT_LONG
printf(" zzuf -h | --help\n"); printf(" zzuf -h | --help\n");
@ -1041,6 +1053,7 @@ static void usage(void)
printf("Mandatory arguments to long options are mandatory for short options too.\n"); printf("Mandatory arguments to long options are mandatory for short options too.\n");
# if defined HAVE_GETOPT_LONG # if defined HAVE_GETOPT_LONG
printf(" -A, --autoinc increment seed each time a new file is opened\n"); printf(" -A, --autoinc increment seed each time a new file is opened\n");
printf(" -b, --bytes <ranges> only fuzz bytes at offsets within <ranges>\n");
printf(" -B, --max-bytes <n> kill children that output more than <n> bytes\n"); printf(" -B, --max-bytes <n> kill children that output more than <n> bytes\n");
#if defined HAVE_REGEX_H #if defined HAVE_REGEX_H
printf(" -c, --cmdline only fuzz files specified in the command line\n"); printf(" -c, --cmdline only fuzz files specified in the command line\n");
@ -1076,6 +1089,7 @@ static void usage(void)
printf(" -V, --version output version information and exit\n"); printf(" -V, --version output version information and exit\n");
# else # else
printf(" -A increment seed each time a new file is opened\n"); printf(" -A increment seed each time a new file is opened\n");
printf(" -b <ranges> only fuzz bytes at offsets within <ranges>\n");
printf(" -B <n> kill children that output more than <n> bytes\n"); printf(" -B <n> kill children that output more than <n> bytes\n");
#if defined HAVE_REGEX_H #if defined HAVE_REGEX_H
printf(" -c only fuzz files specified in the command line\n"); printf(" -c only fuzz files specified in the command line\n");