mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-10 17:21:33 +00:00
commit
42c663e7c7
4
.gitignore
vendored
4
.gitignore
vendored
@ -103,6 +103,10 @@ utils/optimin/build
|
|||||||
utils/optimin/optimin
|
utils/optimin/optimin
|
||||||
utils/persistent_mode/persistent_demo
|
utils/persistent_mode/persistent_demo
|
||||||
utils/persistent_mode/persistent_demo_new
|
utils/persistent_mode/persistent_demo_new
|
||||||
|
utils/persistent_mode/persistent_demo_new_compat
|
||||||
utils/persistent_mode/test-instr
|
utils/persistent_mode/test-instr
|
||||||
|
utils/replay_record/persistent_demo_replay
|
||||||
|
utils/replay_record/persistent_demo_replay_compat
|
||||||
|
utils/replay_record/persistent_demo_replay_argparse
|
||||||
utils/plot_ui/afl-plot-ui
|
utils/plot_ui/afl-plot-ui
|
||||||
vuln_prog
|
vuln_prog
|
||||||
|
@ -124,6 +124,10 @@
|
|||||||
#define CASE_PREFIX "id_"
|
#define CASE_PREFIX "id_"
|
||||||
#endif /* ^!SIMPLE_FILES */
|
#endif /* ^!SIMPLE_FILES */
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_RECORD
|
||||||
|
#define RECORD_PREFIX "RECORD:"
|
||||||
|
#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 */
|
||||||
|
|
||||||
// Little helper to access the ptr to afl->##name_buf - for use in afl_realloc.
|
// Little helper to access the ptr to afl->##name_buf - for use in afl_realloc.
|
||||||
|
131
include/afl-persistent-replay.h
Normal file
131
include/afl-persistent-replay.h
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#ifndef _HAVE_PERSISTENT_REPLAY_H
|
||||||
|
#define _HAVE_PERSISTENT_REPLAY_H
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
#define PATH_MAX 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static unsigned short int is_replay_record;
|
||||||
|
static unsigned int replay_record;
|
||||||
|
static unsigned int replay_record_cnt;
|
||||||
|
static char replay_record_path[PATH_MAX];
|
||||||
|
static char *replay_record_dir;
|
||||||
|
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) {
|
||||||
|
|
||||||
|
char fn[PATH_MAX];
|
||||||
|
|
||||||
|
if (dirbuf->d_name[0] == '.') {
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
snprintf(fn, sizeof(fn), "RECORD:%06u", replay_record);
|
||||||
|
return !!strstr(dirbuf->d_name, fn);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare_files(const struct dirent **da, const struct dirent **db) {
|
||||||
|
|
||||||
|
unsigned int c1 = 0, c2 = 0;
|
||||||
|
|
||||||
|
sscanf((*da)->d_name, "RECORD:%*u,cnt:%06u", &c1);
|
||||||
|
sscanf((*db)->d_name, "RECORD:%*u,cnt:%06u", &c2);
|
||||||
|
|
||||||
|
return c1 - c2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((destructor)) static void __afl_record_replay_destroy(void) {
|
||||||
|
|
||||||
|
for (int i = 0; i < replay_record_cnt; i++) {
|
||||||
|
|
||||||
|
free(record_list[i]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
free(record_list);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void __afl_record_replay_init(
|
||||||
|
#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
|
||||||
|
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"))) {
|
||||||
|
|
||||||
|
// printf("[warning] AFL_PERSISTENT_REPLAY not set.\n");
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
replay_record = atoi(getenv("AFL_PERSISTENT_REPLAY"));
|
||||||
|
replay_record_dir = getenv("AFL_PERSISTENT_DIR");
|
||||||
|
|
||||||
|
if (!(stat(replay_record_dir, &sb) == 0 && S_ISDIR(sb.st_mode))) {
|
||||||
|
|
||||||
|
fprintf(stderr, "[error] Can't find the requested record directory!\n");
|
||||||
|
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;
|
||||||
|
while (*argp) {
|
||||||
|
|
||||||
|
if (!strcmp(*argp, "@@")) {
|
||||||
|
|
||||||
|
record_arg = argp;
|
||||||
|
*record_arg = replay_record_path;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
++argp;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // AFL_PERSISTENT_REPLAY_ARGPARSE
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _HAVE_PERSISTENT_REPLAY_H
|
||||||
|
|
67
include/afl-record-compat.h
Normal file
67
include/afl-record-compat.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#ifndef _HAVE_AFL_COMPAT_H
|
||||||
|
#define _HAVE_AFL_COMPAT_H
|
||||||
|
|
||||||
|
#include <afl-persistent-replay.h>
|
||||||
|
|
||||||
|
#define FUZZ_BUF_SIZE 1024000
|
||||||
|
|
||||||
|
// extern ssize_t read(int fildes, void *buf, size_t nbyte);
|
||||||
|
|
||||||
|
// extern int __afl_persistent_loop(unsigned int max_cnt);
|
||||||
|
// extern unsigned char fuzz_buf[];
|
||||||
|
|
||||||
|
#ifndef __AFL_HAVE_MANUAL_CONTROL
|
||||||
|
#define __AFL_HAVE_MANUAL_CONTROL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __AFL_FUZZ_TESTCASE_LEN (read(0, fuzz_buf, FUZZ_BUF_SIZE))
|
||||||
|
#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
|
||||||
|
#define __AFL_FUZZ_INIT() void sync(void);
|
||||||
|
#define __AFL_INIT() sync()
|
||||||
|
#define __AFL_LOOP(x) __afl_persistent_loop(x)
|
||||||
|
|
||||||
|
unsigned char fuzz_buf[FUZZ_BUF_SIZE];
|
||||||
|
|
||||||
|
int __afl_persistent_loop(unsigned int max_cnt) {
|
||||||
|
|
||||||
|
static unsigned int cycle_cnt = 1;
|
||||||
|
static unsigned short int inited = 0;
|
||||||
|
char tcase[PATH_MAX];
|
||||||
|
|
||||||
|
if (is_replay_record) {
|
||||||
|
|
||||||
|
if (!inited) {
|
||||||
|
|
||||||
|
cycle_cnt = replay_record_cnt;
|
||||||
|
inited = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return --cycle_cnt;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _HAVE_AFL_COMPAT_H
|
||||||
|
|
@ -97,6 +97,11 @@
|
|||||||
|
|
||||||
// #define AFL_PERSISTENT_RECORD
|
// #define AFL_PERSISTENT_RECORD
|
||||||
|
|
||||||
|
/* 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
|
||||||
* ALWAYS_COLORED The env var. AFL_NO_COLOR will have no effect
|
* ALWAYS_COLORED The env var. AFL_NO_COLOR will have no effect
|
||||||
|
@ -195,4 +195,34 @@ 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).
|
||||||
|
|
||||||
|
When `AFL_PERSISTENT_RECORD` is enabled, replay functionality is also included in the compiler-rt library. To replay a specific record, assign the record number to the AFL_PERSISTENT_REPLAY environment variable (i.e., `RECORD:XXXXX`` -> `AFL_PERSISTENT_REPLAY=XXXXX`), and run the test binary as you would normally do.
|
||||||
|
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 include support by enabling `AFL_PERSISTENT_ARGPARSE` in `config.h`.
|
||||||
|
|
||||||
|
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 persistent loop replay replacement
|
||||||
|
|
||||||
|
To use the replay functionality without having to use `afl-cc`, include the [include/record_compat.h](../include/afl-record_compat.h) header file. Together with the [include/afl-persistent-replay.h](../include/afl-persistent-replay.h) header included in it, `afl-record-compat.h` provides a drop-in replacement for the persistent loop mechanism.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#ifndef __AFL_FUZZ_TESTCASE_LEN
|
||||||
|
// #define AFL_PERSISTENT_REPLAY_ARGPARSE
|
||||||
|
#include "afl-record-compat.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__AFL_FUZZ_INIT();
|
||||||
|
```
|
||||||
|
|
||||||
|
A simple example is provided in [persistent_demo_replay.c](../utils/replay_record/persistent_demo_replay.c).
|
||||||
|
|
||||||
|
Be aware that the [afl-record-compat.h](../include/afl-record-compat.h) header should only be included in a single compilation unit, or you will end up with clobbered functions and variables.
|
||||||
|
|
||||||
|
If you need a cleaner solution, you'll have to move the functions and variables defined in [include/record_compat.h](../include/afl-record-compat.h) and [include/afl-persistent-replay.h](../include/afl-persistent-replay.h) in a C file, and add the relevant declarations to a header file. After including the new header file, the compilation unit resulting from compiling the C file can then be linked with your program.
|
@ -87,6 +87,10 @@ __attribute__((weak)) void __sanitizer_symbolize_pc(void *, const char *fmt,
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_RECORD
|
||||||
|
#include "afl-persistent-replay.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Globals needed by the injected instrumentation. The __afl_area_initial region
|
/* Globals needed by the injected instrumentation. The __afl_area_initial region
|
||||||
is used for instrumentation output before __afl_map_shm() has a chance to
|
is used for instrumentation output before __afl_map_shm() has a chance to
|
||||||
run. It will end up as .comm, so it shouldn't be too wasteful. */
|
run. It will end up as .comm, so it shouldn't be too wasteful. */
|
||||||
@ -1354,6 +1358,10 @@ int __afl_persistent_loop(unsigned int max_cnt) {
|
|||||||
static u8 first_pass = 1;
|
static u8 first_pass = 1;
|
||||||
static u32 cycle_cnt;
|
static u32 cycle_cnt;
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_RECORD
|
||||||
|
char tcase[PATH_MAX];
|
||||||
|
#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.
|
||||||
@ -1365,14 +1373,59 @@ int __afl_persistent_loop(unsigned int max_cnt) {
|
|||||||
__afl_area_ptr[0] = 1;
|
__afl_area_ptr[0] = 1;
|
||||||
memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T));
|
memset(__afl_prev_loc, 0, NGRAM_SIZE_MAX * sizeof(PREV_LOC_T));
|
||||||
|
|
||||||
cycle_cnt = max_cnt;
|
|
||||||
first_pass = 0;
|
first_pass = 0;
|
||||||
__afl_selective_coverage_temp = 1;
|
__afl_selective_coverage_temp = 1;
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_RECORD
|
||||||
|
if (unlikely(is_replay_record)) {
|
||||||
|
|
||||||
|
cycle_cnt = replay_record_cnt;
|
||||||
|
goto persistent_record;
|
||||||
|
|
||||||
|
} else
|
||||||
|
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
|
||||||
|
cycle_cnt = max_cnt;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
} else if (--cycle_cnt) {
|
} else if (--cycle_cnt) {
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_RECORD
|
||||||
|
if (unlikely(is_replay_record)) {
|
||||||
|
|
||||||
|
persistent_record:
|
||||||
|
|
||||||
|
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 (unlikely(record_arg)) {
|
||||||
|
|
||||||
|
*record_arg = tcase;
|
||||||
|
|
||||||
|
} else
|
||||||
|
|
||||||
|
#endif // AFL_PERSISTENT_REPLAY_ARGPARSE
|
||||||
|
{
|
||||||
|
|
||||||
|
int fd = open(tcase, O_RDONLY);
|
||||||
|
dup2(fd, 0);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
raise(SIGSTOP);
|
raise(SIGSTOP);
|
||||||
|
|
||||||
__afl_area_ptr[0] = 1;
|
__afl_area_ptr[0] = 1;
|
||||||
|
@ -1599,6 +1599,11 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
|||||||
u32 exec_ms;
|
u32 exec_ms;
|
||||||
u32 write_value = fsrv->last_run_timed_out;
|
u32 write_value = fsrv->last_run_timed_out;
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_RECORD
|
||||||
|
fsrv_run_result_t retval = FSRV_RUN_OK;
|
||||||
|
char *persistent_out_fmt;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
if (fsrv->nyx_mode) {
|
if (fsrv->nyx_mode) {
|
||||||
|
|
||||||
@ -1798,6 +1803,18 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
|||||||
if (unlikely(fsrv->last_run_timed_out)) {
|
if (unlikely(fsrv->last_run_timed_out)) {
|
||||||
|
|
||||||
fsrv->last_kill_signal = fsrv->child_kill_signal;
|
fsrv->last_kill_signal = fsrv->child_kill_signal;
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_RECORD
|
||||||
|
if (unlikely(fsrv->persistent_record)) {
|
||||||
|
|
||||||
|
retval = FSRV_RUN_TMOUT;
|
||||||
|
persistent_out_fmt = "%s/hangs/RECORD:%06u,cnt:%06u";
|
||||||
|
goto store_persistent_record;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
return FSRV_RUN_TMOUT;
|
return FSRV_RUN_TMOUT;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1819,42 +1836,21 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
|||||||
(fsrv->uses_crash_exitcode &&
|
(fsrv->uses_crash_exitcode &&
|
||||||
WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode))) {
|
WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode))) {
|
||||||
|
|
||||||
|
/* For a proper crash, set last_kill_signal to WTERMSIG, else set it to 0 */
|
||||||
|
fsrv->last_kill_signal =
|
||||||
|
WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0;
|
||||||
|
|
||||||
#ifdef AFL_PERSISTENT_RECORD
|
#ifdef AFL_PERSISTENT_RECORD
|
||||||
if (unlikely(fsrv->persistent_record)) {
|
if (unlikely(fsrv->persistent_record)) {
|
||||||
|
|
||||||
char fn[PATH_MAX];
|
retval = FSRV_RUN_CRASH;
|
||||||
u32 i, writecnt = 0;
|
persistent_out_fmt = "%s/crashes/RECORD:%06u,cnt:%06u";
|
||||||
for (i = 0; i < fsrv->persistent_record; ++i) {
|
goto store_persistent_record;
|
||||||
|
|
||||||
u32 entry = (i + fsrv->persistent_record_idx) % fsrv->persistent_record;
|
|
||||||
u8 *data = fsrv->persistent_record_data[entry];
|
|
||||||
u32 len = fsrv->persistent_record_len[entry];
|
|
||||||
if (likely(len && data)) {
|
|
||||||
|
|
||||||
snprintf(fn, sizeof(fn), "%s/RECORD:%06u,cnt:%06u",
|
|
||||||
fsrv->persistent_record_dir, fsrv->persistent_record_cnt,
|
|
||||||
writecnt++);
|
|
||||||
int fd = open(fn, O_CREAT | O_TRUNC | O_WRONLY, 0644);
|
|
||||||
if (fd >= 0) {
|
|
||||||
|
|
||||||
ck_write(fd, data, len, fn);
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
++fsrv->persistent_record_cnt;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* For a proper crash, set last_kill_signal to WTERMSIG, else set it to 0 */
|
|
||||||
fsrv->last_kill_signal =
|
|
||||||
WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0;
|
|
||||||
return FSRV_RUN_CRASH;
|
return FSRV_RUN_CRASH;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1862,6 +1858,40 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
|||||||
/* success :) */
|
/* success :) */
|
||||||
return FSRV_RUN_OK;
|
return FSRV_RUN_OK;
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_RECORD
|
||||||
|
store_persistent_record: {
|
||||||
|
|
||||||
|
char fn[PATH_MAX];
|
||||||
|
u32 i, writecnt = 0;
|
||||||
|
for (i = 0; i < fsrv->persistent_record; ++i) {
|
||||||
|
|
||||||
|
u32 entry = (i + fsrv->persistent_record_idx) % fsrv->persistent_record;
|
||||||
|
u8 *data = fsrv->persistent_record_data[entry];
|
||||||
|
u32 len = fsrv->persistent_record_len[entry];
|
||||||
|
if (likely(len && data)) {
|
||||||
|
|
||||||
|
snprintf(fn, sizeof(fn), persistent_out_fmt, fsrv->persistent_record_dir,
|
||||||
|
fsrv->persistent_record_cnt, writecnt++);
|
||||||
|
int fd = open(fn, O_CREAT | O_TRUNC | O_WRONLY, 0644);
|
||||||
|
if (fd >= 0) {
|
||||||
|
|
||||||
|
ck_write(fd, data, len, fn);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
++fsrv->persistent_record_cnt;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void afl_fsrv_killall() {
|
void afl_fsrv_killall() {
|
||||||
|
@ -1921,6 +1921,9 @@ static void handle_existing_out_dir(afl_state_t *afl) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_RECORD
|
||||||
|
delete_files(fn, RECORD_PREFIX);
|
||||||
|
#endif
|
||||||
if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
|
if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
|
||||||
ck_free(fn);
|
ck_free(fn);
|
||||||
|
|
||||||
@ -1953,6 +1956,9 @@ static void handle_existing_out_dir(afl_state_t *afl) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_RECORD
|
||||||
|
delete_files(fn, RECORD_PREFIX);
|
||||||
|
#endif
|
||||||
if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
|
if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
|
||||||
ck_free(fn);
|
ck_free(fn);
|
||||||
|
|
||||||
|
@ -2182,7 +2182,7 @@ int main(int argc, char **argv_orig, char **envp) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
afl->fsrv.persistent_record_dir = alloc_printf("%s/crashes", afl->out_dir);
|
afl->fsrv.persistent_record_dir = alloc_printf("%s", afl->out_dir);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,4 +7,4 @@ document:
|
|||||||
AFL_DONT_OPTIMIZE=1 ../../afl-clang-fast -D_AFL_DOCUMENT_MUTATIONS -o test-instr test-instr.c
|
AFL_DONT_OPTIMIZE=1 ../../afl-clang-fast -D_AFL_DOCUMENT_MUTATIONS -o test-instr test-instr.c
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f persistent_demo persistent_demo_new test-instr
|
rm -f persistent_demo persistent_demo_new persistent_demo_new_compat test-instr
|
||||||
|
8
utils/replay_record/Makefile
Normal file
8
utils/replay_record/Makefile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
all:
|
||||||
|
test `grep '//[\s\t ]*#define[\s\t ]*AFL_PERSISTENT_RECORD' ../../include/config.h | wc -l` -eq 0 || (echo "AFL_PERSISTENT_RECORD must be enabled in config.h"; exit 1)
|
||||||
|
../../afl-clang-fast -o persistent_demo_replay persistent_demo_replay.c
|
||||||
|
${CC} -I ../../include -o persistent_demo_replay_compat persistent_demo_replay.c
|
||||||
|
${CC} -g -I ../../include -DAFL_PERSISTENT_REPLAY_ARGPARSE -o persistent_demo_replay_argparse persistent_demo_replay.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f persistent_demo_replay persistent_demo_replay_argparse persistent_demo_replay_compat
|
10
utils/replay_record/README.md
Normal file
10
utils/replay_record/README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# AFL++ persistent record replay
|
||||||
|
|
||||||
|
This persistent record replay demo showcases the `AFL_PERSISTENT_RECORD` replay functionality.
|
||||||
|
|
||||||
|
The [Makefile](Makefile) will produce three binaries:
|
||||||
|
+ persistent_demo_replay: uses afl-cc and makes use of the replay functionality included in the compiler runtime library
|
||||||
|
+ persistent_demo_replay_compat: uses the [afl-record-compat.h](../../include/afl-record-compat.h) compatibility header to compile the same example without `afl-cc`
|
||||||
|
+ persistent_demo_replay_argparse: makes use of `afl-record-compat.h`, and the Makefile defines `AFL_PERSISTENT_REPLAY_ARGPARSE` to test the replay functionality but parses the input file via a command-line argument (`@@`-style harness).
|
||||||
|
|
||||||
|
For more information see [README.persistent_mode.md](../../instrumentation/README.persistent_mode.md).
|
148
utils/replay_record/persistent_demo_replay.c
Normal file
148
utils/replay_record/persistent_demo_replay.c
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
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>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* this lets the source compile without afl-clang-fast/lto */
|
||||||
|
#ifndef __AFL_FUZZ_TESTCASE_LEN
|
||||||
|
#include "afl-record-compat.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__AFL_FUZZ_INIT();
|
||||||
|
|
||||||
|
/* Main entry point. */
|
||||||
|
|
||||||
|
/* To ensure checks are not optimized out it is recommended to disable
|
||||||
|
code optimization for the fuzzer harness main() */
|
||||||
|
#pragma clang optimize off
|
||||||
|
#pragma GCC optimize("O0")
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
|
||||||
|
ssize_t len; /* how much input did we read? */
|
||||||
|
unsigned char *buf; /* test case buffer pointer */
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (argc < 2) { printf("Need an input file!"); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
|
||||||
|
__AFL_INIT();
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
|
||||||
|
buf = malloc(1000);
|
||||||
|
#else
|
||||||
|
buf = __AFL_FUZZ_TESTCASE_BUF; // this must be assigned before __AFL_LOOP!
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (__AFL_LOOP(UINT_MAX)) { // increase if you have good stability
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
|
||||||
|
fd = open(argv[1], O_RDONLY);
|
||||||
|
len = read(fd, buf, 1000);
|
||||||
|
close(fd);
|
||||||
|
#else
|
||||||
|
len = __AFL_FUZZ_TESTCASE_LEN; // do not use the macro directly in a call!
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// fprintf(stderr, "input: %zd \"%s\"\n", len, buf);
|
||||||
|
|
||||||
|
/* do we have enough data? */
|
||||||
|
if (len < 8) continue;
|
||||||
|
|
||||||
|
if (strcmp((char *)buf, "thisisateststring") == 0) printf("teststring\n");
|
||||||
|
|
||||||
|
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[5] == '!') {
|
||||||
|
|
||||||
|
printf("six\n");
|
||||||
|
abort();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (buf[5] == 'O') {
|
||||||
|
|
||||||
|
// hang
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
continue;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user