diff --git a/docs/env_variables.md b/docs/env_variables.md index ed44c256..3052663a 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -664,6 +664,24 @@ checks or alter some of the more exotic semantics of the tool: Note that will not be exact and with slow targets it can take seconds until there is a slice for the time test. + - When using `AFL_PRELOAD` with a preload that disable `fork()` calls in + the target, the forkserver becomes unable to fork. + To overcome this issue, the `AFL_PRELOAD_DISCRIMINATE_FORKSERVER_PARENT` + permits to be able to check in the preloaded library if the environment + variable `AFL_FORKSERVER_PARENT` is set, to be able to use vanilla + `fork()` in the forkserver, and the placeholder in the target. + Here is a POC : + ```C + // AFL_PRELOAD_DISCRIMINATE_FORKSERVER_PARENT=1 afl-fuzz ... + pid_t fork(void) + { + if (getenv("AFL_FORKSERVER_PARENT") == NULL) + return 0; // We are in the target + else + return real_fork(); // We are in the forkserver + } + ``` + ## 6) Settings for afl-qemu-trace The QEMU wrapper used to instrument binary-only code supports several settings: diff --git a/include/envs.h b/include/envs.h index 7913e6b9..433b51a5 100644 --- a/include/envs.h +++ b/include/envs.h @@ -118,7 +118,8 @@ static char *afl_environment_variables[] = { "AFL_CFISAN_VERBOSE", "AFL_USE_LSAN", "AFL_WINE_PATH", "AFL_NO_SNAPSHOT", "AFL_EXPAND_HAVOC_NOW", "AFL_USE_FASAN", "AFL_USE_QASAN", "AFL_PRINT_FILENAMES", "AFL_PIZZA_MODE", "AFL_NO_FASTRESUME", - "AFL_SAN_ABSTRACTION", "AFL_SAN_NO_INST", "AFL_SAN_RECOVER", NULL}; + "AFL_SAN_ABSTRACTION", "AFL_SAN_NO_INST", "AFL_SAN_RECOVER", + "AFL_PRELOAD_DISCRIMINATE_FORKSERVER_PARENT", NULL}; extern char *afl_environment_variables[]; diff --git a/include/forkserver.h b/include/forkserver.h index 4f574aa1..db3bfcb3 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -159,6 +159,8 @@ typedef struct afl_forkserver { u8 uses_asan; /* Target uses ASAN/LSAN/MSAN? (bit 0/1/2 respectively) */ + bool setenv; /* setenv() to discriminate the forkserver? */ + bool debug; /* debug mode? */ u8 san_but_not_instrumented; /* Is it sanitizer enabled but not instrumented? diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index cba6436f..24d2ed9e 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -119,6 +119,8 @@ u64 __afl_map_addr; u32 __afl_first_final_loc; u32 __afl_old_forkserver; +u8 __afl_forkserver_setenv = 0; + #ifdef __AFL_CODE_COVERAGE typedef struct afl_module_info_t afl_module_info_t; @@ -888,6 +890,12 @@ static void __afl_start_forkserver(void) { } + if (getenv("AFL_PRELOAD_DISCRIMINATE_FORKSERVER_PARENT") != NULL) { + + __afl_forkserver_setenv = 1; + + } + /* Phone home and tell the parent that we're OK. If parent isn't there, assume we're not running in forkserver mode and just execute program. */ @@ -1054,6 +1062,13 @@ static void __afl_start_forkserver(void) { close(FORKSRV_FD); close(FORKSRV_FD + 1); + + if (unlikely(__afl_forkserver_setenv)) { + + unsetenv("AFL_FORKSERVER_PARENT"); + + } + return; } diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 07564673..2d07ca4d 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -250,6 +250,16 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->child_kill_signal = SIGKILL; fsrv->max_length = MAX_FILE; + if (getenv("AFL_PRELOAD_DISCRIMINATE_FORKSERVER_PARENT") != NULL) { + + fsrv->setenv = 1; + + } else { + + fsrv->setenv = 0; + + } + /* exec related stuff */ fsrv->child_pid = -1; fsrv->map_size = get_map_size(); @@ -878,6 +888,12 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* CHILD PROCESS */ + if (unlikely(fsrv->setenv)) { + + setenv("AFL_FORKSERVER_PARENT", "1", 0); + + } + // enable terminating on sigpipe in the children struct sigaction sa; memset((char *)&sa, 0, sizeof(sa));