better replay mode error handling, added replay mode documentation, code formatting

This commit is contained in:
Davide Quarta
2024-02-05 18:26:46 +01:00
parent 8fedf49984
commit 023fc19ce0
7 changed files with 183 additions and 82 deletions

View File

@ -125,7 +125,7 @@
#endif /* ^!SIMPLE_FILES */ #endif /* ^!SIMPLE_FILES */
#ifdef AFL_PERSISTENT_RECORD #ifdef AFL_PERSISTENT_RECORD
#define RECORD_PREFIX "RECORD:" #define RECORD_PREFIX "RECORD:"
#endif #endif
#define STAGE_BUF_SIZE (64) /* usable size for stage name buf in afl_state */ #define STAGE_BUF_SIZE (64) /* usable size for stage name buf in afl_state */

View File

@ -83,10 +83,14 @@
will be kept and written to the crash/ directory as RECORD:... files. will be kept and written to the crash/ directory as RECORD:... files.
Note that every crash will be written, not only unique ones! */ Note that every crash will be written, not only unique ones! */
// #define AFL_PERSISTENT_RECORD #define AFL_PERSISTENT_RECORD
/* Builds compiler-rt with support to replay persistent records */ /* Adds support in compiler-rt to replay persistent records */
// #define AFL_PERSISTENT_REPLAY #define AFL_PERSISTENT_REPLAY
/* Adds support in compiler-rt to replay persistent records in @@-style
* harnesses */
// #define AFL_PERSISTENT_REPLAY_ARGPARSE
/* console output colors: There are three ways to configure its behavior /* console output colors: There are three ways to configure its behavior
* 1. default: colored outputs fixed on: defined USE_COLOR && defined * 1. default: colored outputs fixed on: defined USE_COLOR && defined

View File

@ -11,71 +11,116 @@
#include <fcntl.h> #include <fcntl.h>
static unsigned short int is_replay_record; static unsigned short int is_replay_record;
static unsigned int replay_record; static unsigned int replay_record;
static unsigned int replay_record_cnt; static unsigned int replay_record_cnt;
static char replay_record_path[PATH_MAX]; static char replay_record_path[PATH_MAX];
static char **record_arg; static char *replay_record_dir;
static char *replay_record_dir; static struct dirent **record_list;
static struct dirent **record_list;
#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
static char **record_arg = NULL;
#endif // AFL_PERSISTENT_REPLAY_ARGPARSE
static int select_files(const struct dirent *dirbuf) { static int select_files(const struct dirent *dirbuf) {
char fn[4096]; char fn[4096];
if (dirbuf->d_name[0] == '.'){ if (dirbuf->d_name[0] == '.') {
return 0; return 0;
} else { } else {
snprintf(fn, sizeof(fn), "RECORD:%06u", replay_record); snprintf(fn, sizeof(fn), "RECORD:%06u", replay_record);
return !!strstr(dirbuf->d_name, fn); return !!strstr(dirbuf->d_name, fn);
} }
} }
static int compare_files(const struct dirent **da, const struct dirent **db) { static int compare_files(const struct dirent **da, const struct dirent **db) {
unsigned int c1=0, c2=0; unsigned int c1 = 0, c2 = 0;
sscanf((*da)->d_name, "RECORD:%*u,cnt:%06u", &c1); sscanf((*da)->d_name, "RECORD:%*u,cnt:%06u", &c1);
sscanf((*db)->d_name, "RECORD:%*u,cnt:%06u", &c2); sscanf((*db)->d_name, "RECORD:%*u,cnt:%06u", &c2);
return c1-c2; return c1 - c2;
} }
__attribute__((destructor)) static void __afl_record_replay_destroy(void){ __attribute__((destructor)) static void __afl_record_replay_destroy(void) {
for (int i=0; i < replay_record_cnt; i++) {
for (int i = 0; i < replay_record_cnt; i++) {
free(record_list[i]); free(record_list[i]);
} }
free(record_list); free(record_list);
} }
__attribute__((constructor)) static void __afl_record_replay_init(int argc, char **argv) { __attribute__((constructor)) static void __afl_record_replay_init(
#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
char **argp; int argc, char **argv
#endif // AFL_PERSISTENT_REPLAY_ARGPARSE
) {
#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
char **argp;
#endif // AFL_PERSISTENT_REPLAY_ARGPARSE
struct stat sb;
/* caveat: if harness uses @@ and we don't pass it, it will regardless loop
* the number of iterations defined for AFL_LOOP (on the same file)*/
if (!(is_replay_record = !!getenv("AFL_PERSISTENT_REPLAY"))) {
/* caveat: if harness uses @@ and we don't pass it, it will regardless loop the number of iterations defined for AFL_LOOP (on the same file)*/
if(!(is_replay_record = !!getenv("AFL_PERSISTENT_REPLAY"))){
// printf("[warning] AFL_PERSISTENT_REPLAY not set.\n"); // printf("[warning] AFL_PERSISTENT_REPLAY not set.\n");
return; return;
} }
replay_record = atoi(getenv("AFL_PERSISTENT_REPLAY")); replay_record = atoi(getenv("AFL_PERSISTENT_REPLAY"));
replay_record_dir = getenv("AFL_PERSISTENT_DIR"); replay_record_dir = getenv("AFL_PERSISTENT_DIR");
replay_record_cnt = scandir(replay_record_dir ? replay_record_dir : "./", &record_list, select_files, compare_files);
if (!replay_record_cnt){ if (!(stat(replay_record_dir, &sb) == 0 && S_ISDIR(sb.st_mode))) {
printf("[error] Can't find the requested record!\n");
fprintf(stderr, "[error] Can't find the requested record directory!\n");
is_replay_record = 0; is_replay_record = 0;
return;
} }
replay_record_cnt = scandir(replay_record_dir ? replay_record_dir : "./",
&record_list, select_files, compare_files);
if (!replay_record_cnt) {
fprintf(stderr, "[error] Can't find the requested record!\n");
is_replay_record = 0;
}
#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
argp = argv; argp = argv;
while (*argp){ while (*argp) {
if (!strcmp(*argp, "@@")){
if (!strcmp(*argp, "@@")) {
record_arg = argp; record_arg = argp;
*record_arg = replay_record_path; *record_arg = replay_record_path;
break; break;
} }
++argp; ++argp;
} }
#endif // AFL_PERSISTENT_REPLAY_ARGPARSE
} }
/* only used if explictly included for compatibility /* only used if explictly included for compatibility
@ -83,67 +128,80 @@ __attribute__((constructor)) static void __afl_record_replay_init(int argc, char
#ifdef AFL_COMPAT #ifdef AFL_COMPAT
#ifndef PATH_MAX #ifndef PATH_MAX
#define PATH_MAX 4096 #define PATH_MAX 4096
#endif #endif
#define FUZZ_BUF_SIZE 1024000 #define FUZZ_BUF_SIZE 1024000
// extern ssize_t read(int fildes, void *buf, size_t nbyte); // extern ssize_t read(int fildes, void *buf, size_t nbyte);
//extern int __afl_persistent_loop(unsigned int max_cnt); // extern int __afl_persistent_loop(unsigned int max_cnt);
//extern unsigned char fuzz_buf[]; // extern unsigned char fuzz_buf[];
#ifndef __AFL_HAVE_MANUAL_CONTROL #ifndef __AFL_HAVE_MANUAL_CONTROL
#define __AFL_HAVE_MANUAL_CONTROL #define __AFL_HAVE_MANUAL_CONTROL
#endif #endif
#define __AFL_FUZZ_TESTCASE_LEN (read(0, fuzz_buf, FUZZ_BUF_SIZE)) #define __AFL_FUZZ_TESTCASE_LEN (read(0, fuzz_buf, FUZZ_BUF_SIZE))
#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
#define __AFL_FUZZ_INIT() void sync(void); #define __AFL_FUZZ_INIT() void sync(void);
#define __AFL_INIT() sync() #define __AFL_INIT() sync()
#define __AFL_LOOP(x) __afl_persistent_loop(x) #define __AFL_LOOP(x) __afl_persistent_loop(x)
unsigned char fuzz_buf[FUZZ_BUF_SIZE]; unsigned char fuzz_buf[FUZZ_BUF_SIZE];
int __afl_persistent_loop(unsigned int max_cnt) { int __afl_persistent_loop(unsigned int max_cnt) {
static unsigned int cycle_cnt = 1; static unsigned int cycle_cnt = 1;
static unsigned short int inited = 0; static unsigned short int inited = 0;
char tcase[PATH_MAX]; char tcase[PATH_MAX];
if( is_replay_record ){ if (is_replay_record) {
if (!inited) {
if (!inited){
cycle_cnt = replay_record_cnt; cycle_cnt = replay_record_cnt;
inited = 1; inited = 1;
} }
snprintf(tcase, PATH_MAX, "%s/%s", snprintf(tcase, PATH_MAX, "%s/%s",
replay_record_dir ? replay_record_dir : "./", replay_record_dir ? replay_record_dir : "./",
record_list[replay_record_cnt-cycle_cnt]->d_name); record_list[replay_record_cnt - cycle_cnt]->d_name);
#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
if (record_arg) { if (record_arg) {
*record_arg = tcase; *record_arg = tcase;
} else {
} else
#endif // AFL_PERSISTENT_REPLAY_ARGPARSE
{
int fd = open(tcase, O_RDONLY); int fd = open(tcase, O_RDONLY);
dup2(fd, 0); dup2(fd, 0);
close(fd); close(fd);
} }
} else { } else {
if (!inited){ if (!inited) {
cycle_cnt = max_cnt; cycle_cnt = max_cnt;
inited = 1; inited = 1;
} }
} }
return cycle_cnt--; return cycle_cnt--;
} }
#endif // AFL_COMPAT #endif // AFL_COMPAT
#endif // _HAVE_PERSISTENT_REPLAY_H #endif // _HAVE_PERSISTENT_REPLAY_H

View File

@ -195,4 +195,32 @@ Then as first line after the `__AFL_LOOP` while loop:
int len = __AFL_FUZZ_TESTCASE_LEN; int len = __AFL_FUZZ_TESTCASE_LEN;
``` ```
And that is all! And that is all!
## 6) Persistent record, and replay
If your software under test requires keeping a state between persistent loop iterations (i.e., a stateful network stack), you can use the `AFL_PERSISTENT_RECORD` variable as described in the [environment variables documentation](../docs/env_variables.md).
To easily replay a crashing, or hanging record, you can use the persistent replay functionality by compiling AFL++ after uncommenting the `AFL_PERSISTENT_REPLAY` define in [config.h](../include/config.h).
You can then run the test binary specifying the record number via the AFL_PERSISTENT_REPLAY environment variable (i.e., `RECORD:XXXXX`` -> `AFL_PERSISTENT_REPLAY=XXXXX`).
The directory where the record files live can be specified via the `AFL_PERSISTENT_DIR` environment varilable, otherwise by default it will be considered the current directory (`./`).
If your harness reads the input files from arguments using the special `@@` argument you will need to define `AFL_PERSISTENT_ARGPARSE` in `config.h`, or before including the `persistent_replay.h` header file as show before.
In order to offer transparent support to harnesses using the `@@` command line argument, arguments are parsed by the `__afl_record_replay_init` init function. Since not all systems support passing arguments to initializers, this functionality is disabled by default, it's recommendable to use the `__AFL_FUZZ_TESTCASE_BUF/__AFL_FUZZ_TESTCASE_LEN` shared memory mechanism instead.
### 7) Drop in replay functionality
To use the replay functionality without having to use `afl-cc` you can just define `AFL_COMPAT` and include the [include/persistent_replay.h](../include/persistent_replay.h) self contained header file that provides a drop-in replacement for the persistent loop mechanism.
```c
#ifndef __AFL_FUZZ_TESTCASE_LEN
#define AFL_COMPAT
// #define AFL_PERSISTENT_REPLAY_ARGPARSE
#include "persistent_replay.h"
#endif
__AFL_FUZZ_INIT();
```
A simple example is provided in [persistent_demo_new.c](../utils/persistent_mode/persistent_demo_new.c).

View File

@ -84,7 +84,7 @@
#include <fcntl.h> #include <fcntl.h>
#ifdef AFL_PERSISTENT_REPLAY #ifdef AFL_PERSISTENT_REPLAY
#include "persistent_replay.h" #include "persistent_replay.h"
#endif #endif
/* Globals needed by the injected instrumentation. The __afl_area_initial region /* Globals needed by the injected instrumentation. The __afl_area_initial region
@ -1344,37 +1344,49 @@ int __afl_persistent_loop(unsigned int max_cnt) {
#ifdef AFL_PERSISTENT_REPLAY #ifdef AFL_PERSISTENT_REPLAY
#ifndef PATH_MAX #ifndef PATH_MAX
#define PATH_MAX 4096 #define PATH_MAX 4096
#endif #endif
static u8 inited = 0; static u8 inited = 0;
char tcase[PATH_MAX]; char tcase[PATH_MAX];
if( unlikely(is_replay_record) ){ if (unlikely(is_replay_record)) {
if (!inited){ if (!inited) {
cycle_cnt = replay_record_cnt;
inited = 1;
}
snprintf(tcase, PATH_MAX, "%s/%s", cycle_cnt = replay_record_cnt;
replay_record_dir ? replay_record_dir : "./", inited = 1;
record_list[replay_record_cnt-cycle_cnt]->d_name);
}
snprintf(tcase, PATH_MAX, "%s/%s",
replay_record_dir ? replay_record_dir : "./",
record_list[replay_record_cnt - cycle_cnt]->d_name);
#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
if (record_arg) {
*record_arg = tcase;
} else
#endif // AFL_PERSISTENT_REPLAY_ARGPARSE
{
int fd = open(tcase, O_RDONLY);
dup2(fd, 0);
close(fd);
}
if (record_arg) {
*record_arg = tcase;
} else {
int fd = open(tcase, O_RDONLY);
dup2(fd, 0);
close(fd);
}
return cycle_cnt--; return cycle_cnt--;
} else } else
#endif #endif
if (first_pass) { if (first_pass) {
/* Make sure that every iteration of __AFL_LOOP() starts with a clean slate. /* Make sure that every iteration of __AFL_LOOP() starts with a clean slate.
On subsequent calls, the parent will take care of that, but on the first On subsequent calls, the parent will take care of that, but on the first

View File

@ -1593,7 +1593,7 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
#ifdef AFL_PERSISTENT_RECORD #ifdef AFL_PERSISTENT_RECORD
fsrv_run_result_t retval = FSRV_RUN_OK; fsrv_run_result_t retval = FSRV_RUN_OK;
char *persistent_out_fmt; char *persistent_out_fmt;
#endif #endif
#ifdef __linux__ #ifdef __linux__
@ -1803,6 +1803,7 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
persistent_out_fmt = "%s/hangs/RECORD:%06u,cnt:%06u"; persistent_out_fmt = "%s/hangs/RECORD:%06u,cnt:%06u";
goto store_persistent_record; goto store_persistent_record;
#endif #endif
} }
/* Did we crash? /* Did we crash?
@ -1841,7 +1842,7 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
#ifdef AFL_PERSISTENT_RECORD #ifdef AFL_PERSISTENT_RECORD
store_persistent_record: store_persistent_record:
if (unlikely(retval == FSRV_RUN_CRASH || retval == FSRV_RUN_TMOUT) && if (unlikely(retval == FSRV_RUN_CRASH || retval == FSRV_RUN_TMOUT) &&
unlikely(fsrv->persistent_record)) { unlikely(fsrv->persistent_record)) {
char fn[PATH_MAX]; char fn[PATH_MAX];

View File

@ -31,8 +31,8 @@
/* this lets the source compile without afl-clang-fast/lto */ /* this lets the source compile without afl-clang-fast/lto */
#ifndef __AFL_FUZZ_TESTCASE_LEN #ifndef __AFL_FUZZ_TESTCASE_LEN
#define AFL_COMPAT #define AFL_COMPAT
#include "persistent_replay.h" #include "persistent_replay.h"
#endif #endif
__AFL_FUZZ_INIT(); __AFL_FUZZ_INIT();
@ -86,8 +86,6 @@ int main(int argc, char **argv) {
if (buf[5] == '!') { if (buf[5] == '!') {
printf("six\n"); printf("six\n");
char *nullo = NULL+1;
*nullo = 'p';
abort(); abort();
} }