persistent mode: shared memory test case transfer

This commit is contained in:
van Hauser
2020-05-25 16:40:55 +02:00
parent 4c394a9d7b
commit 707145c491
13 changed files with 341 additions and 31 deletions

View File

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

View File

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

View File

@ -0,0 +1,118 @@
/*
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;
}

View File

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

View File

@ -304,6 +304,10 @@
#define SHM_ENV_VAR "__AFL_SHM_ID" #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. */ /* Other less interesting, internal-only variables. */
#define CLANG_ENV_VAR "__AFL_CLANG_MODE" #define CLANG_ENV_VAR "__AFL_CLANG_MODE"

View File

@ -73,10 +73,18 @@ typedef struct afl_forkserver {
u8 last_kill_signal; /* Signal that killed the child */ 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 use_fauxsrv; /* Fauxsrv for non-forking targets? */
u8 qemu_mode; /* if running in qemu mode or not */ 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 */ char *cmplog_binary; /* the name of the cmplog binary */
/* Function to kick off the forkserver child */ /* Function to kick off the forkserver child */

View File

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

View File

@ -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++] = cc_params[cc_par_cnt++] =
"-D__AFL_LOOP(_A)=" "-D__AFL_LOOP(_A)="
"({ static volatile char *_B __attribute__((used)); " "({ static volatile char *_B __attribute__((used)); "

View File

@ -76,6 +76,8 @@ u8 __afl_area_initial[MAP_SIZE];
#endif #endif
u8 *__afl_area_ptr = __afl_area_initial; u8 *__afl_area_ptr = __afl_area_initial;
u8 *__afl_dictionary; u8 *__afl_dictionary;
u8 *__afl_fuzz_ptr;
u32 __afl_fuzz_len;
u32 __afl_final_loc; u32 __afl_final_loc;
u32 __afl_map_size = MAP_SIZE; u32 __afl_map_size = MAP_SIZE;
@ -92,6 +94,8 @@ __thread u32 __afl_prev_ctx;
__thread u32 __afl_cmp_counter; __thread u32 __afl_cmp_counter;
#endif #endif
int __afl_sharedmem_fuzzing __attribute__((weak));
struct cmp_map *__afl_cmp_map; struct cmp_map *__afl_cmp_map;
/* Running in persistent mode? */ /* 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. */ /* SHM setup. */
static void __afl_map_shm(void) { 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. */ assume we're not running in forkserver mode and just execute program. */
status |= (FS_OPT_ENABLED | FS_OPT_SNAPSHOT); 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) if (__afl_map_size <= FS_OPT_MAX_MAPSIZE)
status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_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); memcpy(tmp, &status, 4);
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; 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 (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)) == if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) ==
(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 // uh this forkserver master does not understand extended option passing
// or does not want the dictionary // 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 /* If we stopped the child in persistent mode, but there was a race
condition and afl-fuzz already issued SIGKILL, write off the old condition and afl-fuzz already issued SIGKILL, write off the old
process. */ process. */
@ -473,7 +541,8 @@ static void __afl_start_forkserver(void) {
if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) if (__afl_map_size <= FS_OPT_MAX_MAPSIZE)
status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_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); if (status) status |= (FS_OPT_ENABLED);
memcpy(tmp, &status, 4); memcpy(tmp, &status, 4);
@ -482,10 +551,17 @@ static void __afl_start_forkserver(void) {
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; 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 (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)) == if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) ==
(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 // uh this forkserver master does not understand extended option passing
// or does not want the dictionary // 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 /* If we stopped the child in persistent mode, but there was a race
condition and afl-fuzz already issued SIGKILL, write off the old condition and afl-fuzz already issued SIGKILL, write off the old
process. */ process. */

View File

@ -442,7 +442,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
if ((status & FS_OPT_ENABLED) == FS_OPT_ENABLED) { 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); 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) { if ((status & FS_OPT_MAPSIZE) == FS_OPT_MAPSIZE) {
u32 tmp_map_size = FS_OPT_GET_MAPSIZE(status); 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) { if (fsrv->function_ptr == NULL || fsrv->function_opt == NULL) {
// this is not afl-fuzz - we deny and return // 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) { if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) {
FATAL("Writing to forkserver failed."); 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) { 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; s32 fd = fsrv->out_fd;
if (fsrv->out_file) { if (fsrv->out_file) {
@ -785,6 +817,8 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) {
} }
}
} }
/* Execute target application, monitoring for timeouts. Return status /* Execute target application, monitoring for timeouts. Return status
@ -795,6 +829,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
s32 res; s32 res;
u32 exec_ms; u32 exec_ms;
u32 write_value = fsrv->last_run_timed_out;
/* After this memset, fsrv->trace_bits[] are effectively volatile, so we /* After this memset, fsrv->trace_bits[] are effectively volatile, so we
must prevent any earlier operations from venturing into that 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(); 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 /* we have the fork server (or faux server) up and running
First, tell it if the previous run timed out. */ 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; } if (*stop_soon_p) { return 0; }
RPFATAL(res, "Unable to request new process from fork server (OOM?)"); RPFATAL(res, "Unable to request new process from fork server (OOM?)");

View File

@ -2153,6 +2153,30 @@ void check_binary(afl_state_t *afl, u8 *fname) {
OKF(cPIN "Persistent mode binary detected."); OKF(cPIN "Persistent mode binary detected.");
setenv(PERSIST_ENV_VAR, "1", 1); setenv(PERSIST_ENV_VAR, "1", 1);
afl->persistent_mode = 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")) { } else if (getenv("AFL_PERSISTENT")) {

View File

@ -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_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon,
afl->afl_env.afl_debug_child_output); 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) { if (q->exec_cksum) {

View File

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