about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMaik Betka <9078425+voidptr127@users.noreply.github.com>2023-04-17 17:09:48 +0200
committerMaik Betka <9078425+voidptr127@users.noreply.github.com>2023-04-17 17:09:48 +0200
commit529a51c16053125ff0ddce7c6bd149f4ebe65461 (patch)
treee9c8afcfedb6f60b648c29bb4c2c6acc16c9df48
parente55b5c54080698ee2efe317321c1f387c225115b (diff)
downloadafl++-529a51c16053125ff0ddce7c6bd149f4ebe65461.tar.gz
implemented status screen and 50% havoc and 50% splice schedule with limited rounds per queue entry
-rw-r--r--custom_mutators/atnwalk/atnwalk.c137
1 files changed, 124 insertions, 13 deletions
diff --git a/custom_mutators/atnwalk/atnwalk.c b/custom_mutators/atnwalk/atnwalk.c
index 387d8b5d..0194ff18 100644
--- a/custom_mutators/atnwalk/atnwalk.c
+++ b/custom_mutators/atnwalk/atnwalk.c
@@ -1,3 +1,5 @@
+#include "../../include/afl-fuzz.h"
+
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -6,9 +8,14 @@
 #include <sys/un.h>
 #include <unistd.h>
 
-#define INIT_BUF_SIZE 4096
+#define BUF_SIZE_INIT 4096
 #define SOCKET_NAME "/tmp/atnwalk.socket"
 
+// how many errors (e.g. timeouts) to tolerate until moving on to the next queue entry
+#define ATNWALK_ERRORS_MAX 1
+
+// how many execution timeouts to tolerate until moving on to the next queue entry
+#define EXEC_TIMEOUT_MAX 2
 
 // handshake constants
 const uint8_t SERVER_ARE_YOU_ALIVE = 213;
@@ -22,6 +29,14 @@ const uint8_t SERVER_ENCODE_BIT = 0b00001000;
 
 
 typedef struct atnwalk_mutator {
+    afl_state_t *afl;
+    uint8_t atnwalk_error_count;
+    uint64_t prev_timeouts;
+    uint32_t prev_hits;
+    uint32_t stage_havoc_cur;
+    uint32_t stage_havoc_max;
+    uint32_t stage_splice_cur;
+    uint32_t stage_splice_max;
     uint8_t *fuzz_buf;
     size_t fuzz_size;
     uint8_t *post_process_buf;
@@ -56,6 +71,7 @@ int write_all(int fd, uint8_t *buf, size_t buf_size) {
     return 1;
 }
 
+
 void put_uint32(uint8_t *buf, uint32_t val) {
     buf[0] = (uint8_t) (val >> 24);
     buf[1] = (uint8_t) ((val & 0x00ff0000) >> 16);
@@ -63,6 +79,7 @@ void put_uint32(uint8_t *buf, uint32_t val) {
     buf[3] = (uint8_t) (val & 0x000000ff);
 }
 
+
 uint32_t to_uint32(uint8_t *buf) {
     uint32_t val = 0;
     val |= (((uint32_t) buf[0]) << 24);
@@ -72,6 +89,7 @@ uint32_t to_uint32(uint8_t *buf) {
     return val;
 }
 
+
 void put_uint64(uint8_t *buf, uint64_t val) {
     buf[0] = (uint8_t) (val >> 56);
     buf[1] = (uint8_t) ((val & 0x00ff000000000000) >> 48);
@@ -83,6 +101,7 @@ void put_uint64(uint8_t *buf, uint64_t val) {
     buf[7] = (uint8_t) (val & 0x00000000000000ff);
 }
 
+
 /**
  * Initialize this custom mutator
  *
@@ -94,21 +113,48 @@ void put_uint64(uint8_t *buf, uint64_t val) {
  *         There may be multiple instances of this mutator in one afl-fuzz run!
  *         Return NULL on error.
  */
-atnwalk_mutator_t *afl_custom_init(void *afl, unsigned int seed) {
+atnwalk_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
     srand(seed);
     atnwalk_mutator_t *data = (atnwalk_mutator_t *) malloc(sizeof(atnwalk_mutator_t));
     if (!data) {
         perror("afl_custom_init alloc");
         return NULL;
     }
-    data->fuzz_buf = (uint8_t *) malloc(INIT_BUF_SIZE);
-    data->fuzz_size = INIT_BUF_SIZE;
-    data->post_process_buf = (uint8_t *) malloc(INIT_BUF_SIZE);
-    data->post_process_size = INIT_BUF_SIZE;
+    data->afl = afl;
+    data->prev_hits = 0;
+    data->fuzz_buf = (uint8_t *) malloc(BUF_SIZE_INIT);
+    data->fuzz_size = BUF_SIZE_INIT;
+    data->post_process_buf = (uint8_t *) malloc(BUF_SIZE_INIT);
+    data->post_process_size = BUF_SIZE_INIT;
     return data;
 }
 
 
+unsigned int afl_custom_fuzz_count(atnwalk_mutator_t *data, const unsigned char *buf, size_t buf_size) {
+    // afl_custom_fuzz_count is called exactly once before entering the 'stage-loop' for the current queue entry
+    // thus, we use it to reset the error count and to initialize stage variables (somewhat not intended by the API,
+    // but still better than rewriting the whole thing to have a custom mutator stage)
+    data->atnwalk_error_count = 0;
+    data->prev_timeouts = data->afl->total_tmouts;
+
+    // it might happen that on the last execution of the splice stage a new path is found
+    // we need to fix that here and count it
+    if (data->prev_hits) {
+        data->afl->stage_finds[STAGE_SPLICE] += data->afl->queued_items + data->afl->saved_crashes - data->prev_hits;
+    }
+    data->prev_hits = data->afl->queued_items + data->afl->saved_crashes;
+    data->stage_havoc_cur = 0;
+    data->stage_splice_cur = 0;
+
+    // 50% havoc, 50% splice
+    data->stage_havoc_max = data->afl->stage_max >> 1;
+    if (data->stage_havoc_max < HAVOC_MIN) {
+        data->stage_havoc_max = HAVOC_MIN;
+    }
+    data->stage_splice_max = data->stage_havoc_max;
+    return data->stage_havoc_max + data->stage_splice_max;
+}
+
 /**
  * Perform custom mutations on a given input
  *
@@ -132,6 +178,48 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
     uint8_t ctrl_buf[8];
     uint8_t wanted;
 
+    // let's display what's going on in a nice way
+    if (data->stage_havoc_cur == 0) {
+        data->afl->stage_name = (uint8_t *) "atnwalk - havoc";
+    }
+    if (data->stage_havoc_cur == data->stage_havoc_max) {
+        data->afl->stage_name = (uint8_t *) "atnwalk - splice";
+    }
+
+    // increase the respective havoc or splice counters
+    if (data->stage_havoc_cur < data->stage_havoc_max) {
+        data->stage_havoc_cur++;
+        data->afl->stage_cycles[STAGE_HAVOC]++;
+    } else {
+        // if there is nothing to splice, continue with havoc and skip splicing this time
+        if (data->afl->ready_for_splicing_count < 1) {
+            data->stage_havoc_max = data->afl->stage_max;
+            data->stage_havoc_cur++;
+            data->afl->stage_cycles[STAGE_HAVOC]++;
+        } else {
+            data->stage_splice_cur++;
+            data->afl->stage_cycles[STAGE_SPLICE]++;
+        }
+    }
+
+    // keep track of found new corpus seeds per stage and run the stage twice as long as initially planned
+    if (data->afl->queued_items + data->afl->saved_crashes > data->prev_hits) {
+        if (data->stage_splice_cur <= 1) {
+            data->afl->stage_finds[STAGE_HAVOC] += data->afl->queued_items + data->afl->saved_crashes - data->prev_hits;
+        } else {
+            data->afl->stage_finds[STAGE_SPLICE] +=
+                    data->afl->queued_items + data->afl->saved_crashes - data->prev_hits;
+        }
+    }
+    data->prev_hits = data->afl->queued_items + data->afl->saved_crashes;
+
+    // check whether this input produces a lot of timeouts, if it does then abandon this queue entry
+    if (data->afl->total_tmouts - data->prev_timeouts >= EXEC_TIMEOUT_MAX) {
+        data->afl->stage_max = data->afl->stage_cur;
+        *out_buf = buf;
+        return buf_size;
+    }
+
     // initialize the socket
     fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
     if (fd_socket == -1) {
@@ -147,11 +235,6 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
         return 0;
     }
 
-    // TODO: how to set connection deadline? maybe not required if server already closes the connection?
-
-    // TODO: there should be some kind of loop retrying with different seeds and ultimately giving up on that input?
-    //       maybe this is not necessary, because we may also just return a single byte in case of failure?
-
     // ask whether the server is alive
     ctrl_buf[0] = SERVER_ARE_YOU_ALIVE;
     if (!write_all(fd_socket, ctrl_buf, 1)) {
@@ -170,8 +253,8 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
     // tell the server what we want to do
     wanted = SERVER_MUTATE_BIT | SERVER_ENCODE_BIT;
 
-    // 50% chance to perform a crossover if there is an additional buffer available
-    if ((add_buf_size > 0) && (rand() % 2)) {
+    // perform a crossover if we are splicing
+    if (data->stage_splice_cur > 0) {
         wanted |= SERVER_CROSSOVER_BIT;
     }
 
@@ -196,6 +279,10 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
         put_uint32(ctrl_buf, (uint32_t) add_buf_size);
         if (!write_all(fd_socket, ctrl_buf, 4)) {
             close(fd_socket);
+            data->atnwalk_error_count++;
+            if (data->atnwalk_error_count > ATNWALK_ERRORS_MAX) {
+                data->afl->stage_max = data->afl->stage_cur;
+            }
             *out_buf = buf;
             return buf_size;
         }
@@ -203,6 +290,10 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
         // send the additional data for crossover
         if (!write_all(fd_socket, add_buf, add_buf_size)) {
             close(fd_socket);
+            data->atnwalk_error_count++;
+            if (data->atnwalk_error_count > ATNWALK_ERRORS_MAX) {
+                data->afl->stage_max = data->afl->stage_cur;
+            }
             *out_buf = buf;
             return buf_size;
         }
@@ -211,6 +302,10 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
         put_uint64(ctrl_buf, (uint64_t) rand());
         if (!write_all(fd_socket, ctrl_buf, 8)) {
             close(fd_socket);
+            data->atnwalk_error_count++;
+            if (data->atnwalk_error_count > ATNWALK_ERRORS_MAX) {
+                data->afl->stage_max = data->afl->stage_cur;
+            }
             *out_buf = buf;
             return buf_size;
         }
@@ -220,6 +315,10 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
     put_uint64(ctrl_buf, (uint64_t) rand());
     if (!write_all(fd_socket, ctrl_buf, 8)) {
         close(fd_socket);
+        data->atnwalk_error_count++;
+        if (data->atnwalk_error_count > ATNWALK_ERRORS_MAX) {
+            data->afl->stage_max = data->afl->stage_cur;
+        }
         *out_buf = buf;
         return buf_size;
     }
@@ -227,6 +326,10 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
     // obtain the required buffer size for the data that will be returned
     if (!read_all(fd_socket, ctrl_buf, 4)) {
         close(fd_socket);
+        data->atnwalk_error_count++;
+        if (data->atnwalk_error_count > ATNWALK_ERRORS_MAX) {
+            data->afl->stage_max = data->afl->stage_cur;
+        }
         *out_buf = buf;
         return buf_size;
     }
@@ -235,6 +338,10 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
     // if the data is too large then we ignore this round
     if (new_size > max_size) {
         close(fd_socket);
+        data->atnwalk_error_count++;
+        if (data->atnwalk_error_count > ATNWALK_ERRORS_MAX) {
+            data->afl->stage_max = data->afl->stage_cur;
+        }
         *out_buf = buf;
         return buf_size;
     }
@@ -254,6 +361,10 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
     // obtain the encoded data
     if (!read_all(fd_socket, *out_buf, new_size)) {
         close(fd_socket);
+        data->atnwalk_error_count++;
+        if (data->atnwalk_error_count > ATNWALK_ERRORS_MAX) {
+            data->afl->stage_max = data->afl->stage_cur;
+        }
         *out_buf = buf;
         return buf_size;
     }