From 8258e9394a7fb5688561b3f5e18d72392b9b6fe8 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Wed, 10 Jan 2007 17:17:26 +0000 Subject: [PATCH] * Implemented memory limits. Probably breaks on other arches because of all the new functions. --- doc/zzuf.1 | 32 ++++++++--- src/libzzuf.c | 5 ++ src/libzzuf.h | 1 + src/load-mem.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++-- src/zzuf.c | 30 ++++++++-- 5 files changed, 197 insertions(+), 19 deletions(-) diff --git a/doc/zzuf.1 b/doc/zzuf.1 index a5a8690..f04b18f 100644 --- a/doc/zzuf.1 +++ b/doc/zzuf.1 @@ -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 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() diff --git a/src/libzzuf.c b/src/libzzuf.c index 8268c85..4fb671d 100644 --- a/src/libzzuf.c +++ b/src/libzzuf.c @@ -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; diff --git a/src/libzzuf.h b/src/libzzuf.h index 4468d07..abca1ad 100644 --- a/src/libzzuf.h +++ b/src/libzzuf.h @@ -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 */ diff --git a/src/load-mem.c b/src/load-mem.c index badaaa6..eb316b8 100644 --- a/src/load-mem.c +++ b/src/load-mem.c @@ -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 @@ -31,7 +33,10 @@ #include #include #include +#include +#include +#include #include #include #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* we’re + * 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; diff --git a/src/zzuf.c b/src/zzuf.c index 96bb94d..0f20d6c 100644 --- a/src/zzuf.c +++ b/src/zzuf.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #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: /* We’re 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 only fuzz files matching \n"); printf(" -m, --md5 compute the output's MD5 hash\n"); + printf(" -M, --max-memory maximum child virtual memory size in MB\n"); printf(" -n, --network fuzz network input\n"); printf(" -P, --protect protect bytes and characters in \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 only fuzz files matching \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 protect bytes and characters in \n"); printf(" -q do not print the fuzzed application's messages\n");