* Implemented memory limits. Probably breaks on other arches because of all

the new functions.
This commit is contained in:
Sam Hocevar
2007-01-10 17:17:26 +00:00
committed by sam
parent 6e1f3612f8
commit 8258e9394a
5 changed files with 197 additions and 19 deletions
+23 -9
View File
@@ -2,11 +2,11 @@
.SH NAME
zzuf \- multiple purpose fuzzer
.SH SYNOPSIS
\fBzzuf\fR [\fB\-cdiMnqS\fR] [\fB\-r\fR \fIratio\fR] [\fB\-s\fR \fIseed\fR | \fB\-s\fR \fIstart:stop\fR]
\fBzzuf\fR [\fB\-cdiMnqS\fR] [\fB\-r\fR \fIratio\fR] [\fB\-s\fR \fIseed\fR|\fB\-s\fR \fIstart:stop\fR]
.br
[\fB\-F\fR \fIforks\fR] [\fB\-C\fR \fIcrashes\fR] [\fB\-B\fR \fIbytes\fR] [\fB\-T\fR \fIseconds\fR]
.br
[\fB\-P\fR \fIprotect\fR] [\fB\-R\fR \fIrefuse\fR]
[\fB\-M\fR \fImegabytes\fR] [\fB\-P\fR \fIprotect\fR] [\fB\-R\fR \fIrefuse\fR]
.br
[\fB\-I\fR \fIinclude\fR] [\fB\-E\fR \fIexclude\fR] [\fIPROGRAM\fR [\fIARGS\fR]...]
.br
@@ -99,6 +99,15 @@ of the regular expressions will be fuzzed. See also the \fB\-c\fR flag.
Instead of displaying the program's standard output, just print the MD5 digest
of that output. The standard error channel is left untouched.
.TP
\fB\-M\fR, \fB\-\-max-memory\fR=\fImegabytes\fR
Specify the maximum amount of memory, in megabytes, that children are allowed
to allocate. This is useful to detect infinite loops that eat up a lot of
memory. The value should set reasonably high so as not to interfer with normal
program operation.
\fBZzuf\fR uses the \fBsetrlimit\fR() call to set memory usage limitations and
relies on the operating system's ability to enforce such limitations.
.TP
\fB\-n\fR, \fB\-\-network\fR
Fuzz the application's network input. By default \fBzzuf\fR only fuzzes files.
.TP
@@ -227,7 +236,7 @@ can be read by \fBVLC\fR 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 \-s 87423 \-r 0.01 cp movie.avi fuzzy\-movie.avi\fR
\fB zzuf \-c \-s 87423 \-r 0.01 <movie.avi >fuzzy\-movie.avi\fR
\fB vlc fuzzy\-movie.avi\fR
.PP
Fuzz 2% of \fBMPlayer\fR's input bits (\fB\-r\ 0.02\fR) with seeds 0 to 9999
@@ -240,8 +249,8 @@ and disabling its \fBSIGSEGV\fR signal handler (\fB\-S\fR):
\fB mplayer \-\- \-benchmark \-vo null \-fps 1000 movie.avi\fR
.SH RESTRICTIONS
.PP
Due to \fBzzuf\fR using shared object preloading (\fBLD_PRELOAD\fR on most
Unix systems, \fBDYLD_INSERT_LIBRARIES\fR on Mac OS X) to run its child
Due to \fBzzuf\fR using shared object preloading (\fBLD_PRELOAD\fR,
\fB_RLD_LIST\fB, \fBDYLD_INSERT_LIBRARIES\fR, etc.) to run its child
processes, it will fail in the presence of any mechanism that disables
preloading. For instance setuid root binaries will not be fuzzed when run
as an unprivileged user.
@@ -266,19 +275,24 @@ As of now, \fBzzuf\fR does not really support multithreaded applications. The
behaviour with multithreaded applications where more than one thread does file
descriptor operations is undefined.
.SH NOTES
In order to intercept file and network operations and signal handlers,
\fBzzuf\fR diverts and reimplements the following functions, which can
be private libc symbols, too:
In order to intercept file and network operations, signal handlers and memory
allocations, \fBzzuf\fR diverts and reimplements the following functions,
which can be private libc symbols, too:
.TP
Unix file descriptor handling:
\fBopen\fR(), \fBlseek\fR(), \fBread\fR(), \fBaccept\fR(), \fBsocket\fR(),
\fBmmap\fR(), \fBmunmap\fR(), \fBclose\fR()
\fBclose\fR()
.TP
Standard IO streams:
\fBfopen\fR(), \fBfreopen\fR(), \fBfseek\fR(), \fBfseeko\fR(), \fBrewind\fR(),
\fBfread\fR(), \fBgetc\fR(), \fBfgetc\fR(), \fBfgets\fR(), \fBungetc\fR(),
\fBfclose\fR()
.TP
Memory management:
\fBmmap\fR(), \fBmunmap\fR(), \fBmalloc\fR(), \fBcalloc\fR(), \fBvalloc\fR(),
\fBfree\fR(), \fBmemalign\fR(), \fBposix_memalign\fR(), \fBbrk\fR(),
\fBsbrk\fR()
.TP
Linux-specific:
\fBopen64\fR(), \fBlseek64\fR(), \fBmmap64\fR(), \fB_IO_getc\fR(),
\fBgetline\fR(), \fBgetdelim\fR(), \fB__getdelim\fR()
+5
View File
@@ -44,6 +44,7 @@ int _zz_ready = 0;
int _zz_disabled = 0;
int _zz_hasdebug = 0;
int _zz_signal = 0;
int _zz_memory = 0;
int _zz_network = 0;
/* Library initialisation shit */
@@ -83,6 +84,10 @@ void _zz_init(void)
if(tmp && *tmp == '1')
_zz_signal = 1;
tmp = getenv("ZZUF_MEMORY");
if(tmp && *tmp == '1')
_zz_memory = 1;
tmp = getenv("ZZUF_NETWORK");
if(tmp && *tmp == '1')
_zz_network = 1;
+1
View File
@@ -39,6 +39,7 @@ extern int _zz_ready;
extern int _zz_disabled;
extern int _zz_hasdebug;
extern int _zz_signal;
extern int _zz_memory;
extern int _zz_network;
/* Library initialisation shit */
+143 -5
View File
@@ -22,6 +22,8 @@
#define _GNU_SOURCE
/* Use this to get mmap64() on glibc systems */
#define _LARGEFILE64_SOURCE
/* Use this to get posix_memalign */
#define _XOPEN_SOURCE 600
#if defined HAVE_STDINT_H
# include <stdint.h>
@@ -31,7 +33,10 @@
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <errno.h>
#include <signal.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/mman.h>
#if defined HAVE_LIBC_H
@@ -45,13 +50,27 @@
#include "fd.h"
/* Library functions that we divert */
static void * (*mmap_orig) (void *start, size_t length, int prot,
int flags, int fd, off_t offset);
static void * (*calloc_orig) (size_t nmemb, size_t size);
static void * (*malloc_orig) (size_t size);
static void (*free_orig) (void *ptr);
static void * (*valloc_orig) (size_t size);
static void * (*memalign_orig) (size_t boundary, size_t size);
static int (*posix_memalign_orig) (void **memptr, size_t alignment,
size_t size);
static void * (*realloc_orig) (void *ptr, size_t size);
static int (*brk_orig) (void *end_data_segment);
static void * (*sbrk_orig) (intptr_t increment);
static void * (*mmap_orig) (void *start, size_t length, int prot,
int flags, int fd, off_t offset);
/* TODO */
/* static void * (*mremap_orig) (void *old_address, size_t old_size,
size_t new_size, int flags); */
#ifdef HAVE_MMAP64
static void * (*mmap64_orig) (void *start, size_t length, int prot,
int flags, int fd, off64_t offset);
static void * (*mmap64_orig) (void *start, size_t length, int prot,
int flags, int fd, off64_t offset);
#endif
static int (*munmap_orig) (void *start, size_t length);
static int (*munmap_orig) (void *start, size_t length);
#ifdef HAVE_MAP_FD
static kern_return_t (*map_fd_orig) (int fd, vm_offset_t offset,
vm_offset_t *addr, boolean_t find_space,
@@ -60,6 +79,16 @@ static kern_return_t (*map_fd_orig) (int fd, vm_offset_t offset,
void _zz_load_mem(void)
{
LOADSYM(calloc);
LOADSYM(malloc);
LOADSYM(free);
LOADSYM(realloc);
LOADSYM(valloc);
LOADSYM(memalign);
LOADSYM(posix_memalign);
LOADSYM(brk);
LOADSYM(sbrk);
LOADSYM(mmap);
#ifdef HAVE_MMAP64
LOADSYM(mmap64);
@@ -70,6 +99,115 @@ void _zz_load_mem(void)
#endif
}
/* 32k of ugly static memory for programs that call us *before* were
* initialised */
uint64_t dummy_buffer[4096];
void *calloc(size_t nmemb, size_t size)
{
void *ret;
if(!_zz_ready)
{
/* Calloc says we must zero the data */
int i = (nmemb * size + 7) / 8;
while(i--)
dummy_buffer[i] = 0;
return dummy_buffer;
}
ret = calloc_orig(nmemb, size);
if(ret == NULL && _zz_memory && errno == ENOMEM)
raise(SIGKILL);
return ret;
}
void *malloc(size_t size)
{
void *ret;
if(!_zz_ready)
return dummy_buffer;
ret = malloc_orig(size);
if(ret == NULL && _zz_memory && errno == ENOMEM)
raise(SIGKILL);
return ret;
}
void free(void *ptr)
{
if(ptr == dummy_buffer)
return;
if(!_zz_ready)
LOADSYM(free);
free_orig(ptr);
}
void *realloc(void *ptr, size_t size)
{
void *ret;
if(ptr == dummy_buffer)
return ptr;
if(!_zz_ready)
LOADSYM(realloc);
ret = realloc_orig(ptr, size);
if(ret == NULL && _zz_memory && errno == ENOMEM)
raise(SIGKILL);
return ret;
}
void *valloc(size_t size)
{
void *ret;
if(!_zz_ready)
LOADSYM(valloc);
ret = valloc_orig(size);
if(ret == NULL && _zz_memory && errno == ENOMEM)
raise(SIGKILL);
return ret;
}
void *memalign(size_t boundary, size_t size)
{
void *ret;
if(!_zz_ready)
LOADSYM(memalign);
ret = memalign_orig(boundary, size);
if(ret == NULL && _zz_memory && errno == ENOMEM)
raise(SIGKILL);
return ret;
}
int posix_memalign(void **memptr, size_t alignment, size_t size)
{
int ret;
if(!_zz_ready)
LOADSYM(posix_memalign);
ret = posix_memalign_orig(memptr, alignment, size);
if(ret == ENOMEM && _zz_memory)
raise(SIGKILL);
return ret;
}
int brk(void *end_data_segment)
{
int ret;
if(!_zz_ready)
LOADSYM(brk);
ret = brk_orig(end_data_segment);
if(ret == -1 && _zz_memory && errno == ENOMEM)
raise(SIGKILL);
return ret;
}
void *sbrk(intptr_t increment)
{
void *ret;
if(!_zz_ready)
LOADSYM(sbrk);
ret = sbrk_orig(increment);
if(ret == (void *)-1 && _zz_memory && errno == ENOMEM)
raise(SIGKILL);
return ret;
}
/* Table used for mmap() and munmap() */
void **maps = NULL;
int nbmaps = 0;
+25 -5
View File
@@ -36,6 +36,8 @@
#include <sys/time.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "libzzuf.h"
#include "random.h"
@@ -80,6 +82,7 @@ static int quiet = 0;
static int maxbytes = -1;
static int md5 = 0;
static int checkexit = 0;
static int maxmem = -1;
static double maxtime = -1.0;
#define ZZUF_FD_SET(fd, p_fdset, maxfd) \
@@ -119,6 +122,7 @@ int main(int argc, char *argv[])
{ "stdin", 0, NULL, 'i' },
{ "include", 1, NULL, 'I' },
{ "md5", 0, NULL, 'm' },
{ "max-memory", 1, NULL, 'M' },
{ "network", 0, NULL, 'n' },
{ "protect", 1, NULL, 'P' },
{ "quiet", 0, NULL, 'q' },
@@ -131,11 +135,11 @@ int main(int argc, char *argv[])
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'v' },
};
int c = getopt_long(argc, argv, "B:cC:dE:F:iI:mnP:qr:R:s:ST:xhv",
int c = getopt_long(argc, argv, "B:cC:dE:F:iI:mM:nP:qr:R:s:ST:xhv",
long_options, &option_index);
# else
# define MOREINFO "Try `%s -h' for more information.\n"
int c = getopt(argc, argv, "B:cC:dE:F:iI:mnP:qr:R:s:ST:xhv");
int c = getopt(argc, argv, "B:cC:dE:F:iI:mM:nP:qr:R:s:ST:xhv");
# endif
if(c == -1)
break;
@@ -181,6 +185,10 @@ int main(int argc, char *argv[])
case 'm': /* --md5 */
md5 = 1;
break;
case 'M': /* --max-memory */
setenv("ZZUF_MEMORY", "1", 1);
maxmem = atoi(optarg);
break;
case 'n': /* --network */
setenv("ZZUF_NETWORK", "1", 1);
break;
@@ -424,6 +432,14 @@ static void spawn_child(char **argv)
return;
case 0:
/* Were the child */
if(maxmem >= 0)
{
struct rlimit rlim;
rlim.rlim_cur = maxmem * 1000000;
rlim.rlim_max = maxmem * 1000000;
setrlimit(RLIMIT_AS, &rlim);
}
for(j = 0; j < 3; j++)
{
close(fd[j][0]);
@@ -528,8 +544,10 @@ static void clean_children(void)
}
else if(WIFSIGNALED(status))
{
fprintf(stdout, "zzuf[seed=%i]: signal %i\n",
child_list[i].seed, WTERMSIG(status));
fprintf(stdout, "zzuf[seed=%i]: signal %i%s\n",
child_list[i].seed, WTERMSIG(status),
(WTERMSIG(status) == SIGKILL && maxmem >= 0) ?
" (memory exceeded?)" : "");
crashes++;
}
@@ -666,7 +684,7 @@ static void usage(void)
{
printf("Usage: zzuf [-cdimnqSx] [-r ratio] [-s seed | -s start:stop]\n");
printf(" [-F forks] [-C crashes] [-B bytes] [-T seconds]\n");
printf(" [-P protect] [-R refuse]\n");
printf(" [-M bytes] [-P protect] [-R refuse]\n");
printf(" [-I include] [-E exclude] [PROGRAM [ARGS]...]\n");
# ifdef HAVE_GETOPT_LONG
printf(" zzuf -h | --help\n");
@@ -688,6 +706,7 @@ static void usage(void)
printf(" -i, --stdin fuzz standard input\n");
printf(" -I, --include <regex> only fuzz files matching <regex>\n");
printf(" -m, --md5 compute the output's MD5 hash\n");
printf(" -M, --max-memory <n> maximum child virtual memory size in MB\n");
printf(" -n, --network fuzz network input\n");
printf(" -P, --protect <list> protect bytes and characters in <list>\n");
printf(" -q, --quiet do not print children's messages\n");
@@ -710,6 +729,7 @@ static void usage(void)
printf(" -i fuzz standard input\n");
printf(" -I <regex> only fuzz files matching <regex>\n");
printf(" -m compute the output's MD5 hash\n");
printf(" -M maximum child virtual memory size in MB\n");
printf(" -n fuzz network input\n");
printf(" -P <list> protect bytes and characters in <list>\n");
printf(" -q do not print the fuzzed application's messages\n");