mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-15 11:28:08 +00:00
replay mode support
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -103,6 +103,7 @@ 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/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.
|
||||||
|
@ -83,7 +83,10 @@
|
|||||||
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 */
|
||||||
|
// #define AFL_PERSISTENT_REPLAY
|
||||||
|
|
||||||
/* 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
|
||||||
|
149
include/persistent_replay.h
Normal file
149
include/persistent_replay.h
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
#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>
|
||||||
|
|
||||||
|
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 **record_arg;
|
||||||
|
static char *replay_record_dir;
|
||||||
|
static struct dirent **record_list;
|
||||||
|
|
||||||
|
static int select_files(const struct dirent *dirbuf) {
|
||||||
|
|
||||||
|
char fn[4096];
|
||||||
|
|
||||||
|
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(int argc, char **argv) {
|
||||||
|
|
||||||
|
char **argp;
|
||||||
|
|
||||||
|
/* 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");
|
||||||
|
replay_record_cnt = scandir(replay_record_dir ? replay_record_dir : "./", &record_list, select_files, compare_files);
|
||||||
|
|
||||||
|
if (!replay_record_cnt){
|
||||||
|
printf("[error] Can't find the requested record!\n");
|
||||||
|
is_replay_record = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
argp = argv;
|
||||||
|
while (*argp){
|
||||||
|
if (!strcmp(*argp, "@@")){
|
||||||
|
record_arg = argp;
|
||||||
|
*record_arg = replay_record_path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++argp;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* only used if explictly included for compatibility
|
||||||
|
compiling without afl-cc */
|
||||||
|
|
||||||
|
#ifdef AFL_COMPAT
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
#define PATH_MAX 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
|
||||||
|
if (record_arg) {
|
||||||
|
*record_arg = tcase;
|
||||||
|
} else {
|
||||||
|
int fd = open(tcase, O_RDONLY);
|
||||||
|
dup2(fd, 0);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (!inited){
|
||||||
|
cycle_cnt = max_cnt;
|
||||||
|
inited = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return cycle_cnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // AFL_COMPAT
|
||||||
|
|
||||||
|
#endif // _HAVE_PERSISTENT_REPLAY_H
|
@ -83,6 +83,10 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_REPLAY
|
||||||
|
#include "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. */
|
||||||
@ -1338,6 +1342,38 @@ 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_REPLAY
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
#define PATH_MAX 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static u8 inited = 0;
|
||||||
|
char tcase[PATH_MAX];
|
||||||
|
|
||||||
|
if( unlikely(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);
|
||||||
|
|
||||||
|
if (record_arg) {
|
||||||
|
*record_arg = tcase;
|
||||||
|
} else {
|
||||||
|
int fd = open(tcase, O_RDONLY);
|
||||||
|
dup2(fd, 0);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
return cycle_cnt--;
|
||||||
|
} else
|
||||||
|
|
||||||
|
#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.
|
||||||
|
@ -1591,6 +1591,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) {
|
||||||
|
|
||||||
@ -1684,7 +1689,7 @@ afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AFL_PERSISTENT_RECORD
|
#ifdef AFL_eERSISTENT_RECORD
|
||||||
// end of persistent loop?
|
// end of persistent loop?
|
||||||
if (unlikely(fsrv->persistent_record &&
|
if (unlikely(fsrv->persistent_record &&
|
||||||
fsrv->persistent_record_pid != fsrv->child_pid)) {
|
fsrv->persistent_record_pid != fsrv->child_pid)) {
|
||||||
@ -1790,8 +1795,14 @@ 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;
|
||||||
return FSRV_RUN_TMOUT;
|
|
||||||
|
|
||||||
|
#ifndef AFL_PERSISTENT_RECORD
|
||||||
|
return FSRV_RUN_TMOUT;
|
||||||
|
#else
|
||||||
|
retval = FSRV_RUN_TMOUT;
|
||||||
|
persistent_out_fmt = "%s/hangs/RECORD:%06u,cnt:%06u";
|
||||||
|
goto store_persistent_record;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Did we crash?
|
/* Did we crash?
|
||||||
@ -1811,49 +1822,59 @@ 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))) {
|
||||||
|
|
||||||
#ifdef AFL_PERSISTENT_RECORD
|
|
||||||
if (unlikely(fsrv->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), "%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
|
|
||||||
|
|
||||||
/* For a proper crash, set last_kill_signal to WTERMSIG, else set it to 0 */
|
/* For a proper crash, set last_kill_signal to WTERMSIG, else set it to 0 */
|
||||||
fsrv->last_kill_signal =
|
fsrv->last_kill_signal =
|
||||||
WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0;
|
WIFSIGNALED(fsrv->child_status) ? WTERMSIG(fsrv->child_status) : 0;
|
||||||
|
|
||||||
|
#ifndef AFL_PERSISTENT_RECORD
|
||||||
return FSRV_RUN_CRASH;
|
return FSRV_RUN_CRASH;
|
||||||
|
#else
|
||||||
|
retval = FSRV_RUN_CRASH;
|
||||||
|
persistent_out_fmt = "%s/crashes/RECORD:%06u,cnt:%06u";
|
||||||
|
goto store_persistent_record;
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* success :) */
|
/* success :) */
|
||||||
return FSRV_RUN_OK;
|
return FSRV_RUN_OK;
|
||||||
|
|
||||||
|
#ifdef AFL_PERSISTENT_RECORD
|
||||||
|
store_persistent_record:
|
||||||
|
if (unlikely(retval == FSRV_RUN_CRASH || retval == FSRV_RUN_TMOUT) &&
|
||||||
|
unlikely(fsrv->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() {
|
||||||
|
@ -1915,6 +1915,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);
|
||||||
|
|
||||||
@ -1947,6 +1950,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);
|
||||||
|
|
||||||
|
@ -2163,7 +2163,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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
all:
|
all:
|
||||||
../../afl-clang-fast -o persistent_demo persistent_demo.c
|
../../afl-clang-fast -o persistent_demo persistent_demo.c
|
||||||
../../afl-clang-fast -o persistent_demo_new persistent_demo_new.c
|
../../afl-clang-fast -o persistent_demo_new persistent_demo_new.c
|
||||||
|
gcc -g -I ../../include -o persistent_demo_new_compat persistent_demo_new.c
|
||||||
AFL_DONT_OPTIMIZE=1 ../../afl-clang-fast -o test-instr test-instr.c
|
AFL_DONT_OPTIMIZE=1 ../../afl-clang-fast -o test-instr test-instr.c
|
||||||
|
|
||||||
document:
|
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
|
||||||
|
@ -31,17 +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
|
||||||
ssize_t fuzz_len;
|
#include "persistent_replay.h"
|
||||||
unsigned char fuzz_buf[1024000];
|
|
||||||
|
|
||||||
#define __AFL_FUZZ_TESTCASE_LEN fuzz_len
|
|
||||||
#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
|
|
||||||
#define __AFL_FUZZ_INIT() void sync(void);
|
|
||||||
#define __AFL_LOOP(x) \
|
|
||||||
((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0)
|
|
||||||
#define __AFL_INIT() sync()
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
__AFL_FUZZ_INIT();
|
__AFL_FUZZ_INIT();
|
||||||
@ -95,6 +86,8 @@ 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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user