about summary refs log tree commit diff
path: root/custom_mutators
diff options
context:
space:
mode:
authorvan Hauser <vh@thc.org>2021-03-24 18:19:45 +0100
committerGitHub <noreply@github.com>2021-03-24 18:19:45 +0100
commitc2b58cff6fa7d6af766cc6f686046d7e043a3977 (patch)
tree0c04cd932d129b45e31fd17c328844295677ca5f /custom_mutators
parent958436be4ba057e8409787e7ff4ddcfa095c46da (diff)
parent6e2a0ef233fc09e8751e2d4cba3298610d8bed2c (diff)
downloadafl++-c2b58cff6fa7d6af766cc6f686046d7e043a3977.tar.gz
Merge pull request #843 from AFLplusplus/tmp
Tmp
Diffstat (limited to 'custom_mutators')
-rw-r--r--custom_mutators/honggfuzz/common.h0
-rw-r--r--custom_mutators/honggfuzz/honggfuzz.h9
-rw-r--r--custom_mutators/honggfuzz/input.h6
l---------custom_mutators/honggfuzz/libhfcommon1
-rw-r--r--custom_mutators/honggfuzz/libhfcommon/common.h3
l---------custom_mutators/honggfuzz/libhfcommon/log.h (renamed from custom_mutators/honggfuzz/log.h)0
l---------custom_mutators/honggfuzz/libhfcommon/util.h (renamed from custom_mutators/honggfuzz/util.h)0
-rw-r--r--custom_mutators/honggfuzz/mangle.c1174
-rw-r--r--custom_mutators/rust/.gitignore10
-rw-r--r--custom_mutators/rust/Cargo.toml8
-rw-r--r--custom_mutators/rust/README.md11
-rw-r--r--custom_mutators/rust/custom_mutator-sys/Cargo.toml12
-rw-r--r--custom_mutators/rust/custom_mutator-sys/build.rs42
-rw-r--r--custom_mutators/rust/custom_mutator-sys/src/lib.rs5
-rw-r--r--custom_mutators/rust/custom_mutator-sys/wrapper.h4
-rw-r--r--custom_mutators/rust/custom_mutator/Cargo.toml13
-rw-r--r--custom_mutators/rust/custom_mutator/src/lib.rs740
-rw-r--r--custom_mutators/rust/example/Cargo.toml15
-rw-r--r--custom_mutators/rust/example/src/example_mutator.rs50
-rw-r--r--custom_mutators/rust/example_lain/Cargo.toml16
-rw-r--r--custom_mutators/rust/example_lain/rust-toolchain1
-rw-r--r--custom_mutators/rust/example_lain/src/lain_mutator.rs61
22 files changed, 1521 insertions, 660 deletions
diff --git a/custom_mutators/honggfuzz/common.h b/custom_mutators/honggfuzz/common.h
deleted file mode 100644
index e69de29b..00000000
--- a/custom_mutators/honggfuzz/common.h
+++ /dev/null
diff --git a/custom_mutators/honggfuzz/honggfuzz.h b/custom_mutators/honggfuzz/honggfuzz.h
index 9d07fdf4..c80cdd87 100644
--- a/custom_mutators/honggfuzz/honggfuzz.h
+++ b/custom_mutators/honggfuzz/honggfuzz.h
@@ -39,7 +39,7 @@
 #include "libhfcommon/util.h"
 
 #define PROG_NAME    "honggfuzz"
-#define PROG_VERSION "2.3"
+#define PROG_VERSION "2.4"
 
 /* Name of the template which will be replaced with the proper name of the file */
 #define _HF_FILE_PLACEHOLDER "___FILE___"
@@ -208,6 +208,7 @@ typedef struct {
         const char* crashDir;
         const char* covDirNew;
         bool        saveUnique;
+        bool        saveSmaller;
         size_t      dynfileqMaxSz;
         size_t      dynfileqCnt;
         dynfile_t*  dynfileqCurrent;
@@ -279,9 +280,9 @@ typedef struct {
         cmpfeedback_t*  cmpFeedbackMap;
         int             cmpFeedbackFd;
         bool            cmpFeedback;
-        const char*     blacklistFile;
-        uint64_t*       blacklist;
-        size_t          blacklistCnt;
+        const char*     blocklistFile;
+        uint64_t*       blocklist;
+        size_t          blocklistCnt;
         bool            skipFeedbackOnTimeout;
         uint64_t        maxCov[4];
         dynFileMethod_t dynFileMethod;
diff --git a/custom_mutators/honggfuzz/input.h b/custom_mutators/honggfuzz/input.h
index 7b0c55ae..09712f54 100644
--- a/custom_mutators/honggfuzz/input.h
+++ b/custom_mutators/honggfuzz/input.h
@@ -77,11 +77,11 @@ static inline uint64_t util_rndGet(uint64_t min, uint64_t max) {
 }
 static inline uint64_t util_rnd64() { return rand_below(afl_struct, 1 << 30); }
 
-static inline size_t input_getRandomInputAsBuf(run_t *run, const uint8_t **buf) {
-  *buf = queue_input;
+static inline const uint8_t* input_getRandomInputAsBuf(run_t* run, size_t* len) {
+  *len = queue_input_size;
   run->dynfile->data = queue_input;
   run->dynfile->size = queue_input_size;
-  return queue_input_size;
+  return queue_input;
 }
 static inline void input_setSize(run_t* run, size_t sz) {
   run->dynfile->size = sz;
diff --git a/custom_mutators/honggfuzz/libhfcommon b/custom_mutators/honggfuzz/libhfcommon
deleted file mode 120000
index 945c9b46..00000000
--- a/custom_mutators/honggfuzz/libhfcommon
+++ /dev/null
@@ -1 +0,0 @@
-.
\ No newline at end of file
diff --git a/custom_mutators/honggfuzz/libhfcommon/common.h b/custom_mutators/honggfuzz/libhfcommon/common.h
new file mode 100644
index 00000000..c8cf1329
--- /dev/null
+++ b/custom_mutators/honggfuzz/libhfcommon/common.h
@@ -0,0 +1,3 @@
+#ifndef LOG_E
+  #define LOG_E LOG_F
+#endif
diff --git a/custom_mutators/honggfuzz/log.h b/custom_mutators/honggfuzz/libhfcommon/log.h
index 51e19654..51e19654 120000
--- a/custom_mutators/honggfuzz/log.h
+++ b/custom_mutators/honggfuzz/libhfcommon/log.h
diff --git a/custom_mutators/honggfuzz/util.h b/custom_mutators/honggfuzz/libhfcommon/util.h
index 51e19654..51e19654 120000
--- a/custom_mutators/honggfuzz/util.h
+++ b/custom_mutators/honggfuzz/libhfcommon/util.h
diff --git a/custom_mutators/honggfuzz/mangle.c b/custom_mutators/honggfuzz/mangle.c
index 9c3d1ed4..637d428d 100644
--- a/custom_mutators/honggfuzz/mangle.c
+++ b/custom_mutators/honggfuzz/mangle.c
@@ -39,252 +39,208 @@
 #include "libhfcommon/log.h"
 #include "libhfcommon/util.h"
 
-static inline size_t mangle_LenLeft(run_t *run, size_t off) {
-
-  if (off >= run->dynfile->size) {
-
-    LOG_F("Offset is too large: off:%zu >= len:%zu", off, run->dynfile->size);
-
-  }
-
-  return (run->dynfile->size - off - 1);
-
+static inline size_t mangle_LenLeft(run_t* run, size_t off) {
+    if (off >= run->dynfile->size) {
+        LOG_F("Offset is too large: off:%zu >= len:%zu", off, run->dynfile->size);
+    }
+    return (run->dynfile->size - off - 1);
 }
 
-/* Get a random value <1:max>, but prefer smaller ones - up to 4KiB */
+/*
+ * Get a random value <1:max>, but prefer smaller ones
+ * Based on an idea by https://twitter.com/gamozolabs
+ */
 static inline size_t mangle_getLen(size_t max) {
+    if (max > _HF_INPUT_MAX_SIZE) {
+        LOG_F("max (%zu) > _HF_INPUT_MAX_SIZE (%zu)", max, (size_t)_HF_INPUT_MAX_SIZE);
+    }
+    if (max == 0) {
+        LOG_F("max == 0");
+    }
+    if (max == 1) {
+        return 1;
+    }
 
-  if (max > _HF_INPUT_MAX_SIZE) {
-
-    LOG_F("max (%zu) > _HF_INPUT_MAX_SIZE (%zu)", max,
-          (size_t)_HF_INPUT_MAX_SIZE);
-
-  }
-
-  if (max == 0) { LOG_F("max == 0"); }
-  if (max == 1) { return 1; }
-
-  /* Give 50% chance the the uniform distribution */
-  switch (util_rndGet(0, 9)) {
-
-    case 0:
-      return (size_t)util_rndGet(1, HF_MIN(16, max));
-    case 1:
-      return (size_t)util_rndGet(1, HF_MIN(64, max));
-    case 2:
-      return (size_t)util_rndGet(1, HF_MIN(256, max));
-    case 3:
-      return (size_t)util_rndGet(1, HF_MIN(1024, max));
-    case 4:
-      return (size_t)util_rndGet(1, HF_MIN(4096, max));
-    default:
-      break;
-
-  }
-
-  return (size_t)util_rndGet(1, max);
+    /* Give 50% chance the the uniform distribution */
+    if (util_rnd64() & 1) {
+        return (size_t)util_rndGet(1, max);
+    }
 
+    /* effectively exprand() */
+    return (size_t)util_rndGet(1, util_rndGet(1, max));
 }
 
 /* Prefer smaller values here, so use mangle_getLen() */
-static inline size_t mangle_getOffSet(run_t *run) {
-
-  return mangle_getLen(run->dynfile->size) - 1;
-
+static inline size_t mangle_getOffSet(run_t* run) {
+    return mangle_getLen(run->dynfile->size) - 1;
 }
 
 /* Offset which can be equal to the file size */
-static inline size_t mangle_getOffSetPlus1(run_t *run) {
-
-  size_t reqlen = HF_MIN(run->dynfile->size + 1, _HF_INPUT_MAX_SIZE);
-  return mangle_getLen(reqlen) - 1;
-
+static inline size_t mangle_getOffSetPlus1(run_t* run) {
+    size_t reqlen = HF_MIN(run->dynfile->size + 1, _HF_INPUT_MAX_SIZE);
+    return mangle_getLen(reqlen) - 1;
 }
 
-static inline void mangle_Move(run_t *run, size_t off_from, size_t off_to,
-                               size_t len) {
-
-  if (off_from >= run->dynfile->size) { return; }
-  if (off_to >= run->dynfile->size) { return; }
-  if (off_from == off_to) { return; }
-
-  size_t len_from = run->dynfile->size - off_from;
-  len = HF_MIN(len, len_from);
+static inline void mangle_Move(run_t* run, size_t off_from, size_t off_to, size_t len) {
+    if (off_from >= run->dynfile->size) {
+        return;
+    }
+    if (off_to >= run->dynfile->size) {
+        return;
+    }
+    if (off_from == off_to) {
+        return;
+    }
 
-  size_t len_to = run->dynfile->size - off_to;
-  len = HF_MIN(len, len_to);
+    size_t len_from = run->dynfile->size - off_from;
+    len             = HF_MIN(len, len_from);
 
-  memmove(&run->dynfile->data[off_to], &run->dynfile->data[off_from], len);
+    size_t len_to = run->dynfile->size - off_to;
+    len           = HF_MIN(len, len_to);
 
+    memmove(&run->dynfile->data[off_to], &run->dynfile->data[off_from], len);
 }
 
-static inline void mangle_Overwrite(run_t *run, size_t off, const uint8_t *src,
-                                    size_t len, bool printable) {
-
-  if (len == 0) { return; }
-  size_t maxToCopy = run->dynfile->size - off;
-  if (len > maxToCopy) { len = maxToCopy; }
-
-  memmove(&run->dynfile->data[off], src, len);
-  if (printable) { util_turnToPrintable(&run->dynfile->data[off], len); }
+static inline void mangle_Overwrite(
+    run_t* run, size_t off, const uint8_t* src, size_t len, bool printable) {
+    if (len == 0) {
+        return;
+    }
+    size_t maxToCopy = run->dynfile->size - off;
+    if (len > maxToCopy) {
+        len = maxToCopy;
+    }
 
+    memmove(&run->dynfile->data[off], src, len);
+    if (printable) {
+        util_turnToPrintable(&run->dynfile->data[off], len);
+    }
 }
 
-static inline size_t mangle_Inflate(run_t *run, size_t off, size_t len,
-                                    bool printable) {
-
-  if (run->dynfile->size >= run->global->mutate.maxInputSz) { return 0; }
-  if (len > (run->global->mutate.maxInputSz - run->dynfile->size)) {
-
-    len = run->global->mutate.maxInputSz - run->dynfile->size;
-
-  }
-
-  input_setSize(run, run->dynfile->size + len);
-  mangle_Move(run, off, off + len, run->dynfile->size);
-  if (printable) { memset(&run->dynfile->data[off], ' ', len); }
+static inline size_t mangle_Inflate(run_t* run, size_t off, size_t len, bool printable) {
+    if (run->dynfile->size >= run->global->mutate.maxInputSz) {
+        return 0;
+    }
+    if (len > (run->global->mutate.maxInputSz - run->dynfile->size)) {
+        len = run->global->mutate.maxInputSz - run->dynfile->size;
+    }
 
-  return len;
+    input_setSize(run, run->dynfile->size + len);
+    mangle_Move(run, off, off + len, run->dynfile->size);
+    if (printable) {
+        memset(&run->dynfile->data[off], ' ', len);
+    }
 
+    return len;
 }
 
-static inline void mangle_Insert(run_t *run, size_t off, const uint8_t *val,
-                                 size_t len, bool printable) {
-
-  len = mangle_Inflate(run, off, len, printable);
-  mangle_Overwrite(run, off, val, len, printable);
-
+static inline void mangle_Insert(
+    run_t* run, size_t off, const uint8_t* val, size_t len, bool printable) {
+    len = mangle_Inflate(run, off, len, printable);
+    mangle_Overwrite(run, off, val, len, printable);
 }
 
-static inline void mangle_UseValue(run_t *run, const uint8_t *val, size_t len,
-                                   bool printable) {
-
-  if (util_rnd64() % 2) {
-
-    mangle_Insert(run, mangle_getOffSetPlus1(run), val, len, printable);
-
-  } else {
-
-    mangle_Overwrite(run, mangle_getOffSet(run), val, len, printable);
-
-  }
-
+static inline void mangle_UseValue(run_t* run, const uint8_t* val, size_t len, bool printable) {
+    if (util_rnd64() & 1) {
+        mangle_Overwrite(run, mangle_getOffSet(run), val, len, printable);
+    } else {
+        mangle_Insert(run, mangle_getOffSetPlus1(run), val, len, printable);
+    }
 }
 
-static void mangle_MemSwap(run_t *run, bool printable HF_ATTR_UNUSED) {
-
-  size_t off1 = mangle_getOffSet(run);
-  size_t maxlen1 = run->dynfile->size - off1;
-
-  size_t off2 = mangle_getOffSet(run);
-  size_t maxlen2 = run->dynfile->size - off2;
-
-  size_t   len = mangle_getLen(HF_MIN(maxlen1, maxlen2));
-  uint8_t *tmpbuf = (uint8_t *)util_Malloc(len);
-  defer {
-
-    free(tmpbuf);
-
-  };
-
-  memcpy(tmpbuf, &run->dynfile->data[off1], len);
-  memmove(&run->dynfile->data[off1], &run->dynfile->data[off2], len);
-  memcpy(&run->dynfile->data[off2], tmpbuf, len);
-
+static inline void mangle_UseValueAt(
+    run_t* run, size_t off, const uint8_t* val, size_t len, bool printable) {
+    if (util_rnd64() & 1) {
+        mangle_Overwrite(run, off, val, len, printable);
+    } else {
+        mangle_Insert(run, off, val, len, printable);
+    }
 }
 
-static void mangle_MemCopy(run_t *run, bool printable HF_ATTR_UNUSED) {
-
-  size_t off = mangle_getOffSet(run);
-  size_t len = mangle_getLen(run->dynfile->size - off);
-
-  /* Use a temp buf, as Insert/Inflate can change source bytes */
-  uint8_t *tmpbuf = (uint8_t *)util_Malloc(len);
-  defer {
+static void mangle_MemSwap(run_t* run, bool printable HF_ATTR_UNUSED) {
+    /* No big deal if those two are overlapping */
+    size_t off1    = mangle_getOffSet(run);
+    size_t maxlen1 = run->dynfile->size - off1;
+    size_t off2    = mangle_getOffSet(run);
+    size_t maxlen2 = run->dynfile->size - off2;
+    size_t len     = mangle_getLen(HF_MIN(maxlen1, maxlen2));
 
-    free(tmpbuf);
-
-  };
-
-  memcpy(tmpbuf, &run->dynfile->data[off], len);
-
-  mangle_UseValue(run, tmpbuf, len, printable);
+    if (off1 == off2) {
+        return;
+    }
 
+    for (size_t i = 0; i < (len / 2); i++) {
+        /*
+         * First - from the head, next from the tail. Don't worry about layout of the overlapping
+         * part - there's no good solution to that, and it can be left somewhat scrambled,
+         * while still preserving the entropy
+         */
+        const uint8_t tmp1                       = run->dynfile->data[off2 + i];
+        run->dynfile->data[off2 + i]             = run->dynfile->data[off1 + i];
+        run->dynfile->data[off1 + i]             = tmp1;
+        const uint8_t tmp2                       = run->dynfile->data[off2 + (len - 1) - i];
+        run->dynfile->data[off2 + (len - 1) - i] = run->dynfile->data[off1 + (len - 1) - i];
+        run->dynfile->data[off1 + (len - 1) - i] = tmp2;
+    }
 }
 
-static void mangle_Bytes(run_t *run, bool printable) {
-
-  uint16_t buf;
-  if (printable) {
-
-    util_rndBufPrintable((uint8_t *)&buf, sizeof(buf));
-
-  } else {
+static void mangle_MemCopy(run_t* run, bool printable HF_ATTR_UNUSED) {
+    size_t off = mangle_getOffSet(run);
+    size_t len = mangle_getLen(run->dynfile->size - off);
 
-    buf = util_rnd64();
-
-  }
-
-  /* Overwrite with random 1-2-byte values */
-  size_t toCopy = util_rndGet(1, 2);
-  mangle_UseValue(run, (const uint8_t *)&buf, toCopy, printable);
+    /* Use a temp buf, as Insert/Inflate can change source bytes */
+    uint8_t* tmpbuf = (uint8_t*)util_Malloc(len);
+    defer {
+        free(tmpbuf);
+    };
+    memmove(tmpbuf, &run->dynfile->data[off], len);
 
+    mangle_UseValue(run, tmpbuf, len, printable);
 }
 
-static void mangle_ByteRepeatOverwrite(run_t *run, bool printable) {
-
-  size_t off = mangle_getOffSet(run);
-  size_t destOff = off + 1;
-  size_t maxSz = run->dynfile->size - destOff;
-
-  /* No space to repeat */
-  if (!maxSz) {
-
-    mangle_Bytes(run, printable);
-    return;
-
-  }
-
-  size_t len = mangle_getLen(maxSz);
-  memset(&run->dynfile->data[destOff], run->dynfile->data[off], len);
+static void mangle_Bytes(run_t* run, bool printable) {
+    uint16_t buf;
+    if (printable) {
+        util_rndBufPrintable((uint8_t*)&buf, sizeof(buf));
+    } else {
+        buf = util_rnd64();
+    }
 
+    /* Overwrite with random 1-2-byte values */
+    size_t toCopy = util_rndGet(1, 2);
+    mangle_UseValue(run, (const uint8_t*)&buf, toCopy, printable);
 }
 
-static void mangle_ByteRepeatInsert(run_t *run, bool printable) {
-
-  size_t off = mangle_getOffSet(run);
-  size_t destOff = off + 1;
-  size_t maxSz = run->dynfile->size - destOff;
+static void mangle_ByteRepeat(run_t* run, bool printable) {
+    size_t off     = mangle_getOffSet(run);
+    size_t destOff = off + 1;
+    size_t maxSz   = run->dynfile->size - destOff;
 
-  /* No space to repeat */
-  if (!maxSz) {
-
-    mangle_Bytes(run, printable);
-    return;
-
-  }
-
-  size_t len = mangle_getLen(maxSz);
-  len = mangle_Inflate(run, destOff, len, printable);
-  memset(&run->dynfile->data[destOff], run->dynfile->data[off], len);
+    /* No space to repeat */
+    if (!maxSz) {
+        mangle_Bytes(run, printable);
+        return;
+    }
 
+    size_t len = mangle_getLen(maxSz);
+    if (util_rnd64() & 0x1) {
+        len = mangle_Inflate(run, destOff, len, printable);
+    }
+    memset(&run->dynfile->data[destOff], run->dynfile->data[off], len);
 }
 
-static void mangle_Bit(run_t *run, bool printable) {
-
-  size_t off = mangle_getOffSet(run);
-  run->dynfile->data[off] ^= (uint8_t)(1U << util_rndGet(0, 7));
-  if (printable) { util_turnToPrintable(&(run->dynfile->data[off]), 1); }
-
+static void mangle_Bit(run_t* run, bool printable) {
+    size_t off = mangle_getOffSet(run);
+    run->dynfile->data[off] ^= (uint8_t)(1U << util_rndGet(0, 7));
+    if (printable) {
+        util_turnToPrintable(&(run->dynfile->data[off]), 1);
+    }
 }
 
 static const struct {
-
-  const uint8_t val[8];
-  const size_t  size;
-
+    const uint8_t val[8];
+    const size_t  size;
 } mangleMagicVals[] = {
-
     /* 1B - No endianness */
     {"\x00\x00\x00\x00\x00\x00\x00\x00", 1},
     {"\x01\x00\x00\x00\x00\x00\x00\x00", 1},
@@ -516,522 +472,436 @@ static const struct {
     {"\x00\x00\x00\x00\x00\x00\x00\x80", 8},
     {"\x01\x00\x00\x00\x00\x00\x00\x80", 8},
     {"\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8},
-
 };
 
-static void mangle_Magic(run_t *run, bool printable) {
-
-  uint64_t choice = util_rndGet(0, ARRAYSIZE(mangleMagicVals) - 1);
-  mangle_UseValue(run, mangleMagicVals[choice].val,
-                  mangleMagicVals[choice].size, printable);
-
-}
-
-static void mangle_StaticDict(run_t *run, bool printable) {
-
-  if (run->global->mutate.dictionaryCnt == 0) {
-
-    mangle_Bytes(run, printable);
-    return;
-
-  }
-
-  uint64_t choice = util_rndGet(0, run->global->mutate.dictionaryCnt - 1);
-  mangle_UseValue(run, run->global->mutate.dictionary[choice].val,
-                  run->global->mutate.dictionary[choice].len, printable);
-
+static void mangle_Magic(run_t* run, bool printable) {
+    uint64_t choice = util_rndGet(0, ARRAYSIZE(mangleMagicVals) - 1);
+    mangle_UseValue(run, mangleMagicVals[choice].val, mangleMagicVals[choice].size, printable);
 }
 
-static inline const uint8_t *mangle_FeedbackDict(run_t *run, size_t *len) {
-
-  if (!run->global->feedback.cmpFeedback) { return NULL; }
-  cmpfeedback_t *cmpf = run->global->feedback.cmpFeedbackMap;
-  uint32_t       cnt = ATOMIC_GET(cmpf->cnt);
-  if (cnt == 0) { return NULL; }
-  if (cnt > ARRAYSIZE(cmpf->valArr)) { cnt = ARRAYSIZE(cmpf->valArr); }
-  uint32_t choice = util_rndGet(0, cnt - 1);
-  *len = (size_t)ATOMIC_GET(cmpf->valArr[choice].len);
-  if (*len == 0) { return NULL; }
-  return cmpf->valArr[choice].val;
-
+static void mangle_StaticDict(run_t* run, bool printable) {
+    if (run->global->mutate.dictionaryCnt == 0) {
+        mangle_Bytes(run, printable);
+        return;
+    }
+    uint64_t choice = util_rndGet(0, run->global->mutate.dictionaryCnt - 1);
+    mangle_UseValue(run, run->global->mutate.dictionary[choice].val,
+        run->global->mutate.dictionary[choice].len, printable);
 }
 
-static void mangle_ConstFeedbackDict(run_t *run, bool printable) {
-
-  size_t         len;
-  const uint8_t *val = mangle_FeedbackDict(run, &len);
-  if (val == NULL) {
-
-    mangle_Bytes(run, printable);
-    return;
-
-  }
-
-  mangle_UseValue(run, val, len, printable);
-
+static inline const uint8_t* mangle_FeedbackDict(run_t* run, size_t* len) {
+    if (!run->global->feedback.cmpFeedback) {
+        return NULL;
+    }
+    cmpfeedback_t* cmpf = run->global->feedback.cmpFeedbackMap;
+    uint32_t       cnt  = ATOMIC_GET(cmpf->cnt);
+    if (cnt == 0) {
+        return NULL;
+    }
+    if (cnt > ARRAYSIZE(cmpf->valArr)) {
+        cnt = ARRAYSIZE(cmpf->valArr);
+    }
+    uint32_t choice = util_rndGet(0, cnt - 1);
+    *len            = (size_t)ATOMIC_GET(cmpf->valArr[choice].len);
+    if (*len == 0) {
+        return NULL;
+    }
+    return cmpf->valArr[choice].val;
 }
 
-static void mangle_MemSet(run_t *run, bool printable) {
-
-  size_t off = mangle_getOffSet(run);
-  size_t len = mangle_getLen(run->dynfile->size - off);
-  int    val =
-      printable ? (int)util_rndPrintable() : (int)util_rndGet(0, UINT8_MAX);
-
-  memset(&run->dynfile->data[off], val, len);
-
+static void mangle_ConstFeedbackDict(run_t* run, bool printable) {
+    size_t         len;
+    const uint8_t* val = mangle_FeedbackDict(run, &len);
+    if (val == NULL) {
+        mangle_Bytes(run, printable);
+        return;
+    }
+    mangle_UseValue(run, val, len, printable);
 }
 
-static void mangle_RandomOverwrite(run_t *run, bool printable) {
-
-  size_t off = mangle_getOffSet(run);
-  size_t len = mangle_getLen(run->dynfile->size - off);
-  if (printable) {
-
-    util_rndBufPrintable(&run->dynfile->data[off], len);
-
-  } else {
-
-    util_rndBuf(&run->dynfile->data[off], len);
+static void mangle_MemSet(run_t* run, bool printable) {
+    size_t off = mangle_getOffSet(run);
+    size_t len = mangle_getLen(run->dynfile->size - off);
+    int    val = printable ? (int)util_rndPrintable() : (int)util_rndGet(0, UINT8_MAX);
 
-  }
+    if (util_rnd64() & 1) {
+        len = mangle_Inflate(run, off, len, printable);
+    }
 
+    memset(&run->dynfile->data[off], val, len);
 }
 
-static void mangle_RandomInsert(run_t *run, bool printable) {
-
-  size_t off = mangle_getOffSet(run);
-  size_t len = mangle_getLen(run->dynfile->size - off);
-
-  len = mangle_Inflate(run, off, len, printable);
-
-  if (printable) {
+static void mangle_MemClr(run_t* run, bool printable) {
+    size_t off = mangle_getOffSet(run);
+    size_t len = mangle_getLen(run->dynfile->size - off);
+    int    val = printable ? ' ' : 0;
 
-    util_rndBufPrintable(&run->dynfile->data[off], len);
-
-  } else {
-
-    util_rndBuf(&run->dynfile->data[off], len);
-
-  }
+    if (util_rnd64() & 1) {
+        len = mangle_Inflate(run, off, len, printable);
+    }
 
+    memset(&run->dynfile->data[off], val, len);
 }
 
-static inline void mangle_AddSubWithRange(run_t *run, size_t off, size_t varLen,
-                                          uint64_t range, bool printable) {
-
-  int64_t delta = (int64_t)util_rndGet(0, range * 2) - (int64_t)range;
-
-  switch (varLen) {
-
-    case 1: {
-
-      run->dynfile->data[off] += delta;
-      break;
+static void mangle_RandomBuf(run_t* run, bool printable) {
+    size_t off = mangle_getOffSet(run);
+    size_t len = mangle_getLen(run->dynfile->size - off);
 
+    if (util_rnd64() & 1) {
+        len = mangle_Inflate(run, off, len, printable);
     }
 
-    case 2: {
-
-      int16_t val;
-      memcpy(&val, &run->dynfile->data[off], sizeof(val));
-      if (util_rnd64() & 0x1) {
-
-        val += delta;
-
-      } else {
-
-        /* Foreign endianess */
-        val = __builtin_bswap16(val);
-        val += delta;
-        val = __builtin_bswap16(val);
-
-      }
-
-      mangle_Overwrite(run, off, (uint8_t *)&val, varLen, printable);
-      break;
-
+    if (printable) {
+        util_rndBufPrintable(&run->dynfile->data[off], len);
+    } else {
+        util_rndBuf(&run->dynfile->data[off], len);
     }
+}
 
-    case 4: {
-
-      int32_t val;
-      memcpy(&val, &run->dynfile->data[off], sizeof(val));
-      if (util_rnd64() & 0x1) {
-
-        val += delta;
-
-      } else {
-
-        /* Foreign endianess */
-        val = __builtin_bswap32(val);
-        val += delta;
-        val = __builtin_bswap32(val);
-
-      }
-
-      mangle_Overwrite(run, off, (uint8_t *)&val, varLen, printable);
-      break;
-
+static inline void mangle_AddSubWithRange(
+    run_t* run, size_t off, size_t varLen, uint64_t range, bool printable) {
+    int64_t delta = (int64_t)util_rndGet(0, range * 2) - (int64_t)range;
+
+    switch (varLen) {
+        case 1: {
+            run->dynfile->data[off] += delta;
+            break;
+        }
+        case 2: {
+            int16_t val;
+            memcpy(&val, &run->dynfile->data[off], sizeof(val));
+            if (util_rnd64() & 0x1) {
+                val += delta;
+            } else {
+                /* Foreign endianess */
+                val = __builtin_bswap16(val);
+                val += delta;
+                val = __builtin_bswap16(val);
+            }
+            mangle_Overwrite(run, off, (uint8_t*)&val, varLen, printable);
+            break;
+        }
+        case 4: {
+            int32_t val;
+            memcpy(&val, &run->dynfile->data[off], sizeof(val));
+            if (util_rnd64() & 0x1) {
+                val += delta;
+            } else {
+                /* Foreign endianess */
+                val = __builtin_bswap32(val);
+                val += delta;
+                val = __builtin_bswap32(val);
+            }
+            mangle_Overwrite(run, off, (uint8_t*)&val, varLen, printable);
+            break;
+        }
+        case 8: {
+            int64_t val;
+            memcpy(&val, &run->dynfile->data[off], sizeof(val));
+            if (util_rnd64() & 0x1) {
+                val += delta;
+            } else {
+                /* Foreign endianess */
+                val = __builtin_bswap64(val);
+                val += delta;
+                val = __builtin_bswap64(val);
+            }
+            mangle_Overwrite(run, off, (uint8_t*)&val, varLen, printable);
+            break;
+        }
+        default: {
+            LOG_F("Unknown variable length size: %zu", varLen);
+        }
     }
+}
 
-    case 8: {
-
-      int64_t val;
-      memcpy(&val, &run->dynfile->data[off], sizeof(val));
-      if (util_rnd64() & 0x1) {
-
-        val += delta;
-
-      } else {
-
-        /* Foreign endianess */
-        val = __builtin_bswap64(val);
-        val += delta;
-        val = __builtin_bswap64(val);
-
-      }
-
-      mangle_Overwrite(run, off, (uint8_t *)&val, varLen, printable);
-      break;
+static void mangle_AddSub(run_t* run, bool printable) {
+    size_t off = mangle_getOffSet(run);
 
+    /* 1,2,4,8 */
+    size_t varLen = 1U << util_rndGet(0, 3);
+    if ((run->dynfile->size - off) < varLen) {
+        varLen = 1;
     }
 
-    default: {
-
-      LOG_F("Unknown variable length size: %zu", varLen);
-
+    uint64_t range;
+    switch (varLen) {
+        case 1:
+            range = 16;
+            break;
+        case 2:
+            range = 4096;
+            break;
+        case 4:
+            range = 1048576;
+            break;
+        case 8:
+            range = 268435456;
+            break;
+        default:
+            LOG_F("Invalid operand size: %zu", varLen);
     }
 
-  }
-
-}
-
-static void mangle_AddSub(run_t *run, bool printable) {
-
-  size_t off = mangle_getOffSet(run);
-
-  /* 1,2,4,8 */
-  size_t varLen = 1U << util_rndGet(0, 3);
-  if ((run->dynfile->size - off) < varLen) { varLen = 1; }
-
-  uint64_t range;
-  switch (varLen) {
-
-    case 1:
-      range = 16;
-      break;
-    case 2:
-      range = 4096;
-      break;
-    case 4:
-      range = 1048576;
-      break;
-    case 8:
-      range = 268435456;
-      break;
-    default:
-      LOG_F("Invalid operand size: %zu", varLen);
-
-  }
-
-  mangle_AddSubWithRange(run, off, varLen, range, printable);
-
-}
-
-static void mangle_IncByte(run_t *run, bool printable) {
-
-  size_t off = mangle_getOffSet(run);
-  if (printable) {
-
-    run->dynfile->data[off] = (run->dynfile->data[off] - 32 + 1) % 95 + 32;
-
-  } else {
-
-    run->dynfile->data[off] += (uint8_t)1UL;
-
-  }
-
+    mangle_AddSubWithRange(run, off, varLen, range, printable);
 }
 
-static void mangle_DecByte(run_t *run, bool printable) {
-
-  size_t off = mangle_getOffSet(run);
-  if (printable) {
-
-    run->dynfile->data[off] = (run->dynfile->data[off] - 32 + 94) % 95 + 32;
-
-  } else {
-
-    run->dynfile->data[off] -= (uint8_t)1UL;
-
-  }
-
-}
-
-static void mangle_NegByte(run_t *run, bool printable) {
-
-  size_t off = mangle_getOffSet(run);
-  if (printable) {
-
-    run->dynfile->data[off] = 94 - (run->dynfile->data[off] - 32) + 32;
-
-  } else {
-
-    run->dynfile->data[off] = ~(run->dynfile->data[off]);
-
-  }
-
+static void mangle_IncByte(run_t* run, bool printable) {
+    size_t off = mangle_getOffSet(run);
+    if (printable) {
+        run->dynfile->data[off] = (run->dynfile->data[off] - 32 + 1) % 95 + 32;
+    } else {
+        run->dynfile->data[off] += (uint8_t)1UL;
+    }
 }
 
-static void mangle_Expand(run_t *run, bool printable) {
-
-  size_t off = mangle_getOffSet(run);
-  size_t len;
-  if (util_rnd64() % 16) {
-
-    len = mangle_getLen(HF_MIN(16, run->global->mutate.maxInputSz - off));
-
-  } else {
-
-    len = mangle_getLen(run->global->mutate.maxInputSz - off);
-
-  }
-
-  mangle_Inflate(run, off, len, printable);
-
+static void mangle_DecByte(run_t* run, bool printable) {
+    size_t off = mangle_getOffSet(run);
+    if (printable) {
+        run->dynfile->data[off] = (run->dynfile->data[off] - 32 + 94) % 95 + 32;
+    } else {
+        run->dynfile->data[off] -= (uint8_t)1UL;
+    }
 }
 
-static void mangle_Shrink(run_t *run, bool printable HF_ATTR_UNUSED) {
-
-  if (run->dynfile->size <= 2U) { return; }
-
-  size_t off_start = mangle_getOffSet(run);
-  size_t len = mangle_LenLeft(run, off_start);
-  if (len == 0) { return; }
-  if (util_rnd64() % 16) {
-
-    len = mangle_getLen(HF_MIN(16, len));
-
-  } else {
-
-    len = mangle_getLen(len);
-
-  }
-
-  size_t off_end = off_start + len;
-  size_t len_to_move = run->dynfile->size - off_end;
-
-  mangle_Move(run, off_end, off_start, len_to_move);
-  input_setSize(run, run->dynfile->size - len);
-
+static void mangle_NegByte(run_t* run, bool printable) {
+    size_t off = mangle_getOffSet(run);
+    if (printable) {
+        run->dynfile->data[off] = 94 - (run->dynfile->data[off] - 32) + 32;
+    } else {
+        run->dynfile->data[off] = ~(run->dynfile->data[off]);
+    }
 }
 
-static void mangle_ASCIINum(run_t *run, bool printable) {
-
-  size_t len = util_rndGet(2, 8);
-
-  char buf[20];
-  snprintf(buf, sizeof(buf), "%-19" PRId64, (int64_t)util_rnd64());
-
-  mangle_UseValue(run, (const uint8_t *)buf, len, printable);
+static void mangle_Expand(run_t* run, bool printable) {
+    size_t off = mangle_getOffSet(run);
+    size_t len;
+    if (util_rnd64() % 16) {
+        len = mangle_getLen(HF_MIN(16, run->global->mutate.maxInputSz - off));
+    } else {
+        len = mangle_getLen(run->global->mutate.maxInputSz - off);
+    }
 
+    mangle_Inflate(run, off, len, printable);
 }
 
-static void mangle_ASCIINumChange(run_t *run, bool printable) {
-
-  size_t off = mangle_getOffSet(run);
-
-  /* Find a digit */
-  for (; off < run->dynfile->size; off++) {
-
-    if (isdigit(run->dynfile->data[off])) { break; }
-
-  }
-
-  if (off == run->dynfile->size) {
-
-    mangle_Bytes(run, printable);
-    return;
-
-  }
-
-  size_t len = HF_MIN(20, run->dynfile->size - off);
-  char   numbuf[21] = {};
-  strncpy(numbuf, (const char *)&run->dynfile->data[off], len);
-  uint64_t val = (uint64_t)strtoull(numbuf, NULL, 10);
-
-  switch (util_rndGet(0, 5)) {
-
-    case 0:
-      val += util_rndGet(1, 256);
-      break;
-    case 1:
-      val -= util_rndGet(1, 256);
-      break;
-    case 2:
-      val *= util_rndGet(1, 256);
-      break;
-    case 3:
-      val /= util_rndGet(1, 256);
-      break;
-    case 4:
-      val = ~(val);
-      break;
-    case 5:
-      val = util_rnd64();
-      break;
-    default:
-      LOG_F("Invalid choice");
-
-  };
+static void mangle_Shrink(run_t* run, bool printable HF_ATTR_UNUSED) {
+    if (run->dynfile->size <= 2U) {
+        return;
+    }
 
-  len = HF_MIN((size_t)snprintf(numbuf, sizeof(numbuf), "%" PRIu64, val), len);
-  mangle_Overwrite(run, off, (const uint8_t *)numbuf, len, printable);
+    size_t off_start = mangle_getOffSet(run);
+    size_t len       = mangle_LenLeft(run, off_start);
+    if (len == 0) {
+        return;
+    }
+    if (util_rnd64() % 16) {
+        len = mangle_getLen(HF_MIN(16, len));
+    } else {
+        len = mangle_getLen(len);
+    }
+    size_t off_end     = off_start + len;
+    size_t len_to_move = run->dynfile->size - off_end;
 
+    mangle_Move(run, off_end, off_start, len_to_move);
+    input_setSize(run, run->dynfile->size - len);
 }
+static void mangle_ASCIINum(run_t* run, bool printable) {
+    size_t len = util_rndGet(2, 8);
 
-static void mangle_Splice(run_t *run, bool printable) {
-
-  const uint8_t *buf;
-  size_t         sz = input_getRandomInputAsBuf(run, &buf);
-  if (!sz) {
-
-    mangle_Bytes(run, printable);
-    return;
-
-  }
-
-  size_t remoteOff = mangle_getLen(sz) - 1;
-  size_t len = mangle_getLen(sz - remoteOff);
-  mangle_UseValue(run, &buf[remoteOff], len, printable);
+    char buf[20];
+    snprintf(buf, sizeof(buf), "%-19" PRId64, (int64_t)util_rnd64());
 
+    mangle_UseValue(run, (const uint8_t*)buf, len, printable);
 }
 
-static void mangle_Resize(run_t *run, bool printable) {
-
-  ssize_t oldsz = run->dynfile->size;
-  ssize_t newsz = 0;
-
-  uint64_t choice = util_rndGet(0, 32);
-  switch (choice) {
+static void mangle_ASCIINumChange(run_t* run, bool printable) {
+    size_t off = mangle_getOffSet(run);
 
-    case 0:                                     /* Set new size arbitrarily */
-      newsz = (ssize_t)util_rndGet(1, run->global->mutate.maxInputSz);
-      break;
-    case 1 ... 4:                         /* Increase size by a small value */
-      newsz = oldsz + (ssize_t)util_rndGet(0, 8);
-      break;
-    case 5:                              /* Increase size by a larger value */
-      newsz = oldsz + (ssize_t)util_rndGet(9, 128);
-      break;
-    case 6 ... 9:                         /* Decrease size by a small value */
-      newsz = oldsz - (ssize_t)util_rndGet(0, 8);
-      break;
-    case 10:                             /* Decrease size by a larger value */
-      newsz = oldsz - (ssize_t)util_rndGet(9, 128);
-      break;
-    case 11 ... 32:                                           /* Do nothing */
-      newsz = oldsz;
-      break;
-    default:
-      LOG_F("Illegal value from util_rndGet: %" PRIu64, choice);
-      break;
-
-  }
-
-  if (newsz < 1) { newsz = 1; }
-  if (newsz > (ssize_t)run->global->mutate.maxInputSz) {
-
-    newsz = run->global->mutate.maxInputSz;
+    /* Find a digit */
+    for (; off < run->dynfile->size; off++) {
+        if (isdigit(run->dynfile->data[off])) {
+            break;
+        }
+    }
+    size_t left = run->dynfile->size - off;
+    if (left == 0) {
+        return;
+    }
 
-  }
+    size_t   len = 0;
+    uint64_t val = 0;
+    /* 20 is maximum lenght of a string representing a 64-bit unsigned value */
+    for (len = 0; (len < 20) && (len < left); len++) {
+        char c = run->dynfile->data[off + len];
+        if (!isdigit(c)) {
+            break;
+        }
+        val *= 10;
+        val += (c - '0');
+    }
 
-  input_setSize(run, (size_t)newsz);
-  if (newsz > oldsz) {
+    switch (util_rndGet(0, 7)) {
+        case 0:
+            val++;
+            break;
+        case 1:
+            val--;
+            break;
+        case 2:
+            val *= 2;
+            break;
+        case 3:
+            val /= 2;
+            break;
+        case 4:
+            val = util_rnd64();
+            break;
+        case 5:
+            val += util_rndGet(1, 256);
+            break;
+        case 6:
+            val -= util_rndGet(1, 256);
+            break;
+        case 7:
+            val = ~(val);
+            break;
+        default:
+            LOG_F("Invalid choice");
+    };
+
+    char buf[20];
+    snprintf(buf, sizeof(buf), "%-19" PRIu64, val);
+
+    mangle_UseValueAt(run, off, (const uint8_t*)buf, len, printable);
+}
+
+static void mangle_Splice(run_t* run, bool printable) {
+    if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
+        mangle_Bytes(run, printable);
+        return;
+    }
 
-    if (printable) { memset(&run->dynfile->data[oldsz], ' ', newsz - oldsz); }
+    size_t         sz  = 0;
+    const uint8_t* buf = input_getRandomInputAsBuf(run, &sz);
+    if (!buf) {
+        LOG_E("input_getRandomInputAsBuf() returned no input");
+        mangle_Bytes(run, printable);
+        return;
+    }
+    if (!sz) {
+        mangle_Bytes(run, printable);
+        return;
+    }
 
-  }
+    size_t remoteOff = mangle_getLen(sz) - 1;
+    size_t len       = mangle_getLen(sz - remoteOff);
+    mangle_UseValue(run, &buf[remoteOff], len, printable);
+}
+
+static void mangle_Resize(run_t* run, bool printable) {
+    ssize_t oldsz = run->dynfile->size;
+    ssize_t newsz = 0;
+
+    uint64_t choice = util_rndGet(0, 32);
+    switch (choice) {
+        case 0: /* Set new size arbitrarily */
+            newsz = (ssize_t)util_rndGet(1, run->global->mutate.maxInputSz);
+            break;
+        case 1 ... 4: /* Increase size by a small value */
+            newsz = oldsz + (ssize_t)util_rndGet(0, 8);
+            break;
+        case 5: /* Increase size by a larger value */
+            newsz = oldsz + (ssize_t)util_rndGet(9, 128);
+            break;
+        case 6 ... 9: /* Decrease size by a small value */
+            newsz = oldsz - (ssize_t)util_rndGet(0, 8);
+            break;
+        case 10: /* Decrease size by a larger value */
+            newsz = oldsz - (ssize_t)util_rndGet(9, 128);
+            break;
+        case 11 ... 32: /* Do nothing */
+            newsz = oldsz;
+            break;
+        default:
+            LOG_F("Illegal value from util_rndGet: %" PRIu64, choice);
+            break;
+    }
+    if (newsz < 1) {
+        newsz = 1;
+    }
+    if (newsz > (ssize_t)run->global->mutate.maxInputSz) {
+        newsz = run->global->mutate.maxInputSz;
+    }
 
+    input_setSize(run, (size_t)newsz);
+    if (newsz > oldsz) {
+        if (printable) {
+            memset(&run->dynfile->data[oldsz], ' ', newsz - oldsz);
+        }
+    }
 }
 
-void mangle_mangleContent(run_t *run, int speed_factor) {
-
-  static void (*const mangleFuncs[])(run_t * run, bool printable) = {
-
-      /* Every *Insert or Expand expands file, so add more Shrink's */
-      mangle_Shrink,
-      mangle_Shrink,
-      mangle_Shrink,
-      mangle_Shrink,
-      mangle_Expand,
-      mangle_Bit,
-      mangle_IncByte,
-      mangle_DecByte,
-      mangle_NegByte,
-      mangle_AddSub,
-      mangle_MemSet,
-      mangle_MemSwap,
-      mangle_MemCopy,
-      mangle_Bytes,
-      mangle_ASCIINum,
-      mangle_ASCIINumChange,
-      mangle_ByteRepeatOverwrite,
-      mangle_ByteRepeatInsert,
-      mangle_Magic,
-      mangle_StaticDict,
-      mangle_ConstFeedbackDict,
-      mangle_RandomOverwrite,
-      mangle_RandomInsert,
-      mangle_Splice,
-
-  };
-
-  if (run->mutationsPerRun == 0U) { return; }
-  if (run->dynfile->size == 0U) {
-
-    mangle_Resize(run, /* printable= */ run->global->cfg.only_printable);
-
-  }
-
-  uint64_t changesCnt;
-
-  if (speed_factor < 5) {
-
-    changesCnt = util_rndGet(1, run->global->mutate.mutationsPerRun);
-
-  } else if (speed_factor < 10) {
-
-    changesCnt = run->global->mutate.mutationsPerRun;
-
-  } else {
-
-    changesCnt = HF_MIN(speed_factor, 12);
-    changesCnt = HF_MAX(changesCnt, run->global->mutate.mutationsPerRun);
-
-  }
-
-  /* If last coverage acquisition was more than 5 secs ago, use splicing more
-   * frequently */
-  if ((time(NULL) - ATOMIC_GET(run->global->timing.lastCovUpdate)) > 5) {
-
-    if (util_rnd64() % 2) {
-
-      mangle_Splice(run, run->global->cfg.only_printable);
-
+void mangle_mangleContent(run_t* run, int speed_factor) {
+    static void (*const mangleFuncs[])(run_t * run, bool printable) = {
+        mangle_Shrink,
+        mangle_Expand,
+        mangle_Bit,
+        mangle_IncByte,
+        mangle_DecByte,
+        mangle_NegByte,
+        mangle_AddSub,
+        mangle_MemSet,
+        mangle_MemClr,
+        mangle_MemSwap,
+        mangle_MemCopy,
+        mangle_Bytes,
+        mangle_ASCIINum,
+        mangle_ASCIINumChange,
+        mangle_ByteRepeat,
+        mangle_Magic,
+        mangle_StaticDict,
+        mangle_ConstFeedbackDict,
+        mangle_RandomBuf,
+        mangle_Splice,
+    };
+
+    if (run->mutationsPerRun == 0U) {
+        return;
+    }
+    if (run->dynfile->size == 0U) {
+        mangle_Resize(run, /* printable= */ run->global->cfg.only_printable);
     }
 
-  }
-
-  for (uint64_t x = 0; x < changesCnt; x++) {
+    uint64_t changesCnt = run->global->mutate.mutationsPerRun;
 
-    uint64_t choice = util_rndGet(0, ARRAYSIZE(mangleFuncs) - 1);
-    mangleFuncs[choice](run, /* printable= */ run->global->cfg.only_printable);
+    if (speed_factor < 5) {
+        changesCnt = util_rndGet(1, run->global->mutate.mutationsPerRun);
+    } else if (speed_factor < 10) {
+        changesCnt = run->global->mutate.mutationsPerRun;
+    } else {
+        changesCnt = HF_MIN(speed_factor, 10);
+        changesCnt = HF_MAX(changesCnt, (run->global->mutate.mutationsPerRun * 5));
+    }
 
-  }
+    /* If last coverage acquisition was more than 5 secs ago, use splicing more frequently */
+    if ((time(NULL) - ATOMIC_GET(run->global->timing.lastCovUpdate)) > 5) {
+        if (util_rnd64() & 0x1) {
+            mangle_Splice(run, run->global->cfg.only_printable);
+        }
+    }
 
-  wmb();
+    for (uint64_t x = 0; x < changesCnt; x++) {
+        if (run->global->feedback.cmpFeedback && (util_rnd64() & 0x1)) {
+            /*
+             * mangle_ConstFeedbackDict() is quite powerful if the dynamic feedback dictionary
+             * exists. If so, give it 50% chance of being used among all mangling functions.
+             */
+            mangle_ConstFeedbackDict(run, /* printable= */ run->global->cfg.only_printable);
+        } else {
+            uint64_t choice = util_rndGet(0, ARRAYSIZE(mangleFuncs) - 1);
+            mangleFuncs[choice](run, /* printable= */ run->global->cfg.only_printable);
+        }
+    }
 
+    wmb();
 }
-
diff --git a/custom_mutators/rust/.gitignore b/custom_mutators/rust/.gitignore
new file mode 100644
index 00000000..088ba6ba
--- /dev/null
+++ b/custom_mutators/rust/.gitignore
@@ -0,0 +1,10 @@
+# Generated by Cargo
+# will have compiled files and executables
+/target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
diff --git a/custom_mutators/rust/Cargo.toml b/custom_mutators/rust/Cargo.toml
new file mode 100644
index 00000000..e36d24b5
--- /dev/null
+++ b/custom_mutators/rust/Cargo.toml
@@ -0,0 +1,8 @@
+[workspace]
+members = [
+    "custom_mutator-sys",
+    "custom_mutator",
+    "example",
+    # Lain needs a nightly toolchain
+    # "example_lain",
+]
\ No newline at end of file
diff --git a/custom_mutators/rust/README.md b/custom_mutators/rust/README.md
new file mode 100644
index 00000000..e2cc38b4
--- /dev/null
+++ b/custom_mutators/rust/README.md
@@ -0,0 +1,11 @@
+# Rust Custom Mutators
+
+Bindings to create custom mutators in Rust.
+
+These bindings are documented with rustdoc. To view the documentation run
+```cargo doc -p custom_mutator --open```.
+
+A minimal example can be found in `example`. Build it using `cargo build --example example_mutator`. 
+
+An example using [lain](https://github.com/microsoft/lain) for structured fuzzing can be found in `example_lain`.
+Since lain requires a nightly rust toolchain, you need to set one up before you can play with it.
diff --git a/custom_mutators/rust/custom_mutator-sys/Cargo.toml b/custom_mutators/rust/custom_mutator-sys/Cargo.toml
new file mode 100644
index 00000000..104f7df0
--- /dev/null
+++ b/custom_mutators/rust/custom_mutator-sys/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "custom_mutator-sys"
+version = "0.1.0"
+authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+
+[build-dependencies]
+bindgen = "0.56"
diff --git a/custom_mutators/rust/custom_mutator-sys/build.rs b/custom_mutators/rust/custom_mutator-sys/build.rs
new file mode 100644
index 00000000..3c88a90d
--- /dev/null
+++ b/custom_mutators/rust/custom_mutator-sys/build.rs
@@ -0,0 +1,42 @@
+extern crate bindgen;
+
+use std::env;
+use std::path::PathBuf;
+
+// this code is largely taken straight from the handbook: https://github.com/fitzgen/bindgen-tutorial-bzip2-sys
+fn main() {
+    // Tell cargo to invalidate the built crate whenever the wrapper changes
+    println!("cargo:rerun-if-changed=wrapper.h");
+
+    // The bindgen::Builder is the main entry point
+    // to bindgen, and lets you build up options for
+    // the resulting bindings.
+    let bindings = bindgen::Builder::default()
+        // The input header we would like to generate
+        // bindings for.
+        .header("wrapper.h")
+        .whitelist_type("afl_state_t")
+        .blacklist_type(r"u\d+")
+        .opaque_type(r"_.*")
+        .opaque_type("FILE")
+        .opaque_type("in_addr(_t)?")
+        .opaque_type("in_port(_t)?")
+        .opaque_type("sa_family(_t)?")
+        .opaque_type("sockaddr_in(_t)?")
+        .opaque_type("time_t")
+        .rustfmt_bindings(true)
+        .size_t_is_usize(true)
+        // Tell cargo to invalidate the built crate whenever any of the
+        // included header files changed.
+        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
+        // Finish the builder and generate the bindings.
+        .generate()
+        // Unwrap the Result and panic on failure.
+        .expect("Unable to generate bindings");
+
+    // Write the bindings to the $OUT_DIR/bindings.rs file.
+    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
+    bindings
+        .write_to_file(out_path.join("bindings.rs"))
+        .expect("Couldn't write bindings!");
+}
diff --git a/custom_mutators/rust/custom_mutator-sys/src/lib.rs b/custom_mutators/rust/custom_mutator-sys/src/lib.rs
new file mode 100644
index 00000000..a38a13a8
--- /dev/null
+++ b/custom_mutators/rust/custom_mutator-sys/src/lib.rs
@@ -0,0 +1,5 @@
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+
+include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
diff --git a/custom_mutators/rust/custom_mutator-sys/wrapper.h b/custom_mutators/rust/custom_mutator-sys/wrapper.h
new file mode 100644
index 00000000..81cdb90f
--- /dev/null
+++ b/custom_mutators/rust/custom_mutator-sys/wrapper.h
@@ -0,0 +1,4 @@
+#include "../../../include/afl-fuzz.h"
+#include "../../../include/common.h"
+#include "../../../include/config.h"
+#include "../../../include/debug.h"
diff --git a/custom_mutators/rust/custom_mutator/Cargo.toml b/custom_mutators/rust/custom_mutator/Cargo.toml
new file mode 100644
index 00000000..2d3cdbfa
--- /dev/null
+++ b/custom_mutators/rust/custom_mutator/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "custom_mutator"
+version = "0.1.0"
+authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[features]
+afl_internals = ["custom_mutator-sys"]
+
+[dependencies]
+custom_mutator-sys = { path = "../custom_mutator-sys", optional=true }
diff --git a/custom_mutators/rust/custom_mutator/src/lib.rs b/custom_mutators/rust/custom_mutator/src/lib.rs
new file mode 100644
index 00000000..9444e4d1
--- /dev/null
+++ b/custom_mutators/rust/custom_mutator/src/lib.rs
@@ -0,0 +1,740 @@
+#![cfg(unix)]
+//! Somewhat safe and somewhat ergonomic bindings for creating [AFL++](https://github.com/AFLplusplus/AFLplusplus) [custom mutators](https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/custom_mutators.md) in Rust.
+//!
+//! # Usage
+//! AFL++ custom mutators are expected to be dynamic libraries which expose a set of symbols.
+//! Check out [`CustomMutator`] to see which functions of the API are supported.
+//! Then use [`export_mutator`] to export the correct symbols for your mutator.
+//! In order to use the mutator, your crate needs to be a library crate and have a `crate-type` of `cdylib`.
+//! Putting
+//! ```yaml
+//! [lib]
+//! crate-type = ["cdylib"]
+//! ```
+//! into your `Cargo.toml` should do the trick.
+//! The final executable can be found in `target/(debug|release)/your_crate_name.so`.
+//! # Example
+//! See [`export_mutator`] for an example.
+//!
+//! # On `panic`s
+//! This binding is panic-safe in that it will prevent panics from unwinding into AFL++. Any panic will `abort` at the boundary between the custom mutator and AFL++.
+//!
+//! # Access to AFL++ internals
+//! This crate has an optional feature "afl_internals", which gives access to AFL++'s internal state.
+//! The state is passed to [`CustomMutator::init`], when the feature is activated.
+//!
+//! _This is completely unsafe and uses automatically generated types extracted from the AFL++ source._
+use std::{fmt::Debug, path::Path};
+
+#[cfg(feature = "afl_internals")]
+#[doc(hidden)]
+pub use custom_mutator_sys::afl_state;
+
+#[allow(unused_variables)]
+#[doc(hidden)]
+pub trait RawCustomMutator {
+    #[cfg(feature = "afl_internals")]
+    fn init(afl: &'static afl_state, seed: u32) -> Self
+    where
+        Self: Sized;
+    #[cfg(not(feature = "afl_internals"))]
+    fn init(seed: u32) -> Self
+    where
+        Self: Sized;
+
+    fn fuzz<'b, 's: 'b>(
+        &'s mut self,
+        buffer: &'b mut [u8],
+        add_buff: Option<&[u8]>,
+        max_size: usize,
+    ) -> Option<&'b [u8]>;
+
+    fn fuzz_count(&mut self, buffer: &[u8]) -> u32 {
+        1
+    }
+
+    fn queue_new_entry(&mut self, filename_new_queue: &Path, _filename_orig_queue: Option<&Path>) {}
+
+    fn queue_get(&mut self, filename: &Path) -> bool {
+        true
+    }
+
+    fn describe(&mut self, max_description: usize) -> Option<&str> {
+        Some(default_mutator_describe::<Self>(max_description))
+    }
+
+    fn introspection(&mut self) -> Option<&str> {
+        None
+    }
+
+    /*fn post_process(&self, buffer: &[u8], unsigned char **out_buf)-> usize;
+    int afl_custom_init_trim(&self, buffer: &[u8]);
+    size_t afl_custom_trim(&self, unsigned char **out_buf);
+    int afl_custom_post_trim(&self, unsigned char success);
+    size_t afl_custom_havoc_mutation(&self, buffer: &[u8], unsigned char **out_buf, size_t max_size);
+    unsigned char afl_custom_havoc_mutation_probability(&self);*/
+}
+
+/// Wrappers for the custom mutator which provide the bridging between the C API and CustomMutator.
+/// These wrappers are not intended to be used directly, rather export_mutator will use them to publish the custom mutator C API.
+#[doc(hidden)]
+pub mod wrappers {
+    #[cfg(feature = "afl_internals")]
+    use custom_mutator_sys::afl_state;
+
+    use std::{
+        any::Any,
+        convert::TryInto,
+        ffi::{c_void, CStr, OsStr},
+        mem::ManuallyDrop,
+        os::{raw::c_char, unix::ffi::OsStrExt},
+        panic::catch_unwind,
+        path::Path,
+        process::abort,
+        ptr::null,
+        slice,
+    };
+
+    use crate::RawCustomMutator;
+
+    /// A structure to be used as the data pointer for our custom mutator. This was used as additional storage and is kept for now in case its needed later.
+    /// Also has some convenience functions for FFI conversions (from and to ptr) and tries to make misuse hard (see [`FFIContext::from`]).
+    struct FFIContext<M: RawCustomMutator> {
+        mutator: M,
+        /// buffer for storing the description returned by [`RawCustomMutator::describe`] as a CString
+        description_buffer: Vec<u8>,
+        /// buffer for storing the introspection returned by [`RawCustomMutator::introspect`] as a CString
+        introspection_buffer: Vec<u8>,
+    }
+
+    impl<M: RawCustomMutator> FFIContext<M> {
+        fn from(ptr: *mut c_void) -> ManuallyDrop<Box<Self>> {
+            assert!(!ptr.is_null());
+            ManuallyDrop::new(unsafe { Box::from_raw(ptr as *mut Self) })
+        }
+
+        fn into_ptr(self: Box<Self>) -> *const c_void {
+            Box::into_raw(self) as *const c_void
+        }
+
+        #[cfg(feature = "afl_internals")]
+        fn new(afl: &'static afl_state, seed: u32) -> Box<Self> {
+            Box::new(Self {
+                mutator: M::init(afl, seed),
+                description_buffer: Vec::new(),
+                introspection_buffer: Vec::new(),
+            })
+        }
+        #[cfg(not(feature = "afl_internals"))]
+        fn new(seed: u32) -> Box<Self> {
+            Box::new(Self {
+                mutator: M::init(seed),
+                description_buffer: Vec::new(),
+                introspection_buffer: Vec::new(),
+            })
+        }
+    }
+
+    /// panic handler called for every panic
+    fn panic_handler(method: &str, panic_info: Box<dyn Any + Send + 'static>) -> ! {
+        use std::ops::Deref;
+        let cause = panic_info
+            .downcast_ref::<String>()
+            .map(String::deref)
+            .unwrap_or_else(|| {
+                panic_info
+                    .downcast_ref::<&str>()
+                    .copied()
+                    .unwrap_or("<cause unknown>")
+            });
+        eprintln!("A panic occurred at {}: {}", method, cause);
+        abort()
+    }
+
+    /// Internal function used in the macro
+    #[cfg(not(feature = "afl_internals"))]
+    pub fn afl_custom_init_<M: RawCustomMutator>(seed: u32) -> *const c_void {
+        match catch_unwind(|| FFIContext::<M>::new(seed).into_ptr()) {
+            Ok(ret) => ret,
+            Err(err) => panic_handler("afl_custom_init", err),
+        }
+    }
+
+    /// Internal function used in the macro
+    #[cfg(feature = "afl_internals")]
+    pub fn afl_custom_init_<M: RawCustomMutator>(
+        afl: Option<&'static afl_state>,
+        seed: u32,
+    ) -> *const c_void {
+        match catch_unwind(|| {
+            let afl = afl.expect("mutator func called with NULL afl");
+            FFIContext::<M>::new(afl, seed).into_ptr()
+        }) {
+            Ok(ret) => ret,
+            Err(err) => panic_handler("afl_custom_init", err),
+        }
+    }
+
+    /// Internal function used in the macro
+    pub unsafe fn afl_custom_fuzz_<M: RawCustomMutator>(
+        data: *mut c_void,
+        buf: *mut u8,
+        buf_size: usize,
+        out_buf: *mut *const u8,
+        add_buf: *mut u8,
+        add_buf_size: usize,
+        max_size: usize,
+    ) -> usize {
+        match catch_unwind(|| {
+            let mut context = FFIContext::<M>::from(data);
+            if buf.is_null() {
+                panic!("null buf passed to afl_custom_fuzz")
+            }
+            if out_buf.is_null() {
+                panic!("null out_buf passed to afl_custom_fuzz")
+            }
+            let buff_slice = slice::from_raw_parts_mut(buf, buf_size);
+            let add_buff_slice = if add_buf.is_null() {
+                None
+            } else {
+                Some(slice::from_raw_parts(add_buf, add_buf_size))
+            };
+            match context
+                .mutator
+                .fuzz(buff_slice, add_buff_slice, max_size.try_into().unwrap())
+            {
+                Some(buffer) => {
+                    *out_buf = buffer.as_ptr();
+                    buffer.len().try_into().unwrap()
+                }
+                None => {
+                    // return the input buffer with 0-length to let AFL skip this mutation attempt
+                    *out_buf = buf;
+                    0
+                }
+            }
+        }) {
+            Ok(ret) => ret,
+            Err(err) => panic_handler("afl_custom_fuzz", err),
+        }
+    }
+
+    /// Internal function used in the macro
+    pub unsafe fn afl_custom_fuzz_count_<M: RawCustomMutator>(
+        data: *mut c_void,
+        buf: *const u8,
+        buf_size: usize,
+    ) -> u32 {
+        match catch_unwind(|| {
+            let mut context = FFIContext::<M>::from(data);
+            if buf.is_null() {
+                panic!("null buf passed to afl_custom_fuzz")
+            }
+            let buf_slice = slice::from_raw_parts(buf, buf_size);
+            // see https://doc.rust-lang.org/nomicon/borrow-splitting.html
+            let ctx = &mut **context;
+            let mutator = &mut ctx.mutator;
+            mutator.fuzz_count(buf_slice)
+        }) {
+            Ok(ret) => ret,
+            Err(err) => panic_handler("afl_custom_fuzz_count", err),
+        }
+    }
+
+    /// Internal function used in the macro
+    pub fn afl_custom_queue_new_entry_<M: RawCustomMutator>(
+        data: *mut c_void,
+        filename_new_queue: *const c_char,
+        filename_orig_queue: *const c_char,
+    ) {
+        match catch_unwind(|| {
+            let mut context = FFIContext::<M>::from(data);
+            if filename_new_queue.is_null() {
+                panic!("received null filename_new_queue in afl_custom_queue_new_entry");
+            }
+            let filename_new_queue = Path::new(OsStr::from_bytes(
+                unsafe { CStr::from_ptr(filename_new_queue) }.to_bytes(),
+            ));
+            let filename_orig_queue = if !filename_orig_queue.is_null() {
+                Some(Path::new(OsStr::from_bytes(
+                    unsafe { CStr::from_ptr(filename_orig_queue) }.to_bytes(),
+                )))
+            } else {
+                None
+            };
+            context
+                .mutator
+                .queue_new_entry(filename_new_queue, filename_orig_queue);
+        }) {
+            Ok(ret) => ret,
+            Err(err) => panic_handler("afl_custom_queue_new_entry", err),
+        }
+    }
+
+    /// Internal function used in the macro
+    pub unsafe fn afl_custom_deinit_<M: RawCustomMutator>(data: *mut c_void) {
+        match catch_unwind(|| {
+            // drop the context
+            ManuallyDrop::into_inner(FFIContext::<M>::from(data));
+        }) {
+            Ok(ret) => ret,
+            Err(err) => panic_handler("afl_custom_deinit", err),
+        }
+    }
+
+    /// Internal function used in the macro
+    pub fn afl_custom_introspection_<M: RawCustomMutator>(data: *mut c_void) -> *const c_char {
+        match catch_unwind(|| {
+            let context = &mut *FFIContext::<M>::from(data);
+            if let Some(res) = context.mutator.introspection() {
+                let buf = &mut context.introspection_buffer;
+                buf.clear();
+                buf.extend_from_slice(res.as_bytes());
+                buf.push(0);
+                // unwrapping here, as the error case should be extremely rare
+                CStr::from_bytes_with_nul(&buf).unwrap().as_ptr()
+            } else {
+                null()
+            }
+        }) {
+            Ok(ret) => ret,
+            Err(err) => panic_handler("afl_custom_introspection", err),
+        }
+    }
+
+    /// Internal function used in the macro
+    pub fn afl_custom_describe_<M: RawCustomMutator>(
+        data: *mut c_void,
+        max_description_len: usize,
+    ) -> *const c_char {
+        match catch_unwind(|| {
+            let context = &mut *FFIContext::<M>::from(data);
+            if let Some(res) = context.mutator.describe(max_description_len) {
+                let buf = &mut context.description_buffer;
+                buf.clear();
+                buf.extend_from_slice(res.as_bytes());
+                buf.push(0);
+                // unwrapping here, as the error case should be extremely rare
+                CStr::from_bytes_with_nul(&buf).unwrap().as_ptr()
+            } else {
+                null()
+            }
+        }) {
+            Ok(ret) => ret,
+            Err(err) => panic_handler("afl_custom_describe", err),
+        }
+    }
+
+    /// Internal function used in the macro
+    pub fn afl_custom_queue_get_<M: RawCustomMutator>(
+        data: *mut c_void,
+        filename: *const c_char,
+    ) -> u8 {
+        match catch_unwind(|| {
+            let mut context = FFIContext::<M>::from(data);
+            assert!(!filename.is_null());
+
+            context.mutator.queue_get(Path::new(OsStr::from_bytes(
+                unsafe { CStr::from_ptr(filename) }.to_bytes(),
+            ))) as u8
+        }) {
+            Ok(ret) => ret,
+            Err(err) => panic_handler("afl_custom_queue_get", err),
+        }
+    }
+}
+
+/// exports the given Mutator as a custom mutator as the C interface that AFL++ expects.
+/// It is not possible to call this macro multiple times, because it would define the custom mutator symbols multiple times.
+/// # Example
+/// ```
+/// # #[macro_use] extern crate custom_mutator;
+/// # #[cfg(feature = "afl_internals")]
+/// # use custom_mutator::afl_state;
+/// # use custom_mutator::CustomMutator;
+/// struct MyMutator;
+/// impl CustomMutator for MyMutator {
+///     /// ...
+/// #  type Error = ();
+/// #  #[cfg(feature = "afl_internals")]
+/// #  fn init(_afl_state: &afl_state, _seed: u32) -> Result<Self,()> {unimplemented!()}
+/// #  #[cfg(not(feature = "afl_internals"))]
+/// #  fn init(_seed: u32) -> Result<Self, Self::Error> {unimplemented!()}
+/// #  fn fuzz<'b,'s:'b>(&'s mut self, _buffer: &'b mut [u8], _add_buff: Option<&[u8]>, _max_size: usize) -> Result<Option<&'b [u8]>, Self::Error> {unimplemented!()}
+/// }
+/// export_mutator!(MyMutator);
+/// ```
+#[macro_export]
+macro_rules! export_mutator {
+    ($mutator_type:ty) => {
+        #[cfg(feature = "afl_internals")]
+        #[no_mangle]
+        pub extern "C" fn afl_custom_init(
+            afl: ::std::option::Option<&'static $crate::afl_state>,
+            seed: ::std::os::raw::c_uint,
+        ) -> *const ::std::os::raw::c_void {
+            $crate::wrappers::afl_custom_init_::<$mutator_type>(afl, seed as u32)
+        }
+
+        #[cfg(not(feature = "afl_internals"))]
+        #[no_mangle]
+        pub extern "C" fn afl_custom_init(
+            _afl: *const ::std::os::raw::c_void,
+            seed: ::std::os::raw::c_uint,
+        ) -> *const ::std::os::raw::c_void {
+            $crate::wrappers::afl_custom_init_::<$mutator_type>(seed as u32)
+        }
+
+        #[no_mangle]
+        pub extern "C" fn afl_custom_fuzz_count(
+            data: *mut ::std::os::raw::c_void,
+            buf: *const u8,
+            buf_size: usize,
+        ) -> u32 {
+            unsafe {
+                $crate::wrappers::afl_custom_fuzz_count_::<$mutator_type>(data, buf, buf_size)
+            }
+        }
+
+        #[no_mangle]
+        pub extern "C" fn afl_custom_fuzz(
+            data: *mut ::std::os::raw::c_void,
+            buf: *mut u8,
+            buf_size: usize,
+            out_buf: *mut *const u8,
+            add_buf: *mut u8,
+            add_buf_size: usize,
+            max_size: usize,
+        ) -> usize {
+            unsafe {
+                $crate::wrappers::afl_custom_fuzz_::<$mutator_type>(
+                    data,
+                    buf,
+                    buf_size,
+                    out_buf,
+                    add_buf,
+                    add_buf_size,
+                    max_size,
+                )
+            }
+        }
+
+        #[no_mangle]
+        pub extern "C" fn afl_custom_queue_new_entry(
+            data: *mut ::std::os::raw::c_void,
+            filename_new_queue: *const ::std::os::raw::c_char,
+            filename_orig_queue: *const ::std::os::raw::c_char,
+        ) {
+            $crate::wrappers::afl_custom_queue_new_entry_::<$mutator_type>(
+                data,
+                filename_new_queue,
+                filename_orig_queue,
+            )
+        }
+
+        #[no_mangle]
+        pub extern "C" fn afl_custom_queue_get(
+            data: *mut ::std::os::raw::c_void,
+            filename: *const ::std::os::raw::c_char,
+        ) -> u8 {
+            $crate::wrappers::afl_custom_queue_get_::<$mutator_type>(data, filename)
+        }
+
+        #[no_mangle]
+        pub extern "C" fn afl_custom_introspection(
+            data: *mut ::std::os::raw::c_void,
+        ) -> *const ::std::os::raw::c_char {
+            $crate::wrappers::afl_custom_introspection_::<$mutator_type>(data)
+        }
+
+        #[no_mangle]
+        pub extern "C" fn afl_custom_describe(
+            data: *mut ::std::os::raw::c_void,
+            max_description_len: usize,
+        ) -> *const ::std::os::raw::c_char {
+            $crate::wrappers::afl_custom_describe_::<$mutator_type>(data, max_description_len)
+        }
+
+        #[no_mangle]
+        pub extern "C" fn afl_custom_deinit(data: *mut ::std::os::raw::c_void) {
+            unsafe { $crate::wrappers::afl_custom_deinit_::<$mutator_type>(data) }
+        }
+    };
+}
+
+#[cfg(test)]
+/// this sanity test is supposed to just find out whether an empty mutator being exported by the macro compiles
+mod sanity_test {
+    #[cfg(feature = "afl_internals")]
+    use super::afl_state;
+
+    use super::{export_mutator, RawCustomMutator};
+
+    struct ExampleMutator;
+
+    impl RawCustomMutator for ExampleMutator {
+        #[cfg(feature = "afl_internals")]
+        fn init(_afl: &afl_state, _seed: u32) -> Self {
+            unimplemented!()
+        }
+
+        #[cfg(not(feature = "afl_internals"))]
+        fn init(_seed: u32) -> Self {
+            unimplemented!()
+        }
+
+        fn fuzz<'b, 's: 'b>(
+            &'s mut self,
+            _buffer: &'b mut [u8],
+            _add_buff: Option<&[u8]>,
+            _max_size: usize,
+        ) -> Option<&'b [u8]> {
+            unimplemented!()
+        }
+    }
+
+    export_mutator!(ExampleMutator);
+}
+
+#[allow(unused_variables)]
+/// A custom mutator.
+/// [`CustomMutator::handle_error`] will be called in case any method returns an [`Result::Err`].
+pub trait CustomMutator {
+    /// The error type. All methods must return the same error type.
+    type Error: Debug;
+
+    /// The method which handles errors.
+    /// By default, this method will log the error to stderr if the environment variable "`AFL_CUSTOM_MUTATOR_DEBUG`" is set and non-empty.
+    /// After logging the error, execution will continue on a best-effort basis.
+    ///
+    /// This default behaviour can be customized by implementing this method.
+    fn handle_error(err: Self::Error) {
+        if std::env::var("AFL_CUSTOM_MUTATOR_DEBUG")
+            .map(|v| !v.is_empty())
+            .unwrap_or(false)
+        {
+            eprintln!("Error in custom mutator: {:?}", err)
+        }
+    }
+
+    #[cfg(feature = "afl_internals")]
+    fn init(afl: &'static afl_state, seed: u32) -> Result<Self, Self::Error>
+    where
+        Self: Sized;
+
+    #[cfg(not(feature = "afl_internals"))]
+    fn init(seed: u32) -> Result<Self, Self::Error>
+    where
+        Self: Sized;
+
+    fn fuzz_count(&mut self, buffer: &[u8]) -> Result<u32, Self::Error> {
+        Ok(1)
+    }
+
+    fn fuzz<'b, 's: 'b>(
+        &'s mut self,
+        buffer: &'b mut [u8],
+        add_buff: Option<&[u8]>,
+        max_size: usize,
+    ) -> Result<Option<&'b [u8]>, Self::Error>;
+
+    fn queue_new_entry(
+        &mut self,
+        filename_new_queue: &Path,
+        filename_orig_queue: Option<&Path>,
+    ) -> Result<(), Self::Error> {
+        Ok(())
+    }
+
+    fn queue_get(&mut self, filename: &Path) -> Result<bool, Self::Error> {
+        Ok(true)
+    }
+
+    fn describe(&mut self, max_description: usize) -> Result<Option<&str>, Self::Error> {
+        Ok(Some(default_mutator_describe::<Self>(max_description)))
+    }
+
+    fn introspection(&mut self) -> Result<Option<&str>, Self::Error> {
+        Ok(None)
+    }
+}
+
+impl<M> RawCustomMutator for M
+where
+    M: CustomMutator,
+    M::Error: Debug,
+{
+    #[cfg(feature = "afl_internals")]
+    fn init(afl: &'static afl_state, seed: u32) -> Self
+    where
+        Self: Sized,
+    {
+        match Self::init(afl, seed) {
+            Ok(r) => r,
+            Err(e) => {
+                Self::handle_error(e);
+                panic!("Error in afl_custom_init")
+            }
+        }
+    }
+
+    #[cfg(not(feature = "afl_internals"))]
+    fn init(seed: u32) -> Self
+    where
+        Self: Sized,
+    {
+        match Self::init(seed) {
+            Ok(r) => r,
+            Err(e) => {
+                Self::handle_error(e);
+                panic!("Error in afl_custom_init")
+            }
+        }
+    }
+
+    fn fuzz_count(&mut self, buffer: &[u8]) -> u32 {
+        match self.fuzz_count(buffer) {
+            Ok(r) => r,
+            Err(e) => {
+                Self::handle_error(e);
+                0
+            }
+        }
+    }
+
+    fn fuzz<'b, 's: 'b>(
+        &'s mut self,
+        buffer: &'b mut [u8],
+        add_buff: Option<&[u8]>,
+        max_size: usize,
+    ) -> Option<&'b [u8]> {
+        match self.fuzz(buffer, add_buff, max_size) {
+            Ok(r) => r,
+            Err(e) => {
+                Self::handle_error(e);
+                None
+            }
+        }
+    }
+
+    fn queue_new_entry(&mut self, filename_new_queue: &Path, filename_orig_queue: Option<&Path>) {
+        match self.queue_new_entry(filename_new_queue, filename_orig_queue) {
+            Ok(r) => r,
+            Err(e) => {
+                Self::handle_error(e);
+            }
+        }
+    }
+
+    fn queue_get(&mut self, filename: &Path) -> bool {
+        match self.queue_get(filename) {
+            Ok(r) => r,
+            Err(e) => {
+                Self::handle_error(e);
+                false
+            }
+        }
+    }
+
+    fn describe(&mut self, max_description: usize) -> Option<&str> {
+        match self.describe(max_description) {
+            Ok(r) => r,
+            Err(e) => {
+                Self::handle_error(e);
+                None
+            }
+        }
+    }
+
+    fn introspection(&mut self) -> Option<&str> {
+        match self.introspection() {
+            Ok(r) => r,
+            Err(e) => {
+                Self::handle_error(e);
+                None
+            }
+        }
+    }
+}
+
+/// the default value to return from [`CustomMutator::describe`].
+fn default_mutator_describe<T: ?Sized>(max_len: usize) -> &'static str {
+    truncate_str_unicode_safe(std::any::type_name::<T>(), max_len)
+}
+
+#[cfg(all(test, not(feature = "afl_internals")))]
+mod default_mutator_describe {
+    struct MyMutator;
+    use super::CustomMutator;
+    impl CustomMutator for MyMutator {
+        type Error = ();
+
+        fn init(_: u32) -> Result<Self, Self::Error> {
+            Ok(Self)
+        }
+
+        fn fuzz<'b, 's: 'b>(
+            &'s mut self,
+            _: &'b mut [u8],
+            _: Option<&[u8]>,
+            _: usize,
+        ) -> Result<Option<&'b [u8]>, Self::Error> {
+            unimplemented!()
+        }
+    }
+
+    #[test]
+    fn test_default_describe() {
+        assert_eq!(
+            MyMutator::init(0).unwrap().describe(64).unwrap().unwrap(),
+            "custom_mutator::default_mutator_describe::MyMutator"
+        );
+    }
+}
+
+/// little helper function to truncate a `str` to a maximum of bytes while retaining unicode safety
+fn truncate_str_unicode_safe(s: &str, max_len: usize) -> &str {
+    if s.len() <= max_len {
+        s
+    } else {
+        if let Some((last_index, _)) = s
+            .char_indices()
+            .take_while(|(index, _)| *index <= max_len)
+            .last()
+        {
+            &s[..last_index]
+        } else {
+            ""
+        }
+    }
+}
+
+#[cfg(test)]
+mod truncate_test {
+    use super::truncate_str_unicode_safe;
+
+    #[test]
+    fn test_truncate() {
+        for (max_len, input, expected_output) in &[
+            (0usize, "a", ""),
+            (1, "a", "a"),
+            (1, "ä", ""),
+            (2, "ä", "ä"),
+            (3, "äa", "äa"),
+            (4, "äa", "äa"),
+            (1, "👎", ""),
+            (2, "👎", ""),
+            (3, "👎", ""),
+            (4, "👎", "👎"),
+            (1, "abc", "a"),
+            (2, "abc", "ab"),
+        ] {
+            let actual_output = truncate_str_unicode_safe(input, *max_len);
+            assert_eq!(
+                &actual_output, expected_output,
+                "{:#?} truncated to {} bytes should be {:#?}, but is {:#?}",
+                input, max_len, expected_output, actual_output
+            );
+        }
+    }
+}
diff --git a/custom_mutators/rust/example/Cargo.toml b/custom_mutators/rust/example/Cargo.toml
new file mode 100644
index 00000000..070d23b1
--- /dev/null
+++ b/custom_mutators/rust/example/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "example_mutator"
+version = "0.1.0"
+authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+custom_mutator = { path = "../custom_mutator" }
+
+[[example]]
+name = "example_mutator"
+path = "./src/example_mutator.rs"
+crate-type = ["cdylib"]
\ No newline at end of file
diff --git a/custom_mutators/rust/example/src/example_mutator.rs b/custom_mutators/rust/example/src/example_mutator.rs
new file mode 100644
index 00000000..c4711dd1
--- /dev/null
+++ b/custom_mutators/rust/example/src/example_mutator.rs
@@ -0,0 +1,50 @@
+#![cfg(unix)]
+#![allow(unused_variables)]
+
+use custom_mutator::{export_mutator, CustomMutator};
+
+struct ExampleMutator;
+
+impl CustomMutator for ExampleMutator {
+    type Error = ();
+
+    fn init(seed: u32) -> Result<Self, Self::Error> {
+        Ok(Self)
+    }
+
+    fn fuzz<'b, 's: 'b>(
+        &'s mut self,
+        buffer: &'b mut [u8],
+        add_buff: Option<&[u8]>,
+        max_size: usize,
+    ) -> Result<Option<&'b [u8]>, Self::Error> {
+        buffer.reverse();
+        Ok(Some(buffer))
+    }
+}
+
+struct OwnBufferExampleMutator {
+    own_buffer: Vec<u8>,
+}
+
+impl CustomMutator for OwnBufferExampleMutator {
+    type Error = ();
+
+    fn init(seed: u32) -> Result<Self, Self::Error> {
+        Ok(Self {
+            own_buffer: Vec::new(),
+        })
+    }
+
+    fn fuzz<'b, 's: 'b>(
+        &'s mut self,
+        buffer: &'b mut [u8],
+        add_buff: Option<&[u8]>,
+        max_size: usize,
+    ) -> Result<Option<&'b [u8]>, ()> {
+        self.own_buffer.reverse();
+        Ok(Some(self.own_buffer.as_slice()))
+    }
+}
+
+export_mutator!(ExampleMutator);
diff --git a/custom_mutators/rust/example_lain/Cargo.toml b/custom_mutators/rust/example_lain/Cargo.toml
new file mode 100644
index 00000000..29d606a4
--- /dev/null
+++ b/custom_mutators/rust/example_lain/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "example_lain"
+version = "0.1.0"
+authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+custom_mutator = { path = "../custom_mutator" }
+lain="0.5"
+
+[[example]]
+name = "example_lain"
+path = "./src/lain_mutator.rs"
+crate-type = ["cdylib"]
\ No newline at end of file
diff --git a/custom_mutators/rust/example_lain/rust-toolchain b/custom_mutators/rust/example_lain/rust-toolchain
new file mode 100644
index 00000000..07ade694
--- /dev/null
+++ b/custom_mutators/rust/example_lain/rust-toolchain
@@ -0,0 +1 @@
+nightly
\ No newline at end of file
diff --git a/custom_mutators/rust/example_lain/src/lain_mutator.rs b/custom_mutators/rust/example_lain/src/lain_mutator.rs
new file mode 100644
index 00000000..7099aeae
--- /dev/null
+++ b/custom_mutators/rust/example_lain/src/lain_mutator.rs
@@ -0,0 +1,61 @@
+#![cfg(unix)]
+
+use custom_mutator::{export_mutator, CustomMutator};
+use lain::{
+    mutator::Mutator,
+    prelude::*,
+    rand::{rngs::StdRng, SeedableRng},
+};
+
+#[derive(Debug, Mutatable, NewFuzzed, BinarySerialize)]
+struct MyStruct {
+    field_1: u8,
+
+    #[lain(bits = 3)]
+    field_2: u8,
+
+    #[lain(bits = 5)]
+    field_3: u8,
+
+    #[lain(min = 5, max = 10000)]
+    field_4: u32,
+
+    #[lain(ignore)]
+    ignored_field: u64,
+}
+
+struct LainMutator {
+    mutator: Mutator<StdRng>,
+    buffer: Vec<u8>,
+}
+
+impl CustomMutator for LainMutator {
+    type Error = ();
+
+    fn init(seed: u32) -> Result<Self, ()> {
+        Ok(Self {
+            mutator: Mutator::new(StdRng::seed_from_u64(seed as u64)),
+            buffer: Vec::new(),
+        })
+    }
+
+    fn fuzz<'b, 's: 'b>(
+        &'s mut self,
+        _buffer: &'b mut [u8],
+        _add_buff: Option<&[u8]>,
+        max_size: usize,
+    ) -> Result<Option<&'b [u8]>, ()> {
+        // we just sample an instance of MyStruct, ignoring the current input
+        let instance = MyStruct::new_fuzzed(&mut self.mutator, None);
+        let size = instance.serialized_size();
+        if size > max_size {
+            return Err(());
+        }
+        self.buffer.clear();
+        self.buffer.reserve(size);
+        instance.binary_serialize::<_, BigEndian>(&mut self.buffer);
+        Ok(Some(self.buffer.as_slice()))
+    }
+}
+
+export_mutator!(LainMutator);