about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--include/afl-fuzz.h11
-rw-r--r--include/config.h12
-rw-r--r--include/envs.h2
-rw-r--r--src/afl-fuzz-run.c1
-rw-r--r--src/afl-fuzz-stats.c10
-rw-r--r--src/afl-fuzz-statsd.c156
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