diff options
-rw-r--r-- | include/afl-fuzz.h | 11 | ||||
-rw-r--r-- | include/config.h | 12 | ||||
-rw-r--r-- | include/envs.h | 2 | ||||
-rw-r--r-- | src/afl-fuzz-run.c | 1 | ||||
-rw-r--r-- | src/afl-fuzz-stats.c | 10 | ||||
-rw-r--r-- | src/afl-fuzz-statsd.c | 156 |
6 files changed, 191 insertions, 1 deletions
diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 9e469864..427e1aec 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -65,6 +65,8 @@ #include <dlfcn.h> #include <sched.h> +#include <netdb.h> + #include <sys/wait.h> #include <sys/time.h> #ifndef USEMMAP @@ -76,6 +78,7 @@ #include <sys/mman.h> #include <sys/ioctl.h> #include <sys/file.h> +#include <sys/types.h> #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ defined(__NetBSD__) || defined(__DragonFly__) @@ -630,6 +633,8 @@ typedef struct afl_state { u64 plot_prev_qc, plot_prev_uc, plot_prev_uh, plot_prev_ed; u64 stats_last_stats_ms, stats_last_plot_ms, stats_last_ms, stats_last_execs; + // StatsD + u64 statsd_last_send_ms; double stats_avg_exec; u8 *clean_trace; @@ -950,6 +955,12 @@ void maybe_update_plot_file(afl_state_t *, double, double); void show_stats(afl_state_t *); void show_init_stats(afl_state_t *); +/* StatsD */ + +int statsd_socket_init(char *host, int port); +int statsd_send_metric(afl_state_t *afl); +int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen); + /* Run */ fsrv_run_result_t fuzz_run_target(afl_state_t *, afl_forkserver_t *fsrv, u32); diff --git a/include/config.h b/include/config.h index 86285944..c0a04565 100644 --- a/include/config.h +++ b/include/config.h @@ -41,6 +41,18 @@ #define USE_COLOR +/* Enable sending statistics over a StatsD daemon. +Server config can be adjusted with AFL_STATSD_HOST and AFL_STATSD_PORT env var. +*/ + +#define USE_STATSD +#define STATSD_UPDATE_SEC 1 +#define STATSD_DEFAULT_PORT 8125 +#define STATSD_DEFAULT_HOST "127.0.0.1" + +/* comment out to disable tags. */ +#define USE_DOGSTATSD_TAGS + /* If you want to have the original afl internal memory corruption checks. Disabled by default for speed. it is better to use "make ASAN_BUILD=1". */ diff --git a/include/envs.h b/include/envs.h index 3a06aa2a..1fc9e83d 100644 --- a/include/envs.h +++ b/include/envs.h @@ -135,6 +135,8 @@ static char *afl_environment_variables[] = { "AFL_SKIP_BIN_CHECK", "AFL_SKIP_CPUFREQ", "AFL_SKIP_CRASHES", + "AFL_STATSD_HOST", + "AFL_STATSD_PORT", "AFL_TMIN_EXACT", "AFL_TMPDIR", "AFL_TOKEN_FILE", diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index ee22b0f6..5e74dff3 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -903,7 +903,6 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { afl->stage_cur + 1 == afl->stage_max) { show_stats(afl); - } return 0; diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 1d5b169d..2f1e3367 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -423,6 +423,16 @@ void show_stats(afl_state_t *afl) { } + #ifdef USE_STATSD + if (cur_ms - afl->statsd_last_send_ms > STATSD_UPDATE_SEC * 1000) { + /* reset counter, even if send failed. */ + afl->statsd_last_send_ms = cur_ms; + if(statsd_send_metric(afl)){ + WARNF("coundln't send statsd metric."); + } + } + #endif + /* Every now and then, write plot data. */ if (cur_ms - afl->stats_last_plot_ms > PLOT_UPDATE_SEC * 1000) { diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c new file mode 100644 index 00000000..298138be --- /dev/null +++ b/src/afl-fuzz-statsd.c @@ -0,0 +1,156 @@ +#include <stdio.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <string.h> +#include <sys/types.h> +#include <netdb.h> +#include <unistd.h> +#include "afl-fuzz.h" + + +#define MAX_STATSD_PACKET_SIZE 4096 +#define MAX_TAG_LEN 200 +#define METRIC_PREFIX "fuzzing" + +struct sockaddr_in server; +int error = 0; +int statds_sock = 0; + +int statsd_socket_init(char *host, int port){ + int sock; + if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1){ + perror("socket"); + exit(1); + } + + memset(&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_port = htons(port); + + struct addrinfo *result; + struct addrinfo hints; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + if ( (error = getaddrinfo(host, NULL, &hints, &result)) ) { + perror("getaddrinfo"); + exit(1); + } + + memcpy(&(server.sin_addr), &((struct sockaddr_in*)result->ai_addr)->sin_addr, sizeof(struct in_addr)); + freeaddrinfo(result); + + return sock; +} + +int statsd_send_metric(afl_state_t *afl){ + + char buff[MAX_STATSD_PACKET_SIZE] = {0}; + /* Default port and host. + Will be overwritten by AFL_STATSD_PORT and AFL_STATSD_HOST environment variable, if they exists. + */ + u16 port = STATSD_DEFAULT_PORT; + char* host = STATSD_DEFAULT_HOST; + + char* port_env; + char* host_env; + if ((port_env = getenv("AFL_STATSD_PORT")) != NULL) { + port = atoi(port_env); + } + if ((host_env = getenv("AFL_STATSD_HOST")) != NULL) { + host = host_env; + } + + /* statds_sock is a global variable. We set it once in the beginning and reuse the socket. + If the sendto later fail, we reset it to 0 to be able to recreate it. + */ + if(!statds_sock){ + statds_sock = statsd_socket_init(host, port); + if(!statds_sock){ + perror("Cannot create socket"); + return -1; + } + } + + statsd_format_metric(afl, buff, MAX_STATSD_PACKET_SIZE); + if (sendto(statds_sock, buff, strlen(buff), 0, (struct sockaddr *)&server, sizeof(server)) == -1) { + if(!close(statds_sock)){ + perror("Cannot close socket"); + } + statds_sock = 0; + perror("Cannot sendto"); + return -1; + } + + return 0; +} + +int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen){ + /* Metric format: + <some.namespaced.name>:<value>|<type> + */ + #ifdef USE_DOGSTATSD_TAGS + /* Tags format: DogStatsD + <some.namespaced.name>:<value>|<type>|#key:value,key:value,key + */ + char tags[MAX_TAG_LEN * 2] = {0}; + snprintf(tags, MAX_TAG_LEN * 2, + "|#banner:%s,afl_version:%s", + afl->use_banner, + VERSION); + #else + /* No tags. + */ + char *tags = ""; + #endif + /* Sends multiple metrics with one UDP Packet. + bufflen will limit to the max safe size. + */ + snprintf(buff, bufflen, + METRIC_PREFIX".cycle_done:%llu|g%s\n" + METRIC_PREFIX".cycles_wo_finds:%llu|g%s\n" + METRIC_PREFIX".execs_done:%llu|g%s\n" + METRIC_PREFIX".execs_per_sec:%0.02f|g%s\n" + METRIC_PREFIX".paths_total:%u|g%s\n" + METRIC_PREFIX".paths_favored:%u|g%s\n" + METRIC_PREFIX".paths_found:%u|g%s\n" + METRIC_PREFIX".paths_imported:%u|g%s\n" + METRIC_PREFIX".max_depth:%u|g%s\n" + METRIC_PREFIX".cur_path:%u|g%s\n" + METRIC_PREFIX".pending_favs:%u|g%s\n" + METRIC_PREFIX".pending_total:%u|g%s\n" + METRIC_PREFIX".variable_paths:%u|g%s\n" + METRIC_PREFIX".unique_crashes:%llu|g%s\n" + METRIC_PREFIX".unique_hangs:%llu|g%s\n" + METRIC_PREFIX".total_crashes:%llu|g%s\n" + METRIC_PREFIX".slowest_exec_ms:%u|g%s\n" + METRIC_PREFIX".edges_found:%u|g%s\n" + METRIC_PREFIX".var_byte_count:%u|g%s\n" + METRIC_PREFIX".havoc_expansion:%u|g%s\n", + afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, + afl->cycles_wo_finds, tags, + afl->fsrv.total_execs, tags, + afl->fsrv.total_execs / ((double)(get_cur_time() - afl->start_time) / 1000), tags, + afl->queued_paths, tags, + afl->queued_favored, tags, + afl->queued_discovered, tags, + afl->queued_imported, tags, + afl->max_depth, tags, + afl->current_entry, tags, + afl->pending_favored, tags, + afl->pending_not_fuzzed, tags, + afl->queued_variable, tags, + afl->unique_crashes, tags, + afl->unique_hangs, tags, + afl->total_crashes, tags, + afl->slowest_exec_ms, tags, + count_non_255_bytes(afl, afl->virgin_bits), tags, + afl->var_byte_count, tags, + afl->expand_havoc, tags + ); + + return 0; +} \ No newline at end of file |