aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDominik Maier <domenukk@gmail.com>2021-06-07 20:49:23 +0200
committerGitHub <noreply@github.com>2021-06-07 20:49:23 +0200
commit63ee9df54f3c9db8ecbb0fb6186ea912200d9126 (patch)
treeab5424a8bc6e7a210c11cdf0fe9fcd63017f2124 /src
parent2d2d9b0b77c17d1fbfc9b1fc223c4ff62e11b84d (diff)
downloadafl++-63ee9df54f3c9db8ecbb0fb6186ea912200d9126.tar.gz
Forkserver for afl-analyze (#963)
* afl-analyze forkserver * added missing vars to forkserver * synchronized a bit more with afl-tmin * more debugging, runs now, but need to suppress target output * fix dev/null setting * afl-analyze info: Co-authored-by: hexcoder- <heiko@hexco.de>
Diffstat (limited to 'src')
-rw-r--r--src/afl-analyze.c235
1 files changed, 81 insertions, 154 deletions
diff --git a/src/afl-analyze.c b/src/afl-analyze.c
index d43278b9..606254d9 100644
--- a/src/afl-analyze.c
+++ b/src/afl-analyze.c
@@ -55,12 +55,7 @@
#include <sys/types.h>
#include <sys/resource.h>
-static s32 child_pid; /* PID of the tested program */
-
-static u8 *trace_bits; /* SHM with instrumentation bitmap */
-
-static u8 *in_file, /* Analyzer input test case */
- *prog_in; /* Targeted program input file */
+static u8 *in_file; /* Analyzer input test case */
static u8 *in_data; /* Input data for analysis */
@@ -73,20 +68,19 @@ static u64 orig_cksum; /* Original checksum */
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
-static s32 dev_null_fd = -1; /* FD to /dev/null */
-
static bool edges_only, /* Ignore hit counts? */
use_hex_offsets, /* Show hex offsets? */
use_stdin = true; /* Use stdin for program input? */
-static volatile u8 stop_soon, /* Ctrl-C pressed? */
- child_timed_out; /* Child timed out? */
+static volatile u8 stop_soon; /* Ctrl-C pressed? */
static u8 *target_path;
static u8 frida_mode;
static u8 qemu_mode;
static u32 map_size = MAP_SIZE;
+static afl_forkserver_t fsrv = {0}; /* The forkserver */
+
/* Constants used for describing byte behavior. */
#define RESP_NONE 0x00 /* Changing byte is a no-op. */
@@ -156,7 +150,7 @@ static void classify_counts(u8 *mem) {
static inline u8 anything_set(void) {
- u32 *ptr = (u32 *)trace_bits;
+ u32 *ptr = (u32 *)fsrv.trace_bits;
u32 i = (map_size >> 2);
while (i--) {
@@ -173,7 +167,7 @@ static inline u8 anything_set(void) {
static void at_exit_handler(void) {
- unlink(prog_in); /* Ignore errors */
+ unlink(fsrv.out_file); /* Ignore errors */
}
@@ -205,128 +199,29 @@ static void read_initial_file(void) {
}
-/* Write output file. */
-
-static s32 write_to_file(u8 *path, u8 *mem, u32 len) {
-
- s32 ret;
-
- unlink(path); /* Ignore errors */
-
- ret = open(path, O_RDWR | O_CREAT | O_EXCL, DEFAULT_PERMISSION);
-
- if (ret < 0) { PFATAL("Unable to create '%s'", path); }
-
- ck_write(ret, mem, len, path);
-
- lseek(ret, 0, SEEK_SET);
-
- return ret;
-
-}
-
-/* Handle timeout signal. */
-
-static void handle_timeout(int sig) {
-
- (void)sig;
-
- child_timed_out = 1;
-
- if (child_pid > 0) kill(child_pid, SIGKILL);
-
-}
-
/* Execute target application. Returns exec checksum, or 0 if program
times out. */
-static u32 analyze_run_target(char **argv, u8 *mem, u32 len, u8 first_run) {
-
- static struct itimerval it;
- int status = 0;
-
- s32 prog_in_fd;
- u64 cksum;
-
- memset(trace_bits, 0, map_size);
- MEM_BARRIER();
-
- prog_in_fd = write_to_file(prog_in, mem, len);
-
- child_pid = fork();
-
- if (child_pid < 0) { PFATAL("fork() failed"); }
-
- if (!child_pid) {
-
- struct rlimit r;
-
- if (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 ||
- dup2(dev_null_fd, 1) < 0 || dup2(dev_null_fd, 2) < 0) {
-
- *(u32 *)trace_bits = EXEC_FAIL_SIG;
- PFATAL("dup2() failed");
-
- }
-
- close(dev_null_fd);
- close(prog_in_fd);
-
- if (mem_limit) {
+static u32 analyze_run_target(u8 *mem, u32 len, u8 first_run) {
- r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20;
+ afl_fsrv_write_to_testcase(&fsrv, mem, len);
+ fsrv_run_result_t ret = afl_fsrv_run_target(&fsrv, exec_tmout, &stop_soon);
-#ifdef RLIMIT_AS
+ if (ret == FSRV_RUN_ERROR) {
- setrlimit(RLIMIT_AS, &r); /* Ignore errors */
+ FATAL("Error in forkserver");
-#else
-
- setrlimit(RLIMIT_DATA, &r); /* Ignore errors */
-
-#endif /* ^RLIMIT_AS */
-
- }
+ } else if (ret == FSRV_RUN_NOINST) {
- r.rlim_max = r.rlim_cur = 0;
- setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
+ FATAL("Target not instrumented");
- execv(target_path, argv);
+ } else if (ret == FSRV_RUN_NOBITS) {
- *(u32 *)trace_bits = EXEC_FAIL_SIG;
- exit(0);
+ FATAL("Failed to run target");
}
- close(prog_in_fd);
-
- /* Configure timeout, wait for child, cancel timeout. */
-
- child_timed_out = 0;
- it.it_value.tv_sec = (exec_tmout / 1000);
- it.it_value.tv_usec = (exec_tmout % 1000) * 1000;
-
- setitimer(ITIMER_REAL, &it, NULL);
-
- if (waitpid(child_pid, &status, 0) <= 0) { FATAL("waitpid() failed"); }
-
- child_pid = 0;
- it.it_value.tv_sec = 0;
- it.it_value.tv_usec = 0;
-
- setitimer(ITIMER_REAL, &it, NULL);
-
- MEM_BARRIER();
-
- /* Clean up bitmap, analyze exit condition, etc. */
-
- if (*(u32 *)trace_bits == EXEC_FAIL_SIG) {
-
- FATAL("Unable to execute '%s'", argv[0]);
-
- }
-
- classify_counts(trace_bits);
+ classify_counts(fsrv.trace_bits);
total_execs++;
if (stop_soon) {
@@ -338,21 +233,19 @@ static u32 analyze_run_target(char **argv, u8 *mem, u32 len, u8 first_run) {
/* Always discard inputs that time out. */
- if (child_timed_out) {
+ if (fsrv.last_run_timed_out) {
exec_hangs++;
return 0;
}
- cksum = hash64(trace_bits, map_size, HASH_CONST);
+ u64 cksum = hash64(fsrv.trace_bits, fsrv.map_size, HASH_CONST);
- /* We don't actually care if the target is crashing or not,
- except that when it does, the checksum should be different. */
+ if (ret == FSRV_RUN_CRASH) {
- if (WIFSIGNALED(status) ||
- (WIFEXITED(status) && WEXITSTATUS(status) == MSAN_ERROR) ||
- (WIFEXITED(status) && WEXITSTATUS(status))) {
+ /* We don't actually care if the target is crashing or not,
+ except that when it does, the checksum should be different. */
cksum ^= 0xffffffff;
@@ -616,7 +509,7 @@ static void dump_hex(u32 len, u8 *b_data) {
/* Actually analyze! */
-static void analyze(char **argv) {
+static void analyze() {
u32 i;
u32 boring_len = 0, prev_xff = 0, prev_x01 = 0, prev_s10 = 0, prev_a10 = 0;
@@ -642,16 +535,16 @@ static void analyze(char **argv) {
code. */
in_data[i] ^= 0xff;
- xor_ff = analyze_run_target(argv, in_data, in_len, 0);
+ xor_ff = analyze_run_target(in_data, in_len, 0);
in_data[i] ^= 0xfe;
- xor_01 = analyze_run_target(argv, in_data, in_len, 0);
+ xor_01 = analyze_run_target(in_data, in_len, 0);
in_data[i] = (in_data[i] ^ 0x01) - 0x10;
- sub_10 = analyze_run_target(argv, in_data, in_len, 0);
+ sub_10 = analyze_run_target(in_data, in_len, 0);
in_data[i] += 0x20;
- add_10 = analyze_run_target(argv, in_data, in_len, 0);
+ add_10 = analyze_run_target(in_data, in_len, 0);
in_data[i] -= 0x10;
/* Classify current behavior. */
@@ -724,7 +617,7 @@ static void handle_stop_sig(int sig) {
(void)sig;
stop_soon = 1;
- if (child_pid > 0) { kill(child_pid, SIGKILL); }
+ afl_fsrv_killall();
}
@@ -736,10 +629,10 @@ static void set_up_environment(char **argv) {
char *afl_preload;
char *frida_afl_preload = NULL;
- dev_null_fd = open("/dev/null", O_RDWR);
- if (dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); }
+ fsrv.dev_null_fd = open("/dev/null", O_RDWR);
+ if (fsrv.dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); }
- if (!prog_in) {
+ if (!fsrv.out_file) {
u8 *use_dir = ".";
@@ -750,10 +643,15 @@ static void set_up_environment(char **argv) {
}
- prog_in = alloc_printf("%s/.afl-analyze-temp-%u", use_dir, (u32)getpid());
+ fsrv.out_file = alloc_printf("%s/.afl-analyze-temp-%u", use_dir, (u32)getpid());
}
+ unlink(fsrv.out_file);
+ fsrv.out_fd = open(fsrv.out_file, O_RDWR | O_CREAT | O_EXCL, DEFAULT_PERMISSION);
+
+ if (fsrv.out_fd < 0) { PFATAL("Unable to create '%s'", fsrv.out_file); }
+
/* Set sane defaults... */
x = get_afl_env("ASAN_OPTIONS");
@@ -916,11 +814,6 @@ static void setup_signal_handlers(void) {
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
- /* Exec timeout notifications. */
-
- sa.sa_handler = handle_timeout;
- sigaction(SIGALRM, &sa, NULL);
-
}
/* Display usage hints. */
@@ -982,6 +875,8 @@ int main(int argc, char **argv_orig, char **envp) {
SAYF(cCYA "afl-analyze" VERSION cRST " by Michal Zalewski\n");
+ afl_fsrv_init(&fsrv);
+
while ((opt = getopt(argc, argv, "+i:f:m:t:eOQUWh")) > 0) {
switch (opt) {
@@ -994,9 +889,9 @@ int main(int argc, char **argv_orig, char **envp) {
case 'f':
- if (prog_in) { FATAL("Multiple -f options not supported"); }
- use_stdin = 0;
- prog_in = optarg;
+ if (fsrv.out_file) { FATAL("Multiple -f options not supported"); }
+ fsrv.use_stdin = 0;
+ fsrv.out_file = ck_strdup(optarg);
break;
case 'e':
@@ -1017,6 +912,7 @@ int main(int argc, char **argv_orig, char **envp) {
if (!strcmp(optarg, "none")) {
mem_limit = 0;
+ fsrv.mem_limit = 0;
break;
}
@@ -1055,6 +951,8 @@ int main(int argc, char **argv_orig, char **envp) {
}
+ fsrv.mem_limit = mem_limit;
+
}
break;
@@ -1074,6 +972,8 @@ int main(int argc, char **argv_orig, char **envp) {
}
+ fsrv.exec_tmout = exec_tmout;
+
break;
case 'O': /* FRIDA mode */
@@ -1081,6 +981,7 @@ int main(int argc, char **argv_orig, char **envp) {
if (frida_mode) { FATAL("Multiple -O options not supported"); }
frida_mode = 1;
+ fsrv.frida_mode = frida_mode;
break;
@@ -1090,6 +991,8 @@ int main(int argc, char **argv_orig, char **envp) {
if (!mem_limit_given) { mem_limit = MEM_LIMIT_QEMU; }
qemu_mode = 1;
+ fsrv.mem_limit = mem_limit;
+ fsrv.qemu_mode = qemu_mode;
break;
case 'U':
@@ -1098,6 +1001,7 @@ int main(int argc, char **argv_orig, char **envp) {
if (!mem_limit_given) { mem_limit = MEM_LIMIT_UNICORN; }
unicorn_mode = 1;
+ fsrv.mem_limit = mem_limit;
break;
case 'W': /* Wine+QEMU mode */
@@ -1107,6 +1011,8 @@ int main(int argc, char **argv_orig, char **envp) {
use_wine = 1;
if (!mem_limit_given) { mem_limit = 0; }
+ fsrv.qemu_mode = qemu_mode;
+ fsrv.mem_limit = mem_limit;
break;
@@ -1125,6 +1031,7 @@ int main(int argc, char **argv_orig, char **envp) {
if (optind == argc || !in_file) { usage(argv[0]); }
map_size = get_map_size();
+ fsrv.map_size = map_size;
use_hex_offsets = !!get_afl_env("AFL_ANALYZE_HEX");
@@ -1134,14 +1041,15 @@ int main(int argc, char **argv_orig, char **envp) {
/* initialize cmplog_mode */
shm.cmplog_mode = 0;
- trace_bits = afl_shm_init(&shm, map_size, 0);
+
atexit(at_exit_handler);
setup_signal_handlers();
set_up_environment(argv);
- target_path = find_binary(argv[optind]);
- detect_file_args(argv + optind, prog_in, &use_stdin);
+ fsrv.target_path = find_binary(argv[optind]);
+ fsrv.trace_bits = afl_shm_init(&shm, map_size, 0);
+ detect_file_args(argv + optind, fsrv.out_file, &use_stdin);
if (qemu_mode) {
@@ -1165,14 +1073,31 @@ int main(int argc, char **argv_orig, char **envp) {
SAYF("\n");
+ if (getenv("AFL_FORKSRV_INIT_TMOUT")) {
+
+ s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT"));
+ if (forksrv_init_tmout < 1) {
+
+ FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT");
+
+ }
+
+ fsrv.init_tmout = (u32)forksrv_init_tmout;
+
+ }
+
+ fsrv.kill_signal =
+ parse_afl_kill_signal_env(getenv("AFL_KILL_SIGNAL"), SIGKILL);
+
read_initial_file();
ACTF("Performing dry run (mem limit = %llu MB, timeout = %u ms%s)...",
mem_limit, exec_tmout, edges_only ? ", edges only" : "");
- analyze_run_target(use_argv, in_data, in_len, 1);
+ afl_fsrv_start(&fsrv, use_argv, &stop_soon, false);
+ analyze_run_target(in_data, in_len, 1);
- if (child_timed_out) {
+ if (fsrv.last_run_timed_out) {
FATAL("Target binary times out (adjusting -t may help).");
@@ -1184,13 +1109,15 @@ int main(int argc, char **argv_orig, char **envp) {
}
- analyze(use_argv);
+ analyze();
OKF("We're done here. Have a nice day!\n");
- if (target_path) { ck_free(target_path); }
-
afl_shm_deinit(&shm);
+ afl_fsrv_deinit(&fsrv);
+ if (fsrv.target_path) { ck_free(fsrv.target_path); }
+ if (in_data) { ck_free(in_data); }
+
exit(0);