about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--custom_mutators/atnwalk/atnwalk.c254
-rw-r--r--src/afl-fuzz-run.c11
2 files changed, 232 insertions, 33 deletions
diff --git a/custom_mutators/atnwalk/atnwalk.c b/custom_mutators/atnwalk/atnwalk.c
index cc9f9618..584add61 100644
--- a/custom_mutators/atnwalk/atnwalk.c
+++ b/custom_mutators/atnwalk/atnwalk.c
@@ -1,5 +1,4 @@
-#include "../../include/afl-fuzz.h"
-
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -7,10 +6,10 @@
 #include <sys/un.h>
 #include <unistd.h>
 
-
 #define INIT_BUF_SIZE 4096
 #define SOCKET_NAME "/tmp/atnwalk.socket"
 
+
 // handshake constants
 const uint8_t SERVER_ARE_YOU_ALIVE = 42;
 const uint8_t SERVER_YES_I_AM_ALIVE = 213;
@@ -23,8 +22,10 @@ const uint8_t SERVER_ENCODE_BIT = 0b00001000;
 
 
 typedef struct atnwalk_mutator {
-    uint8_t *decoded_buf;
-    size_t decoded_size;
+    uint8_t *fuzz_buf;
+    size_t fuzz_size;
+    uint8_t *post_process_buf;
+    size_t post_process_size;
 } atnwalk_mutator_t;
 
 
@@ -55,6 +56,32 @@ 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);
+    buf[2] = (uint8_t) ((val & 0x0000ff00) >> 8);
+    buf[3] = (uint8_t) (val & 0x000000ff);
+}
+
+uint32_t to_uint32(uint8_t *buf) {
+    uint32_t val = 0;
+    val |= (((uint32_t) buf[0]) << 24);
+    val |= (((uint32_t) buf[1]) << 16);
+    val |= (((uint32_t) buf[2]) << 8);
+    val |= ((uint32_t) buf[3]);
+    return val;
+}
+
+void put_uint64(uint8_t *buf, uint64_t val) {
+    buf[0] = (uint8_t) (val >> 56);
+    buf[1] = (uint8_t) ((val & 0x00ff000000000000) >> 48);
+    buf[2] = (uint8_t) ((val & 0x0000ff0000000000) >> 40);
+    buf[3] = (uint8_t) ((val & 0x000000ff00000000) >> 32);
+    buf[4] = (uint8_t) ((val & 0x00000000ff000000) >> 24);
+    buf[5] = (uint8_t) ((val & 0x0000000000ff0000) >> 16);
+    buf[6] = (uint8_t) ((val & 0x000000000000ff00) >> 8);
+    buf[7] = (uint8_t) (val & 0x00000000000000ff);
+}
 
 /**
  * Initialize this custom mutator
@@ -67,19 +94,21 @@ int write_all(int fd, uint8_t *buf, size_t buf_size) {
  *         There may be multiple instances of this mutator in one afl-fuzz run!
  *         Return NULL on error.
  */
-atnwalk_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
+atnwalk_mutator_t *afl_custom_init(void *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->decoded_buf = (uint8_t *) malloc(INIT_BUF_SIZE);
-    data->decoded_size = INIT_BUF_SIZE;
+    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;
     return data;
 }
 
-// TODO: implement
+
 /**
  * Perform custom mutations on a given input
  *
@@ -100,13 +129,12 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
                        uint8_t *add_buf, size_t add_buf_size, size_t max_size) {
     struct sockaddr_un addr;
     int fd_socket;
-    ssize_t n;
-    uint8_t buffer[5];
+    uint8_t ctrl_buf[8];
+    uint8_t wanted;
 
     // initialize the socket
     fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
     if (fd_socket == -1) {
-        perror("socket");
         *out_buf = NULL;
         return 0;
     }
@@ -114,26 +142,127 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
     addr.sun_family = AF_UNIX;
     strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);
     if (connect(fd_socket, (const struct sockaddr *) &addr, sizeof(addr)) == -1) {
-        perror("atnwalk server is down");
+        close(fd_socket);
+        *out_buf = NULL;
+        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)) {
+        close(fd_socket);
         *out_buf = NULL;
         return 0;
     }
 
-    if (!write_all(fd_socket, buffer, 5)) {
-        perror("write to atnwalk server failed");
+    // see whether the server replies as expected
+    if (!read_all(fd_socket, ctrl_buf, 1) || ctrl_buf[0] != SERVER_YES_I_AM_ALIVE) {
+        close(fd_socket);
+        *out_buf = NULL;
+        return 0;
+    }
+
+    // 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)) {
+        wanted |= SERVER_CROSSOVER_BIT;
+    }
+
+    // tell the server what we want and how much data will be sent
+    ctrl_buf[0] = wanted;
+    put_uint32(ctrl_buf + 1, (uint32_t) buf_size);
+    if (!write_all(fd_socket, ctrl_buf, 5)) {
+        close(fd_socket);
         *out_buf = NULL;
         return 0;
     }
 
-    if (read_all(fd_socket, buffer, 5)) {
-        perror("read to atnwalk server failed");
-        exit(EXIT_FAILURE);
+    // send the data to mutate and encode
+    if (!write_all(fd_socket, buf, buf_size)) {
+        close(fd_socket);
+        *out_buf = buf;
+        return buf_size;
+    }
+
+    if (wanted & SERVER_CROSSOVER_BIT) {
+        // since we requested crossover, we will first tell how much additional data is to be expected
+        put_uint32(ctrl_buf, (uint32_t) add_buf_size);
+        if (!write_all(fd_socket, ctrl_buf, 4)) {
+            close(fd_socket);
+            *out_buf = buf;
+            return buf_size;
+        }
+
+        // send the additional data for crossover
+        if (!write_all(fd_socket, add_buf, add_buf_size)) {
+            close(fd_socket);
+            *out_buf = buf;
+            return buf_size;
+        }
+
+        // lastly, a seed is required for crossover so send one
+        put_uint64(ctrl_buf, (uint64_t) rand());
+        if (!write_all(fd_socket, ctrl_buf, 8)) {
+            close(fd_socket);
+            *out_buf = buf;
+            return buf_size;
+        }
+    }
+
+    // since we requested mutation, we need to provide a seed for that
+    put_uint64(ctrl_buf, (uint64_t) rand());
+    if (!write_all(fd_socket, ctrl_buf, 8)) {
+        close(fd_socket);
+        *out_buf = buf;
+        return buf_size;
+    }
+
+    // obtain the required buffer size for the data that will be returned
+    if (!read_all(fd_socket, ctrl_buf, 4)) {
+        close(fd_socket);
+        *out_buf = buf;
+        return buf_size;
+    }
+    size_t new_size = (size_t) to_uint32(ctrl_buf);
+
+    // if the data is too large then we ignore this round
+    if (new_size > max_size) {
+        close(fd_socket);
+        *out_buf = buf;
+        return buf_size;
+    }
+
+    if (new_size > buf_size) {
+        // buf is too small, need to use data->fuzz_buf, let's see whether we need to reallocate
+        if (new_size > data->fuzz_size) {
+            data->fuzz_size = new_size << 1;
+            data->fuzz_buf = (uint8_t *) realloc(data->fuzz_buf, data->fuzz_size);
+        }
+        *out_buf = data->fuzz_buf;
+    } else {
+        // new_size fits into buf, so re-use it
+        *out_buf = buf;
+    }
+
+    // obtain the encoded data
+    if (!read_all(fd_socket, *out_buf, new_size)) {
+        close(fd_socket);
+        *out_buf = buf;
+        return buf_size;
     }
 
     close(fd_socket);
+    return new_size;
 }
 
-// TODO: implement
+
 /**
  * A post-processing function to use right before AFL writes the test case to
  * disk in order to execute the target.
@@ -151,23 +280,90 @@ size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, u
  *     A return of 0 indicates an error.
  */
 size_t afl_custom_post_process(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size, uint8_t **out_buf) {
-    data->decoded_buf[0] = 'p';
-    data->decoded_buf[1] = 'u';
-    data->decoded_buf[2] = 't';
-    data->decoded_buf[3] = 's';
-    data->decoded_buf[4] = ' ';
-    data->decoded_buf[5] = ';';
-    data->decoded_buf[6] = '\n';
-    return 7;
+    struct sockaddr_un addr;
+    int fd_socket;
+    uint8_t ctrl_buf[8];
+
+    // initialize the socket
+    fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (fd_socket == -1) {
+        *out_buf = NULL;
+        return 0;
+    }
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);
+    if (connect(fd_socket, (const struct sockaddr *) &addr, sizeof(addr)) == -1) {
+        close(fd_socket);
+        *out_buf = NULL;
+        return 0;
+    }
+
+    // ask whether the server is alive
+    ctrl_buf[0] = SERVER_ARE_YOU_ALIVE;
+    if (!write_all(fd_socket, ctrl_buf, 1)) {
+        close(fd_socket);
+        *out_buf = NULL;
+        return 0;
+    }
+
+    // see whether the server replies as expected
+    if (!read_all(fd_socket, ctrl_buf, 1) || ctrl_buf[0] != SERVER_YES_I_AM_ALIVE) {
+        close(fd_socket);
+        *out_buf = NULL;
+        return 0;
+    }
+
+    // tell the server what we want and how much data will be sent
+    ctrl_buf[0] = SERVER_DECODE_BIT;
+    put_uint32(ctrl_buf + 1, (uint32_t) buf_size);
+    if (!write_all(fd_socket, ctrl_buf, 5)) {
+        close(fd_socket);
+        *out_buf = NULL;
+        return 0;
+    }
+
+    // send the data to decode
+    if (!write_all(fd_socket, buf, buf_size)) {
+        close(fd_socket);
+        *out_buf = buf;
+        return buf_size;
+    }
+
+    // obtain the required buffer size for the data that will be returned
+    if (!read_all(fd_socket, ctrl_buf, 4)) {
+        close(fd_socket);
+        *out_buf = buf;
+        return buf_size;
+    }
+    size_t new_size = (size_t) to_uint32(ctrl_buf);
+
+    // need to use data->post_process_buf, let's see whether we need to reallocate
+    if (new_size > data->post_process_size) {
+        data->post_process_size = new_size << 1;
+        data->post_process_buf = (uint8_t *) realloc(data->post_process_buf, data->post_process_size);
+    }
+    *out_buf = data->post_process_buf;
+
+    // obtain the decoded data
+    if (!read_all(fd_socket, *out_buf, new_size)) {
+        close(fd_socket);
+        *out_buf = buf;
+        return buf_size;
+    }
+
+    close(fd_socket);
+    return new_size;
 }
 
-// TODO: implement
+
 /**
  * Deinitialize everything
  *
  * @param data The data ptr from afl_custom_init
  */
 void afl_custom_deinit(atnwalk_mutator_t *data) {
-    free(data->decoded_buf);
+    free(data->fuzz_buf);
+    free(data->post_process_buf);
     free(data);
 }
diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c
index ee4a3298..26a1ea36 100644
--- a/src/afl-fuzz-run.c
+++ b/src/afl-fuzz-run.c
@@ -130,12 +130,15 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) {
       new_size = afl->max_length;
 
     }
-
-    if (new_mem != *mem) { *mem = new_mem; }
+    // TODO: think about how to enable the change without breaking other implementations
+    // if (new_mem != *mem) { *mem = new_mem; }
 
     /* everything as planned. use the potentially new data. */
-    afl_fsrv_write_to_testcase(&afl->fsrv, *mem, new_size);
-    len = new_size;
+    // TODO: think about how to enable the change without breaking other implementations
+    afl_fsrv_write_to_testcase(&afl->fsrv, new_mem, new_size);
+
+    // TODO: think about how to enable the change without breaking other implementations
+    // len = new_size;
 
   } else {