mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-15 19:38:09 +00:00
Add unstable coverage support
This commit is contained in:
@ -175,6 +175,9 @@ useful for debugging purposes, e.g. investigating unstable edges.
|
||||
implies `AFL_FRIDA_INST_NO_OPTIMIZE`.
|
||||
* `AFL_FRIDA_INST_TRACE_UNIQUE` - As per `AFL_FRIDA_INST_TRACE`, but each edge
|
||||
is logged only once, requires `AFL_FRIDA_INST_NO_OPTIMIZE`.
|
||||
* `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE` - File to write DynamoRio format
|
||||
coverage information for unstable edges (e.g. to be loaded within IDA
|
||||
lighthouse).
|
||||
* `AFL_FRIDA_OUTPUT_STDOUT` - Redirect the standard output of the target
|
||||
application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`)
|
||||
* `AFL_FRIDA_OUTPUT_STDERR` - Redirect the standard error of the target
|
||||
|
@ -18,6 +18,7 @@
|
||||
js_api_set_instrument_seed;
|
||||
js_api_set_instrument_trace;
|
||||
js_api_set_instrument_trace_unique;
|
||||
js_api_set_instrument_unstable_coverage_file;
|
||||
js_api_set_persistent_address;
|
||||
js_api_set_persistent_count;
|
||||
js_api_set_persistent_debug;
|
||||
|
@ -12,6 +12,7 @@ extern gboolean instrument_optimize;
|
||||
extern gboolean instrument_unique;
|
||||
extern __thread guint64 instrument_previous_pc;
|
||||
extern guint64 instrument_hash_zero;
|
||||
extern char * instrument_coverage_unstable_filename;
|
||||
|
||||
extern gboolean instrument_use_fixed_seed;
|
||||
extern guint64 instrument_fixed_seed;
|
||||
@ -44,6 +45,10 @@ void instrument_coverage_init(void);
|
||||
void instrument_coverage_start(uint64_t address);
|
||||
void instrument_coverage_end(uint64_t address);
|
||||
|
||||
void instrument_coverage_unstable(guint64 edge, guint64 previous_rip,
|
||||
guint64 previous_end, guint64 current_rip,
|
||||
guint64 current_end);
|
||||
|
||||
void instrument_on_fork();
|
||||
|
||||
guint64 instrument_get_offset_hash(GumAddress current_rip);
|
||||
|
@ -29,14 +29,23 @@ guint64 instrument_hash_seed = 0;
|
||||
|
||||
gboolean instrument_use_fixed_seed = FALSE;
|
||||
guint64 instrument_fixed_seed = 0;
|
||||
char * instrument_coverage_unstable_filename = NULL;
|
||||
|
||||
static GumStalkerTransformer *transformer = NULL;
|
||||
|
||||
__thread guint64 instrument_previous_pc = 0;
|
||||
|
||||
static GumAddress previous_rip = 0;
|
||||
static GumAddress previous_end = 0;
|
||||
static u8 * edges_notified = NULL;
|
||||
|
||||
typedef struct {
|
||||
|
||||
GumAddress address;
|
||||
GumAddress end;
|
||||
|
||||
} block_ctx_t;
|
||||
|
||||
static void trace_debug(char *format, ...) {
|
||||
|
||||
va_list ap;
|
||||
@ -91,7 +100,9 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
|
||||
|
||||
UNUSED_PARAMETER(context);
|
||||
|
||||
GumAddress current_rip = GUM_ADDRESS(user_data);
|
||||
block_ctx_t *ctx = (block_ctx_t *)user_data;
|
||||
GumAddress current_rip = ctx->address;
|
||||
guint16 current_end = ctx->end;
|
||||
guint64 current_pc = instrument_get_offset_hash(current_rip);
|
||||
guint64 edge;
|
||||
|
||||
@ -112,10 +123,18 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
|
||||
|
||||
if (instrument_unique) { edges_notified[edge] = 1; }
|
||||
|
||||
previous_rip = current_rip;
|
||||
}
|
||||
|
||||
if (unlikely(instrument_coverage_unstable_filename != NULL)) {
|
||||
|
||||
instrument_coverage_unstable(edge, previous_rip, previous_end, current_rip,
|
||||
current_end);
|
||||
|
||||
}
|
||||
|
||||
previous_rip = current_rip;
|
||||
previous_end = current_end;
|
||||
|
||||
instrument_previous_pc = ((current_pc & (MAP_SIZE - 1) >> 1)) |
|
||||
((current_pc & 0x1) << (MAP_SIZE_POW2 - 1));
|
||||
|
||||
@ -130,6 +149,7 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
|
||||
const cs_insn *instr;
|
||||
gboolean begin = TRUE;
|
||||
gboolean excluded;
|
||||
block_ctx_t * ctx = NULL;
|
||||
|
||||
while (gum_stalker_iterator_next(iterator, &instr)) {
|
||||
|
||||
@ -183,8 +203,9 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
|
||||
|
||||
} else {
|
||||
|
||||
gum_stalker_iterator_put_callout(
|
||||
iterator, on_basic_block, GSIZE_TO_POINTER(instr->address), NULL);
|
||||
ctx = gum_malloc0(sizeof(block_ctx_t));
|
||||
ctx->address = GUM_ADDRESS(instr->address);
|
||||
gum_stalker_iterator_put_callout(iterator, on_basic_block, ctx, NULL);
|
||||
|
||||
}
|
||||
|
||||
@ -211,6 +232,8 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
|
||||
|
||||
}
|
||||
|
||||
if (ctx != NULL) { ctx->end = (instr->address + instr->size); }
|
||||
|
||||
instrument_flush(output);
|
||||
instrument_debug_end(output);
|
||||
instrument_coverage_end(instr->address + instr->size);
|
||||
@ -224,6 +247,8 @@ void instrument_config(void) {
|
||||
instrument_unique = (getenv("AFL_FRIDA_INST_TRACE_UNIQUE") != NULL);
|
||||
instrument_use_fixed_seed = (getenv("AFL_FRIDA_INST_SEED") != NULL);
|
||||
instrument_fixed_seed = util_read_num("AFL_FRIDA_INST_SEED");
|
||||
instrument_coverage_unstable_filename =
|
||||
(getenv("AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE"));
|
||||
|
||||
instrument_debug_config();
|
||||
instrument_coverage_config();
|
||||
@ -241,6 +266,9 @@ void instrument_init(void) {
|
||||
OKF("Instrumentation - unique [%c]", instrument_unique ? 'X' : ' ');
|
||||
OKF("Instrumentation - fixed seed [%c] [0x%016" G_GINT64_MODIFIER "x]",
|
||||
instrument_use_fixed_seed ? 'X' : ' ', instrument_fixed_seed);
|
||||
OKF("Instrumentation - unstable coverage [%c] [%s]",
|
||||
instrument_coverage_unstable_filename == NULL ? ' ' : 'X',
|
||||
instrument_coverage_unstable_filename);
|
||||
|
||||
if (instrument_tracing && instrument_optimize) {
|
||||
|
||||
@ -249,6 +277,13 @@ void instrument_init(void) {
|
||||
|
||||
}
|
||||
|
||||
if (instrument_coverage_unstable_filename && instrument_optimize) {
|
||||
|
||||
WARNF("AFL_FRIDA_INST_COVERAGE_FILE implies AFL_FRIDA_INST_NO_OPTIMIZE");
|
||||
instrument_optimize = FALSE;
|
||||
|
||||
}
|
||||
|
||||
if (instrument_unique && instrument_optimize) {
|
||||
|
||||
WARNF("AFL_FRIDA_INST_TRACE_UNIQUE implies AFL_FRIDA_INST_NO_OPTIMIZE");
|
||||
|
@ -12,14 +12,14 @@
|
||||
|
||||
char *instrument_coverage_filename = NULL;
|
||||
|
||||
static int coverage_fd = -1;
|
||||
static int coverage_pipes[2] = {-1, -1};
|
||||
static uint64_t coverage_last_start = 0;
|
||||
static GHashTable *coverage_hash = NULL;
|
||||
static GArray * coverage_modules = NULL;
|
||||
static GArray * coverage_ranges = NULL;
|
||||
static guint coverage_marked_modules = 0;
|
||||
static guint coverage_marked_entries = 0;
|
||||
static int normal_coverage_fd = -1;
|
||||
static int normal_coverage_pipes[2] = {-1, -1};
|
||||
|
||||
static int unstable_coverage_fd = -1;
|
||||
static int unstable_coverage_pipes[2] = {-1, -1};
|
||||
|
||||
static uint64_t normal_coverage_last_start = 0;
|
||||
static gchar * unstable_coverage_fuzzer_stats = NULL;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -40,7 +40,24 @@ typedef struct {
|
||||
uint64_t end;
|
||||
coverage_range_t *module;
|
||||
|
||||
} coverage_data_t;
|
||||
} normal_coverage_data_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
guint64 edge;
|
||||
guint64 from;
|
||||
guint64 from_end;
|
||||
guint64 to;
|
||||
guint64 to_end;
|
||||
|
||||
} unstable_coverage_data_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
GArray *modules;
|
||||
guint count;
|
||||
|
||||
} coverage_mark_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -53,7 +70,7 @@ typedef struct {
|
||||
static gboolean coverage_range(const GumRangeDetails *details,
|
||||
gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(user_data);
|
||||
GArray * coverage_ranges = (GArray *)user_data;
|
||||
coverage_range_t coverage = {0};
|
||||
|
||||
if (details->file == NULL) { return TRUE; }
|
||||
@ -113,11 +130,36 @@ void instrument_coverage_print(char *format, ...) {
|
||||
|
||||
}
|
||||
|
||||
static void coverage_get_modules(void) {
|
||||
static GArray *coverage_get_ranges(void) {
|
||||
|
||||
instrument_coverage_print("Coverage - Collecting ranges\n");
|
||||
|
||||
GArray *coverage_ranges =
|
||||
g_array_sized_new(false, false, sizeof(coverage_range_t), 100);
|
||||
gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, coverage_range,
|
||||
coverage_ranges);
|
||||
g_array_sort(coverage_ranges, coverage_sort);
|
||||
|
||||
for (guint i = 0; i < coverage_ranges->len; i++) {
|
||||
|
||||
coverage_range_t *range =
|
||||
&g_array_index(coverage_ranges, coverage_range_t, i);
|
||||
instrument_coverage_print("Coverage Range - %3u: 0x%016" G_GINT64_MODIFIER
|
||||
"X - 0x%016" G_GINT64_MODIFIER "X (%s)\n",
|
||||
i, range->base_address, range->limit,
|
||||
range->path);
|
||||
|
||||
}
|
||||
|
||||
return coverage_ranges;
|
||||
|
||||
}
|
||||
|
||||
static GArray *coverage_get_modules(void) {
|
||||
|
||||
instrument_coverage_print("Coverage - Collecting modules\n");
|
||||
|
||||
coverage_modules =
|
||||
GArray *coverage_ranges = coverage_get_ranges();
|
||||
GArray *coverage_modules =
|
||||
g_array_sized_new(false, false, sizeof(coverage_range_t), 100);
|
||||
|
||||
coverage_range_t current = {0};
|
||||
@ -150,6 +192,7 @@ static void coverage_get_modules(void) {
|
||||
}
|
||||
|
||||
if (current.is_executable) { g_array_append_val(coverage_modules, current); }
|
||||
g_array_free(coverage_ranges, TRUE);
|
||||
|
||||
for (guint i = 0; i < coverage_modules->len; i++) {
|
||||
|
||||
@ -162,35 +205,16 @@ static void coverage_get_modules(void) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void coverage_get_ranges(void) {
|
||||
|
||||
instrument_coverage_print("Coverage - Collecting ranges\n");
|
||||
|
||||
coverage_ranges =
|
||||
g_array_sized_new(false, false, sizeof(coverage_range_t), 100);
|
||||
gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, coverage_range, NULL);
|
||||
g_array_sort(coverage_ranges, coverage_sort);
|
||||
|
||||
for (guint i = 0; i < coverage_ranges->len; i++) {
|
||||
|
||||
coverage_range_t *range =
|
||||
&g_array_index(coverage_ranges, coverage_range_t, i);
|
||||
instrument_coverage_print("Coverage Range - %3u: 0x%016" G_GINT64_MODIFIER
|
||||
"X - 0x%016" G_GINT64_MODIFIER "X (%s)\n",
|
||||
i, range->base_address, range->limit,
|
||||
range->path);
|
||||
|
||||
}
|
||||
return coverage_modules;
|
||||
|
||||
}
|
||||
|
||||
static void instrument_coverage_mark(void *key, void *value, void *user_data) {
|
||||
|
||||
UNUSED_PARAMETER(key);
|
||||
UNUSED_PARAMETER(user_data);
|
||||
coverage_data_t *val = (coverage_data_t *)value;
|
||||
coverage_mark_ctx_t * ctx = (coverage_mark_ctx_t *)user_data;
|
||||
GArray * coverage_modules = ctx->modules;
|
||||
normal_coverage_data_t *val = (normal_coverage_data_t *)value;
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < coverage_modules->len; i++) {
|
||||
@ -202,7 +226,7 @@ static void instrument_coverage_mark(void *key, void *value, void *user_data) {
|
||||
if (val->end >= module->limit) break;
|
||||
|
||||
val->module = module;
|
||||
coverage_marked_entries++;
|
||||
ctx->count = ctx->count + 1;
|
||||
module->count++;
|
||||
return;
|
||||
|
||||
@ -223,7 +247,7 @@ static void coverage_write(void *data, size_t size) {
|
||||
for (char *cursor = (char *)data; remain > 0;
|
||||
remain -= written, cursor += written) {
|
||||
|
||||
written = write(coverage_fd, cursor, remain);
|
||||
written = write(normal_coverage_fd, cursor, remain);
|
||||
|
||||
if (written < 0) {
|
||||
|
||||
@ -254,7 +278,7 @@ static void coverage_format(char *format, ...) {
|
||||
|
||||
}
|
||||
|
||||
static void coverage_write_modules() {
|
||||
static void coverage_write_modules(GArray *coverage_modules) {
|
||||
|
||||
guint emitted = 0;
|
||||
for (guint i = 0; i < coverage_modules->len; i++) {
|
||||
@ -283,7 +307,7 @@ static void coverage_write_events(void *key, void *value, void *user_data) {
|
||||
|
||||
UNUSED_PARAMETER(key);
|
||||
UNUSED_PARAMETER(user_data);
|
||||
coverage_data_t *val = (coverage_data_t *)value;
|
||||
normal_coverage_data_t *val = (normal_coverage_data_t *)value;
|
||||
|
||||
if (val->module == NULL) { return; }
|
||||
|
||||
@ -299,7 +323,7 @@ static void coverage_write_events(void *key, void *value, void *user_data) {
|
||||
|
||||
}
|
||||
|
||||
static void coverage_write_header() {
|
||||
static void coverage_write_header(guint coverage_marked_modules) {
|
||||
|
||||
char version[] = "DRCOV VERSION: 2\n";
|
||||
char flavour[] = "DRCOV FLAVOR: frida\n";
|
||||
@ -309,14 +333,12 @@ static void coverage_write_header() {
|
||||
coverage_format("Module Table: version 2, count %u\n",
|
||||
coverage_marked_modules);
|
||||
coverage_write(columns, sizeof(columns) - 1);
|
||||
coverage_write_modules();
|
||||
coverage_format("BB Table: %u bbs\n", coverage_marked_entries);
|
||||
g_hash_table_foreach(coverage_hash, coverage_write_events, NULL);
|
||||
|
||||
}
|
||||
|
||||
static void coverage_mark_modules() {
|
||||
static guint coverage_mark_modules(GArray *coverage_modules) {
|
||||
|
||||
guint coverage_marked_modules = 0;
|
||||
guint i;
|
||||
for (i = 0; i < coverage_modules->len; i++) {
|
||||
|
||||
@ -336,29 +358,40 @@ static void coverage_mark_modules() {
|
||||
|
||||
}
|
||||
|
||||
return coverage_marked_modules;
|
||||
|
||||
}
|
||||
|
||||
static void instrument_coverage_run() {
|
||||
static void instrument_coverage_normal_run() {
|
||||
|
||||
int bytes;
|
||||
coverage_data_t data;
|
||||
coverage_data_t *value;
|
||||
normal_coverage_data_t data;
|
||||
normal_coverage_data_t *value;
|
||||
instrument_coverage_print("Coverage - Running\n");
|
||||
|
||||
if (close(coverage_pipes[STDOUT_FILENO]) != 0) {
|
||||
if (close(normal_coverage_pipes[STDOUT_FILENO]) != 0) {
|
||||
|
||||
FATAL("Failed to close parent read pipe");
|
||||
|
||||
}
|
||||
|
||||
for (bytes =
|
||||
read(coverage_pipes[STDIN_FILENO], &data, sizeof(coverage_data_t));
|
||||
bytes == sizeof(coverage_data_t);
|
||||
bytes =
|
||||
read(coverage_pipes[STDIN_FILENO], &data, sizeof(coverage_data_t))) {
|
||||
GHashTable *coverage_hash =
|
||||
g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
|
||||
if (coverage_hash == NULL) {
|
||||
|
||||
value = (coverage_data_t *)gum_malloc0(sizeof(coverage_data_t));
|
||||
memcpy(value, &data, sizeof(coverage_data_t));
|
||||
FATAL("Failed to g_hash_table_new, errno: %d", errno);
|
||||
|
||||
}
|
||||
|
||||
for (bytes = read(normal_coverage_pipes[STDIN_FILENO], &data,
|
||||
sizeof(normal_coverage_data_t));
|
||||
bytes == sizeof(normal_coverage_data_t);
|
||||
bytes = read(normal_coverage_pipes[STDIN_FILENO], &data,
|
||||
sizeof(normal_coverage_data_t))) {
|
||||
|
||||
value =
|
||||
(normal_coverage_data_t *)gum_malloc0(sizeof(normal_coverage_data_t));
|
||||
memcpy(value, &data, sizeof(normal_coverage_data_t));
|
||||
g_hash_table_insert(coverage_hash, GSIZE_TO_POINTER(data.start), value);
|
||||
|
||||
}
|
||||
@ -367,21 +400,252 @@ static void instrument_coverage_run() {
|
||||
|
||||
instrument_coverage_print("Coverage - Preparing\n");
|
||||
|
||||
coverage_get_ranges();
|
||||
coverage_get_modules();
|
||||
GArray *coverage_modules = coverage_get_modules();
|
||||
|
||||
guint size = g_hash_table_size(coverage_hash);
|
||||
instrument_coverage_print("Coverage - Total Entries: %u\n", size);
|
||||
|
||||
g_hash_table_foreach(coverage_hash, instrument_coverage_mark, NULL);
|
||||
instrument_coverage_print("Coverage - Marked Entries: %u\n",
|
||||
coverage_marked_entries);
|
||||
coverage_mark_ctx_t ctx = {.modules = coverage_modules, .count = 0};
|
||||
|
||||
coverage_mark_modules();
|
||||
g_hash_table_foreach(coverage_hash, instrument_coverage_mark, &ctx);
|
||||
instrument_coverage_print("Coverage - Marked Entries: %u\n", ctx.count);
|
||||
|
||||
guint coverage_marked_modules = coverage_mark_modules(coverage_modules);
|
||||
instrument_coverage_print("Coverage - Marked Modules: %u\n",
|
||||
coverage_marked_modules);
|
||||
|
||||
coverage_write_header();
|
||||
coverage_write_header(coverage_marked_modules);
|
||||
coverage_write_modules(coverage_modules);
|
||||
coverage_format("BB Table: %u bbs\n", ctx.count);
|
||||
g_hash_table_foreach(coverage_hash, coverage_write_events, NULL);
|
||||
|
||||
g_hash_table_unref(coverage_hash);
|
||||
|
||||
instrument_coverage_print("Coverage - Completed\n");
|
||||
|
||||
}
|
||||
|
||||
static GArray *instrument_coverage_unstable_read_unstable_ids(void) {
|
||||
|
||||
gchar * contents = NULL;
|
||||
gsize length = 0;
|
||||
GArray *unstable_edge_ids =
|
||||
g_array_sized_new(false, false, sizeof(gpointer), 100);
|
||||
|
||||
if (!g_file_get_contents(unstable_coverage_fuzzer_stats, &contents, &length,
|
||||
NULL)) {
|
||||
|
||||
FATAL("Failed to read fuzzer_stats");
|
||||
|
||||
}
|
||||
|
||||
instrument_coverage_print("\n");
|
||||
instrument_coverage_print("Unstable coverage stats:\n");
|
||||
instrument_coverage_print("========================\n");
|
||||
instrument_coverage_print("%s\n", contents);
|
||||
instrument_coverage_print("\n");
|
||||
|
||||
gchar **lines = g_strsplit(contents, "\n", -1);
|
||||
gchar **values = NULL;
|
||||
|
||||
for (guint i = 0; lines[i] != NULL; i++) {
|
||||
|
||||
gchar **fields = g_strsplit(lines[i], ":", 2);
|
||||
if (fields[0] == NULL) {
|
||||
|
||||
g_strfreev(fields);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
g_strstrip(fields[0]);
|
||||
if (g_strcmp0(fields[0], "var_bytes") != 0) {
|
||||
|
||||
g_strfreev(fields);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (fields[1] == NULL) {
|
||||
|
||||
g_strfreev(fields);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
g_strstrip(fields[1]);
|
||||
values = g_strsplit(fields[1], " ", -1);
|
||||
g_strfreev(fields);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (values == NULL) {
|
||||
|
||||
instrument_coverage_print(
|
||||
"Failed to find var_bytes, did you set AFL_DEBUG?\n");
|
||||
|
||||
}
|
||||
|
||||
for (guint i = 0; values[i] != NULL; i++) {
|
||||
|
||||
g_strstrip(values[i]);
|
||||
gpointer val = GSIZE_TO_POINTER(g_ascii_strtoull(values[i], NULL, 10));
|
||||
g_array_append_val(unstable_edge_ids, val);
|
||||
|
||||
}
|
||||
|
||||
g_strfreev(values);
|
||||
g_strfreev(lines);
|
||||
g_free(contents);
|
||||
|
||||
for (guint i = 0; i < unstable_edge_ids->len; i++) {
|
||||
|
||||
gpointer *id = &g_array_index(unstable_edge_ids, gpointer, i);
|
||||
|
||||
instrument_coverage_print("Unstable edge (%10u): %" G_GINT64_MODIFIER "u\n",
|
||||
i, GPOINTER_TO_SIZE(*id));
|
||||
|
||||
}
|
||||
|
||||
return unstable_edge_ids;
|
||||
|
||||
}
|
||||
|
||||
static GHashTable *instrument_collect_unstable_blocks(
|
||||
GHashTable *unstable_coverage_hash, GArray *unstable_edge_ids) {
|
||||
|
||||
GHashTable *unstable_blocks =
|
||||
g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
|
||||
|
||||
for (guint i = 0; i < unstable_edge_ids->len; i++) {
|
||||
|
||||
gpointer *id = &g_array_index(unstable_edge_ids, gpointer, i);
|
||||
|
||||
GHashTable *child =
|
||||
(GHashTable *)g_hash_table_lookup(unstable_coverage_hash, *id);
|
||||
|
||||
if (child == NULL) { FATAL("Failed to find edge ID"); }
|
||||
|
||||
GHashTableIter iter = {0};
|
||||
gpointer value;
|
||||
g_hash_table_iter_init(&iter, child);
|
||||
while (g_hash_table_iter_next(&iter, NULL, &value)) {
|
||||
|
||||
unstable_coverage_data_t *unstable = (unstable_coverage_data_t *)value;
|
||||
normal_coverage_data_t * from =
|
||||
gum_malloc0(sizeof(normal_coverage_data_t));
|
||||
normal_coverage_data_t *to = gum_malloc0(sizeof(normal_coverage_data_t));
|
||||
from->start = unstable->from;
|
||||
from->end = unstable->from_end;
|
||||
from->module = NULL;
|
||||
|
||||
to->start = unstable->to;
|
||||
to->end = unstable->to_end;
|
||||
to->module = NULL;
|
||||
|
||||
g_hash_table_insert(unstable_blocks, GSIZE_TO_POINTER(from->start), from);
|
||||
g_hash_table_insert(unstable_blocks, GSIZE_TO_POINTER(to->start), to);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return unstable_blocks;
|
||||
|
||||
}
|
||||
|
||||
static void instrument_coverage_unstable_run(void) {
|
||||
|
||||
int bytes;
|
||||
unstable_coverage_data_t data;
|
||||
unstable_coverage_data_t *value;
|
||||
instrument_coverage_print("Unstable coverage - Running\n");
|
||||
|
||||
if (close(unstable_coverage_pipes[STDOUT_FILENO]) != 0) {
|
||||
|
||||
FATAL("Failed to close parent read pipe");
|
||||
|
||||
}
|
||||
|
||||
GHashTable *unstable_coverage_hash = g_hash_table_new_full(
|
||||
g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)g_hash_table_unref);
|
||||
if (unstable_coverage_hash == NULL) {
|
||||
|
||||
FATAL("Failed to g_hash_table_new, errno: %d", errno);
|
||||
|
||||
}
|
||||
|
||||
guint edges = 0;
|
||||
|
||||
for (bytes = read(unstable_coverage_pipes[STDIN_FILENO], &data,
|
||||
sizeof(unstable_coverage_data_t));
|
||||
bytes == sizeof(unstable_coverage_data_t);
|
||||
bytes = read(unstable_coverage_pipes[STDIN_FILENO], &data,
|
||||
sizeof(unstable_coverage_data_t))) {
|
||||
|
||||
value = (unstable_coverage_data_t *)gum_malloc0(
|
||||
sizeof(unstable_coverage_data_t));
|
||||
memcpy(value, &data, sizeof(unstable_coverage_data_t));
|
||||
|
||||
gpointer hash_value = g_hash_table_lookup(unstable_coverage_hash,
|
||||
GSIZE_TO_POINTER(value->edge));
|
||||
if (hash_value == NULL) {
|
||||
|
||||
hash_value =
|
||||
g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
|
||||
|
||||
if (!g_hash_table_insert(unstable_coverage_hash,
|
||||
GSIZE_TO_POINTER(value->edge), hash_value)) {
|
||||
|
||||
FATAL("Entry already in hashtable");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (g_hash_table_insert(hash_value, GSIZE_TO_POINTER(value->from), value)) {
|
||||
|
||||
edges++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (bytes != 0) { FATAL("Unstable coverage data truncated"); }
|
||||
|
||||
instrument_coverage_print("Coverage - Preparing\n");
|
||||
|
||||
GArray *coverage_modules = coverage_get_modules();
|
||||
|
||||
instrument_coverage_print("Found edges: %u\n", edges);
|
||||
|
||||
GArray *unstable_edge_ids = instrument_coverage_unstable_read_unstable_ids();
|
||||
|
||||
GHashTable *unstable_blocks = instrument_collect_unstable_blocks(
|
||||
unstable_coverage_hash, unstable_edge_ids);
|
||||
|
||||
guint size = g_hash_table_size(unstable_blocks);
|
||||
instrument_coverage_print("Unstable blocks: %u\n", size);
|
||||
|
||||
coverage_mark_ctx_t ctx = {.modules = coverage_modules, .count = 0};
|
||||
|
||||
g_hash_table_foreach(unstable_blocks, instrument_coverage_mark, &ctx);
|
||||
instrument_coverage_print("Coverage - Marked Entries: %u\n", ctx.count);
|
||||
|
||||
guint coverage_marked_modules = coverage_mark_modules(coverage_modules);
|
||||
instrument_coverage_print("Coverage - Marked Modules: %u\n",
|
||||
coverage_marked_modules);
|
||||
|
||||
coverage_write_header(coverage_marked_modules);
|
||||
coverage_write_modules(coverage_modules);
|
||||
coverage_format("BB Table: %u bbs\n", ctx.count);
|
||||
g_hash_table_foreach(unstable_blocks, coverage_write_events, NULL);
|
||||
|
||||
g_hash_table_unref(unstable_blocks);
|
||||
g_array_free(unstable_edge_ids, TRUE);
|
||||
g_hash_table_unref(unstable_coverage_hash);
|
||||
|
||||
instrument_coverage_print("Coverage - Completed\n");
|
||||
|
||||
@ -393,7 +657,7 @@ void instrument_coverage_config(void) {
|
||||
|
||||
}
|
||||
|
||||
void instrument_coverage_init(void) {
|
||||
void instrument_coverage_normal_init(void) {
|
||||
|
||||
OKF("Coverage - enabled [%c]",
|
||||
instrument_coverage_filename == NULL ? ' ' : 'X');
|
||||
@ -407,19 +671,147 @@ void instrument_coverage_init(void) {
|
||||
|
||||
OKF("Coverage - path [%s]", path);
|
||||
|
||||
coverage_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
|
||||
normal_coverage_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||||
|
||||
if (coverage_fd < 0) { FATAL("Failed to open coverage file '%s'", path); }
|
||||
if (normal_coverage_fd < 0) {
|
||||
|
||||
FATAL("Failed to open coverage file '%s'", path);
|
||||
|
||||
}
|
||||
|
||||
g_free(path);
|
||||
|
||||
if (pipe(coverage_pipes) != 0) { FATAL("Failed to create pipes"); }
|
||||
if (pipe(normal_coverage_pipes) != 0) { FATAL("Failed to create pipes"); }
|
||||
|
||||
coverage_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
|
||||
if (coverage_hash == NULL) {
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) { FATAL("Failed to start coverage process"); }
|
||||
|
||||
FATAL("Failed to g_hash_table_new, errno: %d", errno);
|
||||
if (pid == 0) {
|
||||
|
||||
instrument_coverage_normal_run();
|
||||
kill(getpid(), SIGKILL);
|
||||
_exit(0);
|
||||
|
||||
}
|
||||
|
||||
if (close(normal_coverage_fd) < 0) {
|
||||
|
||||
FATAL("Failed to close coverage output file");
|
||||
|
||||
}
|
||||
|
||||
if (close(normal_coverage_pipes[STDIN_FILENO]) != 0) {
|
||||
|
||||
FATAL("Failed to close parent read pipe");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void instrument_coverage_unstable_find_output(void) {
|
||||
|
||||
pid_t parent = getpid();
|
||||
gchar *fds_name = g_strdup_printf("/proc/%d/fd/", getppid());
|
||||
|
||||
gchar *root = g_file_read_link("/proc/self/root", NULL);
|
||||
if (root == NULL) { FATAL("Failed to read link"); }
|
||||
|
||||
GDir *dir = g_dir_open(fds_name, 0, NULL);
|
||||
|
||||
OKF("Coverage Unstable - fds: %s", fds_name);
|
||||
|
||||
for (const gchar *filename = g_dir_read_name(dir); filename != NULL;
|
||||
filename = g_dir_read_name(dir)) {
|
||||
|
||||
gchar *fullname = g_build_path("/", fds_name, filename, NULL);
|
||||
|
||||
gchar *link = g_file_read_link(fullname, NULL);
|
||||
if (link == NULL) { FATAL("Failed to read link: %s", fullname); }
|
||||
|
||||
gchar *basename = g_path_get_basename(link);
|
||||
if (g_strcmp0(basename, "default") != 0) {
|
||||
|
||||
g_free(basename);
|
||||
g_free(link);
|
||||
g_free(fullname);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
gchar *relative = NULL;
|
||||
size_t root_len = strnlen(root, PATH_MAX);
|
||||
if (g_str_has_suffix(link, root)) {
|
||||
|
||||
relative = g_build_path("/", &link[root_len], NULL);
|
||||
|
||||
} else {
|
||||
|
||||
relative = g_build_path("/", link, NULL);
|
||||
|
||||
}
|
||||
|
||||
gchar *cmdline = g_build_path("/", relative, "cmdline", NULL);
|
||||
if (!g_file_test(cmdline, G_FILE_TEST_EXISTS)) {
|
||||
|
||||
g_free(cmdline);
|
||||
g_free(basename);
|
||||
g_free(relative);
|
||||
g_free(link);
|
||||
g_free(fullname);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
unstable_coverage_fuzzer_stats =
|
||||
g_build_path("/", relative, "fuzzer_stats", NULL);
|
||||
g_free(cmdline);
|
||||
g_free(basename);
|
||||
g_free(relative);
|
||||
g_free(link);
|
||||
g_free(fullname);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
g_dir_close(dir);
|
||||
g_free(fds_name);
|
||||
|
||||
if (unstable_coverage_fuzzer_stats == NULL) {
|
||||
|
||||
FATAL("Failed to find fuzzer stats");
|
||||
|
||||
}
|
||||
|
||||
OKF("Fuzzer stats: %s", unstable_coverage_fuzzer_stats);
|
||||
|
||||
}
|
||||
|
||||
void instrument_coverage_unstable_init(void) {
|
||||
|
||||
if (instrument_coverage_unstable_filename == NULL) { return; }
|
||||
|
||||
char *path = g_canonicalize_filename(instrument_coverage_unstable_filename,
|
||||
g_get_current_dir());
|
||||
|
||||
OKF("Coverage - unstable path [%s]", instrument_coverage_unstable_filename);
|
||||
|
||||
unstable_coverage_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
||||
|
||||
if (unstable_coverage_fd < 0) {
|
||||
|
||||
FATAL("Failed to open unstable coverage file '%s'", path);
|
||||
|
||||
}
|
||||
|
||||
g_free(path);
|
||||
|
||||
instrument_coverage_unstable_find_output();
|
||||
|
||||
if (pipe(unstable_coverage_pipes) != 0) {
|
||||
|
||||
FATAL("Failed to create unstable pipes");
|
||||
|
||||
}
|
||||
|
||||
@ -428,15 +820,19 @@ void instrument_coverage_init(void) {
|
||||
|
||||
if (pid == 0) {
|
||||
|
||||
instrument_coverage_run();
|
||||
instrument_coverage_unstable_run();
|
||||
kill(getpid(), SIGKILL);
|
||||
_exit(0);
|
||||
|
||||
}
|
||||
|
||||
if (close(coverage_fd) < 0) { FATAL("Failed to close coverage output file"); }
|
||||
if (close(unstable_coverage_fd) < 0) {
|
||||
|
||||
if (close(coverage_pipes[STDIN_FILENO]) != 0) {
|
||||
FATAL("Failed to close unstable coverage output file");
|
||||
|
||||
}
|
||||
|
||||
if (close(unstable_coverage_pipes[STDIN_FILENO]) != 0) {
|
||||
|
||||
FATAL("Failed to close parent read pipe");
|
||||
|
||||
@ -444,11 +840,18 @@ void instrument_coverage_init(void) {
|
||||
|
||||
}
|
||||
|
||||
void instrument_coverage_init(void) {
|
||||
|
||||
instrument_coverage_normal_init();
|
||||
instrument_coverage_unstable_init();
|
||||
|
||||
}
|
||||
|
||||
void instrument_coverage_start(uint64_t address) {
|
||||
|
||||
if (instrument_coverage_filename == NULL) { return; }
|
||||
|
||||
coverage_last_start = address;
|
||||
normal_coverage_last_start = address;
|
||||
|
||||
}
|
||||
|
||||
@ -456,12 +859,12 @@ void instrument_coverage_end(uint64_t address) {
|
||||
|
||||
if (instrument_coverage_filename == NULL) { return; }
|
||||
|
||||
coverage_data_t data = {
|
||||
normal_coverage_data_t data = {
|
||||
|
||||
.start = coverage_last_start, .end = address, .module = NULL};
|
||||
.start = normal_coverage_last_start, .end = address, .module = NULL};
|
||||
|
||||
if (write(coverage_pipes[STDOUT_FILENO], &data, sizeof(coverage_data_t)) !=
|
||||
sizeof(coverage_data_t)) {
|
||||
if (write(normal_coverage_pipes[STDOUT_FILENO], &data,
|
||||
sizeof(normal_coverage_data_t)) != sizeof(normal_coverage_data_t)) {
|
||||
|
||||
FATAL("Coverage I/O error");
|
||||
|
||||
@ -469,3 +872,26 @@ void instrument_coverage_end(uint64_t address) {
|
||||
|
||||
}
|
||||
|
||||
void instrument_coverage_unstable(guint64 edge, guint64 previous_rip,
|
||||
guint64 previous_end, guint64 current_rip,
|
||||
guint64 current_end) {
|
||||
|
||||
if (instrument_coverage_unstable_filename == NULL) { return; }
|
||||
unstable_coverage_data_t data = {
|
||||
|
||||
.edge = edge,
|
||||
.from = previous_rip,
|
||||
.from_end = previous_end,
|
||||
.to = current_rip,
|
||||
.to_end = current_end};
|
||||
|
||||
if (write(unstable_coverage_pipes[STDOUT_FILENO], &data,
|
||||
sizeof(unstable_coverage_data_t)) !=
|
||||
sizeof(unstable_coverage_data_t)) {
|
||||
|
||||
FATAL("Unstable coverage I/O error");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -137,6 +137,14 @@ class Afl {
|
||||
static setInstrumentTracingUnique() {
|
||||
Afl.jsApiSetInstrumentTraceUnique();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE`. This function takes a single
|
||||
* `string` as an argument.
|
||||
*/
|
||||
static setInstrumentUnstableCoverageFile(file) {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
Afl.jsApiSetInstrumentUnstableCoverageFile(buf);
|
||||
}
|
||||
/**
|
||||
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
|
||||
* `NativePointer` should be provided as it's argument.
|
||||
@ -255,6 +263,7 @@ Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_n
|
||||
Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]);
|
||||
Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []);
|
||||
Afl.jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction("js_api_set_instrument_trace_unique", "void", []);
|
||||
Afl.jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_unstable_coverage_file", "void", ["pointer"]);
|
||||
Afl.jsApiSetPersistentAddress = Afl.jsApiGetFunction("js_api_set_persistent_address", "void", ["pointer"]);
|
||||
Afl.jsApiSetPersistentCount = Afl.jsApiGetFunction("js_api_set_persistent_count", "void", ["uint64"]);
|
||||
Afl.jsApiSetPersistentDebug = Afl.jsApiGetFunction("js_api_set_persistent_debug", "void", []);
|
||||
|
@ -164,6 +164,13 @@ __attribute__((visibility("default"))) void js_api_set_instrument_trace_unique(
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void
|
||||
js_api_set_instrument_unstable_coverage_file(char *path) {
|
||||
|
||||
instrument_coverage_unstable_filename = g_strdup(path);
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_stdout(char *file) {
|
||||
|
||||
output_stdout = g_strdup(file);
|
||||
|
@ -85,6 +85,21 @@ frida: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE)
|
||||
-- \
|
||||
$(UNSTABLE_BIN) @@
|
||||
|
||||
frida_coverage: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE)
|
||||
AFL_DEBUG=1 \
|
||||
AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
|
||||
AFL_FRIDA_OUTPUT_STDOUT=/tmp/stdout.txt \
|
||||
AFL_FRIDA_OUTPUT_STDERR=/tmp/stderr.txt \
|
||||
AFL_FRIDA_INST_COVERAGE_FILE=/tmp/coverage.dat \
|
||||
AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE=/tmp/unstable.dat \
|
||||
$(ROOT)afl-fuzz \
|
||||
-D \
|
||||
-O \
|
||||
-i $(UNSTABLE_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(UNSTABLE_BIN) @@
|
||||
|
||||
debug:
|
||||
gdb \
|
||||
--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
|
||||
|
@ -163,6 +163,15 @@ class Afl {
|
||||
Afl.jsApiSetInstrumentTraceUnique();
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE`. This function takes a single
|
||||
* `string` as an argument.
|
||||
*/
|
||||
public static setInstrumentUnstableCoverageFile(file: string): void {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
Afl.jsApiSetInstrumentUnstableCoverageFile(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
|
||||
* `NativePointer` should be provided as it's argument.
|
||||
@ -338,6 +347,11 @@ class Afl {
|
||||
"void",
|
||||
[]);
|
||||
|
||||
private static readonly jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction(
|
||||
"js_api_set_instrument_unstable_coverage_file",
|
||||
"void",
|
||||
["pointer"]);
|
||||
|
||||
private static readonly jsApiSetPersistentAddress = Afl.jsApiGetFunction(
|
||||
"js_api_set_persistent_address",
|
||||
"void",
|
||||
|
@ -65,6 +65,7 @@ static char *afl_environment_variables[] = {
|
||||
"AFL_FRIDA_INST_SEED",
|
||||
"AFL_FRIDA_INST_TRACE",
|
||||
"AFL_FRIDA_INST_TRACE_UNIQUE",
|
||||
"AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE",
|
||||
"AFL_FRIDA_JS_SCRIPT",
|
||||
"AFL_FRIDA_OUTPUT_STDOUT",
|
||||
"AFL_FRIDA_OUTPUT_STDERR",
|
||||
|
Reference in New Issue
Block a user