From 3dece217046d2553da5a9c53d65e972f51a6be06 Mon Sep 17 00:00:00 2001 From: Ammar Askar Date: Thu, 8 Aug 2019 16:47:43 -0700 Subject: [PATCH 1/3] Make some quality-of-life improvements Enable ASAN usage by providing -m none to AFL programs. Bind socket with reusable addr. Don't hardcode seeds/ directory. --- neuzz.c | 3 ++- nn.py | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/neuzz.c b/neuzz.c index 35e33c62..60d65dda 100644 --- a/neuzz.c +++ b/neuzz.c @@ -94,7 +94,8 @@ typedef uint64_t u64; unsigned long total_execs; /* Total number of execs */ static int shm_id; /* ID of the SHM region */ -static int mem_limit = 1024; /* Maximum memory limit for target program */ +// mem_limit = 0 is equiv. to -m none +static int mem_limit = 0; /* Maximum memory limit for target program */ static int cpu_aff = -1; /* Selected CPU core */ int round_cnt = 0; /* Round number counter */ int edge_gain=0; /* If there is new edge gain */ diff --git a/nn.py b/nn.py index fe0dcaec..68dfbe01 100644 --- a/nn.py +++ b/nn.py @@ -1,3 +1,4 @@ +import argparse import os import sys import glob @@ -33,6 +34,7 @@ SPLIT_RATIO = len(seed_list) # get binary argv argvv = sys.argv[1:] +args = None # process training data from afl raw data @@ -45,19 +47,19 @@ def process_data(): global new_seeds # shuffle training samples - seed_list = glob.glob('./seeds/*') + seed_list = glob.glob('./{}/*'.format(args.output_folder)) seed_list.sort() SPLIT_RATIO = len(seed_list) rand_index = np.arange(SPLIT_RATIO) np.random.shuffle(seed_list) - new_seeds = glob.glob('./seeds/id_*') + new_seeds = glob.glob('./{}/id_*'.format(args.output_folder)) call = subprocess.check_output # get MAX_FILE_SIZE cwd = os.getcwd() - max_file_name = call(['ls', '-S', cwd + '/seeds/']).decode('utf8').split('\n')[0].rstrip('\n') - MAX_FILE_SIZE = os.path.getsize(cwd + '/seeds/' + max_file_name) + max_file_name = call(['ls', '-S', args.output_folder]).decode('utf8').split('\n')[0].rstrip('\n') + MAX_FILE_SIZE = os.path.getsize(args.output_folder + '/' + max_file_name) # create directories to save label, spliced seeds, variant length seeds, crashes and mutated seeds. if os.path.isdir("./bitmaps/") == False: @@ -76,13 +78,14 @@ def process_data(): for f in seed_list: tmp_list = [] try: + mem_limit = '512' if not args.enable_asan else 'none' # append "-o tmp_file" to strip's arguments to avoid tampering tested binary. if argvv[0] == './strip': - out = call(['./afl-showmap', '-q', '-e', '-o', '/dev/stdout', '-m', '512', '-t', '500'] + argvv + [f] + ['-o', 'tmp_file']) + out = call(['./afl-showmap', '-q', '-e', '-o', '/dev/stdout', '-m', mem_limit, '-t', '500'] + args.target + [f] + ['-o', 'tmp_file']) else: - out = call(['./afl-showmap', '-q', '-e', '-o', '/dev/stdout', '-m', '512', '-t', '500'] + argvv + [f]) - except subprocess.CalledProcessError: - print("find a crash") + out = call(['./afl-showmap', '-q', '-e', '-o', '/dev/stdout', '-m', mem_limit, '-t', '500'] + args.target + [f]) + except subprocess.CalledProcessError as e: + print("find a crash", e) for line in out.splitlines(): edge = line.split(b':')[0] tmp_cnt.append(edge) @@ -399,6 +402,8 @@ def gen_grad(data): def setup_server(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # Allow re-use so we don't have to wait if the process crashes. + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((HOST, PORT)) sock.listen(1) conn, addr = sock.accept() @@ -415,4 +420,20 @@ def setup_server(): conn.close() -setup_server() +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="""Runs the background machine + learning process for Neuzz.""") + parser.add_argument('-e', + '--enable-asan', + help='Enable ASAN (runs afl-showmap with -m none)', + default=False, + action='store_true') + parser.add_argument('-o', + '--output-folder', + help="""The -o folder provided to afl, this is where the + Neuzz process reads seeds from.""", + required=True) + parser.add_argument('target', nargs=argparse.REMAINDER) + args = parser.parse_args() + + setup_server() From 41ac1353f2804c3faf14eb1b2cf21ddd0943a4b4 Mon Sep 17 00:00:00 2001 From: Ammar Askar Date: Fri, 9 Aug 2019 09:36:16 -0700 Subject: [PATCH 2/3] Fix output directory not working in anything but seeds/ --- neuzz.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/neuzz.c b/neuzz.c index 60d65dda..c8541b24 100644 --- a/neuzz.c +++ b/neuzz.c @@ -1691,9 +1691,18 @@ void dry_run(char* dir, int stage){ struct dirent *entry; struct stat statbuf; if((dp = opendir(dir)) == NULL) { + perror("opendir"); fprintf(stderr,"cannot open directory: %s\n", dir); return; } + // Store the original cwd so we can chdir back to it. + char original_cwd[PATH_MAX]; + if (getcwd(original_cwd, sizeof(original_cwd)) == NULL) { + perror("getcwd"); + return; + } + + if(chdir(dir)== -1) perror("chdir failed\n"); int cnt = 0; @@ -1756,7 +1765,7 @@ void dry_run(char* dir, int stage){ } } } - if(chdir("..") == -1) + if(chdir(original_cwd) == -1) perror("chdir failed\n"); closedir(dp); @@ -1783,6 +1792,7 @@ void copy_file(char* src, char* dst){ fptr1 = fopen(src, "r"); if (fptr1 == NULL) { + perror("fopen"); printf("Cannot open file %s \n", src); exit(0); } @@ -1790,6 +1800,7 @@ void copy_file(char* src, char* dst){ fptr2 = fopen(dst, "w"); if (fptr2 == NULL) { + perror("fopen"); printf("Cannot open file %s \n", dst); exit(0); } From e93c7a4c625aa1a17ae2f99e5902d62a46eaa068 Mon Sep 17 00:00:00 2001 From: Ammar Askar Date: Fri, 9 Aug 2019 12:41:54 -0700 Subject: [PATCH 3/3] Enable -m option in fuzzer and explain how to run with ASAN --- .gitignore | 2 ++ README.md | 17 +++++++++++++++++ neuzz.c | 45 ++++++++++++++++++++++++++++++++++++++------- nn.py | 11 ++++++----- 4 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0b386144 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Ignore the compiled neuzz binary. +neuzz diff --git a/README.md b/README.md index 2ff84d18..ed71eb07 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,23 @@ If you want to try NEUZZ on a new program, 1. Compile the new program from source code using afl-gcc. 2. Collect the training data by running AFL on the binary for a while(about an hour), then copy the queue folder to neuzz_in. 3. Follow the above two steps to start NN module and NEUZZ module. + +### Running with ASAN + +If your binary is compiled with ASAN instrumentation, do the following to run +it properly. + +Pass `--enable-asan` to `nn.py`: +```bash + python nn.py --enable-asan ./readelf -a +``` + +And pass `-m none` to `./nuezz` as you would to afl: +```bash + ./neuzz -m none -i neuzz_in -o seeds -l 7506 ./readelf -a @@ +``` + + ## Sample programs Try 10 real-world programs on NEUZZ. Check setup details at programs/[program names]/README. diff --git a/neuzz.c b/neuzz.c index c8541b24..0d621780 100644 --- a/neuzz.c +++ b/neuzz.c @@ -94,8 +94,7 @@ typedef uint64_t u64; unsigned long total_execs; /* Total number of execs */ static int shm_id; /* ID of the SHM region */ -// mem_limit = 0 is equiv. to -m none -static int mem_limit = 0; /* Maximum memory limit for target program */ +static int mem_limit = 1024; /* Maximum memory limit for target program */ static int cpu_aff = -1; /* Selected CPU core */ int round_cnt = 0; /* Round number counter */ int edge_gain=0; /* If there is new edge gain */ @@ -343,8 +342,7 @@ void init_forkserver(char** argv) { int st_pipe[2], ctl_pipe[2]; int status; int rlen; - char* cwd = getcwd(NULL, 0); - out_file = alloc_printf("%s/%s/.cur_input",cwd, out_dir); + out_file = alloc_printf("%s/.cur_input", out_dir); printf("Spinning up the fork server...\n"); if (pipe(st_pipe) || pipe(ctl_pipe)) perror("pipe() failed"); @@ -1996,9 +1994,9 @@ void start_fuzz_test(int f_len){ } -void main(int argc, char*argv[]){ +int main(int argc, char*argv[]){ int opt; - while ((opt = getopt(argc, argv, "+i:o:l:")) > 0) + while ((opt = getopt(argc, argv, "+i:o:l:m:")) > 0) switch (opt) { @@ -2034,9 +2032,42 @@ void main(int argc, char*argv[]){ printf("num_index %d %d small %d medium %d large %d\n", num_index[12], num_index[13], havoc_blk_small, havoc_blk_medium, havoc_blk_large); printf("mutation len: %ld\n", len); break; + + case 'm': /* memory limit */ + if (!strcmp(optarg, "none")) { + mem_limit = 0; + break; + } + + char suffix = 'M'; + if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || optarg[0] == '-') { + fprintf(stderr, "Bad syntax used for -m\n"); + return -1; + } + + switch (suffix) { + case 'T': mem_limit *= 1024 * 1024; break; + case 'G': mem_limit *= 1024; break; + case 'k': mem_limit /= 1024; break; + case 'M': break; + default: + fprintf(stderr, "Unsupported suffix or bad syntax for -m\n"); + return -1; + } + + if (mem_limit < 5) { + fprintf(stderr, "Dangerously low value of -m\n"); + return -1; + } + if (sizeof(rlim_t) == 4 && mem_limit > 2000) { + fprintf(stderr, "Value of -m out of range on 32-bit systems\n"); + return -1; + } + break; default: printf("no manual..."); + return 0; } setup_signal_handlers(); @@ -2055,6 +2086,6 @@ void main(int argc, char*argv[]){ start_fuzz(len); printf("total execs %ld edge coverage %d.\n", total_execs, count_non_255_bytes(virgin_bits)); - return; + return 0; } diff --git a/nn.py b/nn.py index 68dfbe01..5761eac9 100644 --- a/nn.py +++ b/nn.py @@ -46,20 +46,21 @@ def process_data(): global seed_list global new_seeds + output_folder = os.path.abspath(args.output_folder) # shuffle training samples - seed_list = glob.glob('./{}/*'.format(args.output_folder)) + seed_list = glob.glob('{}/*'.format(output_folder)) seed_list.sort() SPLIT_RATIO = len(seed_list) rand_index = np.arange(SPLIT_RATIO) np.random.shuffle(seed_list) - new_seeds = glob.glob('./{}/id_*'.format(args.output_folder)) + new_seeds = glob.glob('{}/id_*'.format(output_folder)) call = subprocess.check_output # get MAX_FILE_SIZE cwd = os.getcwd() - max_file_name = call(['ls', '-S', args.output_folder]).decode('utf8').split('\n')[0].rstrip('\n') - MAX_FILE_SIZE = os.path.getsize(args.output_folder + '/' + max_file_name) + max_file_name = call(['ls', '-S', output_folder]).decode('utf8').split('\n')[0].rstrip('\n') + MAX_FILE_SIZE = os.path.getsize(output_folder + '/' + max_file_name) # create directories to save label, spliced seeds, variant length seeds, crashes and mutated seeds. if os.path.isdir("./bitmaps/") == False: @@ -432,7 +433,7 @@ def setup_server(): '--output-folder', help="""The -o folder provided to afl, this is where the Neuzz process reads seeds from.""", - required=True) + default='seeds') parser.add_argument('target', nargs=argparse.REMAINDER) args = parser.parse_args()