* 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
zzuf \- multiple purpose fuzzer
.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
[\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
[\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
[\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
\fBzzuf \-h\fR | \fB\-\-help\fR
.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
times and you want to test a different seed each time.
.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
Automatically terminate child processes that output more than \fIn\fR bytes
on the standard output and standard error channels. This is useful to detect
@ -149,12 +160,12 @@ backslash (\(oq\\\(cq)
.RE
.IP
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
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
(\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,
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
Fuzz 1% of the input bits of the \fBcat\fR program using seed 94324:
.PP
\fB zzuf \-s 94324 \-r 0.01 cat /etc/motd\fR
\fB zzuf \-s94324 \-r0.01 cat /etc/motd\fR
.PP
Fuzz the input of the \fBcat\fR program but do not fuzz newline characters
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
\fBzzuf\fR:
.PP
\fB zzuf \-c \-s 87423 \-r 0.01 vlc movie.avi\fR
\fB zzuf \-c \-s87423 \-r0.01 vlc movie.avi\fR
.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
\fB vlc fuzzy\-movie.avi\fR
.PP
Fuzz between 0.1% and 2% of MPlayer's input bits (\fB\-r\ 0.001:0.02\fR)
with seeds 0 to 9999 (\fB\-s\ 0:10000\fR), disabling its standard output
messages (\fB\-q\fR), launching up to five simultaneous child processes
(\fB\-F\ 5\fR) but wait at least half a second between launches
(\fB\-D\ 0.5\fR), killing MPlayer if it takes more than one minute to
read the file (\fB\-T\ 60\fR) and disabling its \fBSIGSEGV\fR signal handler
Fuzz between 0.1% and 2% of MPlayer's input bits (\fB\-r0.001:0.02\fR)
with seeds 0 to 9999 (\fB\-s0:10000\fR), preserving the AVI 4-byte header
by restricting fuzzing to offsets after 4 (\fB\-b4\-\fR), disabling its
standard output messages (\fB\-q\fR), launching up to five simultaneous child
processes (\fB\-F5\fR) but waiting at least half a second between launches
(\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):
.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
\fB mplayer \-\- \-benchmark \-vo null \-fps 1000 movie.avi\fR
.PP
@ -286,7 +298,7 @@ and open it in Firefox\(tm in auto-increment mode (\fB\-A\fR):
.br
(or: \fBjot -w \(aq<img src="hello.jpg#%d">\(aq 200 1 > hello.html\fR)
.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
.PP
Due to \fBzzuf\fR using shared object preloading (\fBLD_PRELOAD\fR,

View File

@ -24,6 +24,7 @@
# include <inttypes.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libzzuf.h"
@ -35,13 +36,52 @@
#define MAGIC1 0x33ea84f7
#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 refuse[256];
/* Local prototypes */
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)
{
readchars(protect, list);
@ -65,8 +105,8 @@ void _zz_fuzz(int fd, volatile uint8_t *buf, uint64_t len)
(unsigned long int)pos);
#endif
fuzz = _zz_getfuzz(fd);
aligned_buf = buf - pos;
fuzz = _zz_getfuzz(fd);
for(i = pos / 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++)
{
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])
continue;

View File

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

View File

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

View File

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

View File

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

View File

@ -125,9 +125,9 @@ int main(int argc, char *argv[])
for(;;)
{
# 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
# 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
# if defined HAVE_GETOPT_LONG
# 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 */
{ "autoinc", 0, NULL, 'A' },
{ "bytes", 1, NULL, 'b' },
{ "max-bytes", 1, NULL, 'B' },
#if defined HAVE_REGEX_H
{ "cmdline", 0, NULL, 'c' },
@ -180,6 +181,9 @@ int main(int argc, char *argv[])
case 'A': /* --autoinc */
setenv("ZZUF_AUTOINC", "1", 1);
break;
case 'b': /* --bytes */
opts->bytes = optarg;
break;
case 'B': /* --max-bytes */
opts->maxbytes = atoi(optarg);
break;
@ -296,6 +300,11 @@ int main(int argc, char *argv[])
/* If asked to read from the standard input */
if(optind >= argc)
{
if(opts->bytes)
_zz_bytes(opts->bytes);
/* FIXME: protect and refuse are ignored */
if(opts->endseed != opts->seed + 1)
{
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);
#endif
if(opts->bytes)
setenv("ZZUF_BYTES", opts->bytes, 1);
if(opts->protect)
setenv("ZZUF_PROTECT", opts->protect, 1);
if(opts->refuse)
@ -1020,14 +1031,15 @@ static void usage(void)
{
#if defined HAVE_REGEX_H
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
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");
printf(" [-T seconds] [-M bytes] [-P protect] [-R refuse]\n");
printf(" [PROGRAM [--] [ARGS]...]\n");
#endif
printf(" [-D delay] [-F forks] [-C crashes] [-B bytes] [-T seconds]\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
# if defined HAVE_GETOPT_LONG
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");
# if defined HAVE_GETOPT_LONG
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");
#if defined HAVE_REGEX_H
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");
# else
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");
#if defined HAVE_REGEX_H
printf(" -c only fuzz files specified in the command line\n");