persistent mode: shared memory test case transfer
MODIFY
docs/Changelog.md
docs/Changelog.md
@@ -10,6 +10,8 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
### Version ++2.65d (dev)
+ - initial support for persistent mode shared memory testcase handover
+ (instead of via files/stdin)
- afl-fuzz:
- -S slaves now only sync from the master to increase performance,
the -M master stilly syncs from everyone. Added checks that exactly

MODIFY
examples/persistent_demo/persistent_demo.c
examples/persistent_demo/persistent_demo.c
@@ -63,7 +63,7 @@ int main(int argc, char **argv) {
We just have some trivial inline code that faults on 'foo!'. */
/* do we have enough data? */
- if (len < 4) return 0;
+ if (len < 8) return 0;
if (buf[0] == 'f') {
@@ -77,6 +77,12 @@ int main(int argc, char **argv) {
if (buf[3] == '!') {
printf("four\n");
+ if (buf[4] == '!') {
+
+ printf("five\n");
+ if (buf[5] == '!') {
+
+ printf("six\n");
abort();
}
@@ -87,6 +93,10 @@ int main(int argc, char **argv) {
}
+ }
+
+ }
+
/*** END PLACEHOLDER CODE ***/
}

ADD
examples/persistent_demo/persistent_demo_new.c
+/*
+ american fuzzy lop++ - persistent mode example
+ --------------------------------------------
+
+ Originally written by Michal Zalewski
+
+ Copyright 2015 Google Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ This file demonstrates the high-performance "persistent mode" that may be
+ suitable for fuzzing certain fast and well-behaved libraries, provided that
+ they are stateless or that their internal state can be easily reset
+ across runs.
+
+ To make this work, the library and this shim need to be compiled in LLVM
+ mode using afl-clang-fast (other compiler wrappers will *not* work).
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+
+__AFL_FUZZ_INIT();
+
+unsigned int crc32_for_byte(unsigned int r) {
+
+ for (int j = 0; j < 8; ++j)
+ r = (r & 1 ? 0 : (unsigned int)0xEDB88320L) ^ r >> 1;
+ return r ^ (unsigned int)0xFF000000L;
+
+}
+
+unsigned int crc32(unsigned char *data, unsigned int n_bytes) {
+
+ static unsigned char table[0x100];
+ unsigned int crc = 0;
+ if (!*table)
+ for (unsigned int i = 0; i < 0x100; ++i)
+ table[i] = crc32_for_byte(i);
+ for (unsigned int i = 0; i < n_bytes; ++i)
+ crc = table[(unsigned char)crc ^ (data)[i]] ^ crc >> 8;
+ return crc;
+
+}
+
+/* Main entry point. */
+
+int main(int argc, char **argv) {
+
+ ssize_t len; /* how much input did we read? */
+ unsigned char *buf; /* test case buffer pointer */
+
+ /* The number passed to __AFL_LOOP() controls the maximum number of
+ iterations before the loop exits and the program is allowed to
+ terminate normally. This limits the impact of accidental memory leaks
+ and similar hiccups. */
+
+ buf = __AFL_FUZZ_TESTCASE_BUF;
+
+ while (__AFL_LOOP(1000)) {
+
+ len = __AFL_FUZZ_TESTCASE_LEN;
+
+ /* do we have enough data? */
+ if (len < 8) return 0;
+
+ if (buf[0] == 'f') {
+
+ printf("one\n");
+ if (buf[1] == 'o') {
+
+ printf("two\n");
+ if (buf[2] == 'o') {
+
+ printf("three\n");
+ if (buf[3] == '!') {
+
+ printf("four\n");
+ if (buf[4] == '!') {
+
+ printf("five\n");
+ if (buf[6] == '!') {
+
+ printf("six\n");
+ abort();
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ /*** END PLACEHOLDER CODE ***/
+
+ }
+
+ /* Once the loop is exited, terminate normally - AFL will restart the process
+ when this happens, with a clean slate when it comes to allocated memory,
+ leftover file descriptors, etc. */
+
+ return 0;
+
+}
+

MODIFY
include/afl-fuzz.h
include/afl-fuzz.h
@@ -342,6 +342,7 @@ typedef struct afl_state {
afl_forkserver_t fsrv;
sharedmem_t shm;
+ sharedmem_t * shm_fuzz;
afl_env_vars_t afl_env;
char **argv; /* argv if needed */

MODIFY
include/config.h
include/config.h
#define SHM_ENV_VAR "__AFL_SHM_ID"
+/* Environment variable used to pass SHM FUZZ ID to the called program. */
+
+#define SHM_FUZZ_ENV_VAR "__AFL_SHM_FUZZ_ID"
+
/* Other less interesting, internal-only variables. */
#define CLANG_ENV_VAR "__AFL_CLANG_MODE"

MODIFY
include/forkserver.h
include/forkserver.h
@@ -73,10 +73,18 @@ typedef struct afl_forkserver {
u8 last_kill_signal; /* Signal that killed the child */
+ u8 use_shdmen_fuzz; /* use shared mem for test cases */
+
+ u8 support_shdmen_fuzz; /* set by afl-fuzz */
+
u8 use_fauxsrv; /* Fauxsrv for non-forking targets? */
u8 qemu_mode; /* if running in qemu mode or not */
+ u32 shdmem_fuzz_len; /* length of the fuzzing test case */
+
+ u8 *shdmem_fuzz; /* allocated memory for fuzzing */
+
char *cmplog_binary; /* the name of the cmplog binary */
/* Function to kick off the forkserver child */

MODIFY
include/types.h
include/types.h
@@ -43,10 +43,11 @@ typedef uint32_t u32;
#define FS_ERROR_MMAP 16
/* Reporting options */
-#define FS_OPT_ENABLED 0x8f000001
+#define FS_OPT_ENABLED 0x80000001
#define FS_OPT_MAPSIZE 0x40000000
#define FS_OPT_SNAPSHOT 0x20000000
#define FS_OPT_AUTODICT 0x10000000
+#define FS_OPT_SHDMEM_FUZZ 0x01000000
// FS_OPT_MAX_MAPSIZE is 8388608 = 0x800000 = 2^23 = 1 << 22
#define FS_OPT_MAX_MAPSIZE ((0x00fffffe >> 1) + 1)
#define FS_OPT_GET_MAPSIZE(x) (((x & 0x00fffffe) >> 1) + 1)

MODIFY
llvm_mode/afl-clang-fast.c
llvm_mode/afl-clang-fast.c
@@ -489,6 +489,14 @@ static void edit_params(u32 argc, char **argv, char **envp) {
*/
+ cc_params[cc_par_cnt++] =
+ "-D__AFL_FUZZ_INIT()="
+ "int __afl_sharedmem_fuzzing = 1;"
+ "extern unsigned int __afl_fuzz_len;"
+ "extern unsigned char *__afl_fuzz_ptr;";
+ cc_params[cc_par_cnt++] = "-D__AFL_FUZZ_TESTCASE_BUF=__afl_fuzz_ptr";
+ cc_params[cc_par_cnt++] = "-D__AFL_FUZZ_TESTCASE_LEN=__afl_fuzz_len";
+
cc_params[cc_par_cnt++] =
"-D__AFL_LOOP(_A)="
"({ static volatile char *_B __attribute__((used)); "

MODIFY
llvm_mode/afl-llvm-rt.o.c
llvm_mode/afl-llvm-rt.o.c
@@ -76,6 +76,8 @@ u8 __afl_area_initial[MAP_SIZE];
#endif
u8 *__afl_area_ptr = __afl_area_initial;
u8 *__afl_dictionary;
+u8 *__afl_fuzz_ptr;
+u32 __afl_fuzz_len;
u32 __afl_final_loc;
u32 __afl_map_size = MAP_SIZE;
@@ -92,6 +94,8 @@ __thread u32 __afl_prev_ctx;
__thread u32 __afl_cmp_counter;
#endif
+int __afl_sharedmem_fuzzing __attribute__((weak));
+
struct cmp_map *__afl_cmp_map;
/* Running in persistent mode? */
@@ -109,6 +113,59 @@ void send_forkserver_error(int error) {
}
+/* SHM fuzzing setup. */
+
+static void __afl_map_shm_fuzz() {
+
+ char *id_str = getenv(SHM_FUZZ_ENV_VAR);
+
+ if (id_str) {
+
+#ifdef USEMMAP
+ const char * shm_file_path = id_str;
+ int shm_fd = -1;
+ unsigned char *shm_base = NULL;
+
+ /* create the shared memory segment as if it was a file */
+ shm_fd = shm_open(shm_file_path, O_RDWR, 0600);
+ if (shm_fd == -1) {
+
+ fprintf(stderr, "shm_open() failed for fuzz\n");
+ send_forkserver_error(FS_ERROR_SHM_OPEN);
+ exit(1);
+
+ }
+
+ __afl_fuzz_ptr = mmap(0, MAX_FILE, PROT_READ, MAP_SHARED, shm_fd, 0);
+
+#else
+ u32 shm_id = atoi(id_str);
+
+ __afl_fuzz_ptr = shmat(shm_id, NULL, 0);
+
+#endif
+
+ /* Whooooops. */
+
+ if (__afl_fuzz_ptr == (void *)-1) {
+
+ fprintf(stderr, "Error: could not access fuzzing shared memory\n");
+ exit(1);
+
+ }
+
+ if (getenv("AFL_DEBUG"))
+ fprintf(stderr, "DEBUG: successfully got fuzzing shared memory\n");
+
+ } else {
+
+ fprintf(stderr, "Error: variable for fuzzing shared memory is not set\n");
+ exit(1);
+
+ }
+
+}
+
/* SHM setup. */
static void __afl_map_shm(void) {
@@ -310,17 +367,25 @@ static void __afl_start_snapshots(void) {
assume we're not running in forkserver mode and just execute program. */
status |= (FS_OPT_ENABLED | FS_OPT_SNAPSHOT);
+ if (__afl_sharedmem_fuzzing != 0) status |= FS_OPT_SHDMEM_FUZZ;
if (__afl_map_size <= FS_OPT_MAX_MAPSIZE)
status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE);
- if (__afl_dictionary_len > 0 && __afl_dictionary) status |= FS_OPT_AUTODICT;
+ if (__afl_dictionary_len && __afl_dictionary) status |= FS_OPT_AUTODICT;
memcpy(tmp, &status, 4);
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
- if (__afl_dictionary_len > 0 && __afl_dictionary) {
+ if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) {
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
+ if ((was_killed & (0xffffffff & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ))) ==
+ (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) {
+
+ __afl_map_shm_fuzz();
+
+ }
+
if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) ==
(FS_OPT_ENABLED | FS_OPT_AUTODICT)) {
@@ -357,7 +422,7 @@ static void __afl_start_snapshots(void) {
// uh this forkserver master does not understand extended option passing
// or does not want the dictionary
- already_read_first = 1;
+ if (!__afl_fuzz_ptr) already_read_first = 1;
}
@@ -378,6 +443,9 @@ static void __afl_start_snapshots(void) {
}
+ __afl_fuzz_len = (was_killed >> 8);
+ was_killed = (was_killed & 0xff);
+
/* If we stopped the child in persistent mode, but there was a race
condition and afl-fuzz already issued SIGKILL, write off the old
process. */
@@ -473,7 +541,8 @@ static void __afl_start_forkserver(void) {
if (__afl_map_size <= FS_OPT_MAX_MAPSIZE)
status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE);
- if (__afl_dictionary_len > 0 && __afl_dictionary) status |= FS_OPT_AUTODICT;
+ if (__afl_dictionary_len && __afl_dictionary) status |= FS_OPT_AUTODICT;
+ if (__afl_sharedmem_fuzzing != 0) status |= FS_OPT_SHDMEM_FUZZ;
if (status) status |= (FS_OPT_ENABLED);
memcpy(tmp, &status, 4);
@@ -482,10 +551,17 @@ static void __afl_start_forkserver(void) {
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
- if (__afl_dictionary_len > 0 && __afl_dictionary) {
+ if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) {
if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1);
+ if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) ==
+ (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) {
+
+ __afl_map_shm_fuzz();
+
+ }
+
if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) ==
(FS_OPT_ENABLED | FS_OPT_AUTODICT)) {
@@ -522,7 +598,7 @@ static void __afl_start_forkserver(void) {
// uh this forkserver master does not understand extended option passing
// or does not want the dictionary
- already_read_first = 1;
+ if (!__afl_fuzz_ptr) already_read_first = 1;
}
@@ -544,6 +620,9 @@ static void __afl_start_forkserver(void) {
}
+ __afl_fuzz_len = (was_killed >> 8);
+ was_killed = (was_killed & 0xff);
+
/* If we stopped the child in persistent mode, but there was a race
condition and afl-fuzz already issued SIGKILL, write off the old
process. */

MODIFY
src/afl-forkserver.c
src/afl-forkserver.c
@@ -442,7 +442,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
if ((status & FS_OPT_ENABLED) == FS_OPT_ENABLED) {
- if (!be_quiet && getenv("AFL_DEBUG")) {
+ if (getenv("AFL_DEBUG")) {
ACTF("Extended forkserver functions received (%08x).", status);
@@ -455,6 +455,28 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
}
+ if ((status & FS_OPT_SHDMEM_FUZZ) == FS_OPT_SHDMEM_FUZZ) {
+
+ if (fsrv->support_shdmen_fuzz) {
+
+ fsrv->use_shdmen_fuzz = 1;
+ if (!be_quiet) { ACTF("Using SHARED MEMORY FUZZING feature."); }
+
+ if ((status & FS_OPT_AUTODICT) == 0) {
+
+ u32 send_status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ);
+ if (write(fsrv->fsrv_ctl_fd, &send_status, 4) != 4) {
+
+ FATAL("Writing to forkserver failed.");
+
+ }
+
+ }
+
+ }
+
+ }
+
if ((status & FS_OPT_MAPSIZE) == FS_OPT_MAPSIZE) {
u32 tmp_map_size = FS_OPT_GET_MAPSIZE(status);
@@ -490,7 +512,10 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
if (fsrv->function_ptr == NULL || fsrv->function_opt == NULL) {
// this is not afl-fuzz - we deny and return
- status = (0xffffffff ^ (FS_OPT_ENABLED | FS_OPT_AUTODICT));
+ if (fsrv->use_shdmen_fuzz)
+ status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ);
+ else
+ status = (FS_OPT_ENABLED | FS_OPT_AUTODICT);
if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
FATAL("Writing to forkserver failed.");
@@ -749,6 +774,13 @@ static void afl_fsrv_kill(afl_forkserver_t *fsrv) {
void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) {
+ if (fsrv->shdmem_fuzz) {
+
+ memcpy(fsrv->shdmem_fuzz, buf, len);
+ fsrv->shdmem_fuzz_len = len;
+
+ } else {
+
s32 fd = fsrv->out_fd;
if (fsrv->out_file) {
@@ -787,6 +819,8 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) {
}
+}
+
/* Execute target application, monitoring for timeouts. Return status
information. The called program will update afl->fsrv->trace_bits. */
@@ -795,6 +829,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
s32 res;
u32 exec_ms;
+ u32 write_value = fsrv->last_run_timed_out;
/* After this memset, fsrv->trace_bits[] are effectively volatile, so we
must prevent any earlier operations from venturing into that
@@ -804,10 +839,12 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
MEM_BARRIER();
+ if (fsrv->shdmem_fuzz_len) write_value += (fsrv->shdmem_fuzz_len << 8);
+
/* we have the fork server (or faux server) up and running
First, tell it if the previous run timed out. */
- if ((res = write(fsrv->fsrv_ctl_fd, &fsrv->last_run_timed_out, 4)) != 4) {
+ if ((res = write(fsrv->fsrv_ctl_fd, &write_value, 4)) != 4) {
if (*stop_soon_p) { return 0; }
RPFATAL(res, "Unable to request new process from fork server (OOM?)");

MODIFY
src/afl-fuzz-init.c
src/afl-fuzz-init.c
@@ -2153,6 +2153,30 @@ void check_binary(afl_state_t *afl, u8 *fname) {
OKF(cPIN "Persistent mode binary detected.");
setenv(PERSIST_ENV_VAR, "1", 1);
afl->persistent_mode = 1;
+ // do not fail if we can not get the fuzzing shared mem
+ if ((afl->shm_fuzz = calloc(1, sizeof(sharedmem_t)))) {
+
+ // we need to set the dumb mode to not overwrite the SHM_ENV_VAR
+ if ((afl->fsrv.shdmem_fuzz = afl_shm_init(afl->shm_fuzz, MAX_FILE, 1))) {
+
+#ifdef USEMMAP
+ setenv(SHM_FUZZ_ENV_VAR, afl->shm_fuzz->g_shm_file_path, 1);
+#else
+ u8 *shm_str;
+ shm_str = alloc_printf("%d", afl->shm_fuzz->shm_id);
+ setenv(SHM_FUZZ_ENV_VAR, shm_str, 1);
+ ck_free(shm_str);
+#endif
+ afl->fsrv.support_shdmen_fuzz = 1;
+
+ } else {
+
+ free(afl->shm_fuzz);
+ afl->shm_fuzz = NULL;
+
+ }
+
+ }
} else if (getenv("AFL_PERSISTENT")) {

MODIFY
src/afl-fuzz-run.c
src/afl-fuzz-run.c
@@ -231,6 +231,16 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon,
afl->afl_env.afl_debug_child_output);
+ if (afl->fsrv.support_shdmen_fuzz && !afl->fsrv.use_shdmen_fuzz) {
+
+ afl_shm_deinit(afl->shm_fuzz);
+ free(afl->shm_fuzz);
+ afl->shm_fuzz = NULL;
+ afl->fsrv.support_shdmen_fuzz = 0;
+ afl->fsrv.shdmem_fuzz = NULL;
+
+ }
+
}
if (q->exec_cksum) {

MODIFY
src/afl-fuzz.c
src/afl-fuzz.c
@@ -1379,6 +1379,14 @@ stop_fuzzing:
destroy_extras(afl);
destroy_custom_mutators(afl);
afl_shm_deinit(&afl->shm);
+
+ if (afl->shm_fuzz) {
+
+ afl_shm_deinit(afl->shm_fuzz);
+ free(afl->shm_fuzz);
+
+ }
+
afl_fsrv_deinit(&afl->fsrv);
if (afl->orig_cmdline) { ck_free(afl->orig_cmdline); }
ck_free(afl->fsrv.target_path);

persistent mode: shared memory test case transfer