about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--docs/custom_mutators.md7
-rw-r--r--examples/README.md8
-rw-r--r--examples/custom_mutators/Makefile7
-rw-r--r--examples/custom_mutators/example.c33
-rw-r--r--examples/custom_mutators/example.py19
-rw-r--r--include/afl-fuzz.h3
-rw-r--r--llvm_mode/Makefile2
-rw-r--r--src/afl-fuzz-mutators.c4
-rw-r--r--src/afl-fuzz-one.c4
-rw-r--r--src/afl-fuzz-python.c4
-rw-r--r--test/test-custom-mutator.c19
-rwxr-xr-xtest/test.sh126
12 files changed, 168 insertions, 68 deletions
diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md
index 2163b2d5..ef97b8d3 100644
--- a/docs/custom_mutators.md
+++ b/docs/custom_mutators.md
@@ -30,7 +30,8 @@ C/C++:
 void afl_custom_init(void *afl, unsigned int seed);
 size_t afl_custom_fuzz(uint8_t** buf, size_t buf_size, uint8_t* add_buf,
                        size_t add_buf_size, size_t max_size);
-size_t afl_custom_write_to_testcase(uint8_t* buf, size_t buf_size, uint8_t** out_buf);
+size_t afl_custom_write_to_testcase(uint8_t* buf, size_t buf_size,
+                                    uint8_t* out_buf, size_t out_buf_size);
 uint32_t afl_custom_init_trim(uint8_t* buf, size_t buf_size);
 void afl_custom_trim(uint8_t** out_buf, size_t* out_buf_size);
 uint32_t afl_custom_post_trim(uint8_t success);
@@ -111,7 +112,7 @@ def queue_new_entry(filename_new_queue, filename_orig_queue):
 
 - `queue_new_entry` (optional):
 
-    This methods is called after adding a new test case to the queue. 
+    This methods is called after adding a new test case to the queue.
 
 ### Trimming Support
 
@@ -167,7 +168,7 @@ a fallback to the builtin default trimming routine.
 Optionally, the following environment variables are supported:
 
 - `AFL_CUSTOM_MUTATOR_ONLY`
- 
+
     Disable all other mutation stages. This can prevent broken testcases
     (those that your Python module can't work with anymore) to fill up your
     queue. Best combined with a custom trimming routine (see below) because
diff --git a/examples/README.md b/examples/README.md
index 37fae1a0..6c09caeb 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -3,8 +3,6 @@
 Here's a quick overview of the stuff you can find in this directory:
 
   - custom_mutstors      - An example custom mutator
-  
-  - python_mutators      - Python mutators examples
 
   - argv_fuzzing         - a simple wrapper to allow cmdline to be fuzzed
                            (e.g., to test setuid programs).
@@ -15,8 +13,8 @@ Here's a quick overview of the stuff you can find in this directory:
   - bash_shellshock      - a simple hack used to find a bunch of
                            post-Shellshock bugs in bash.
 
-  - canvas_harness       - a test harness used to find browser bugs with a 
-                           corpus generated using simple image parsing 
+  - canvas_harness       - a test harness used to find browser bugs with a
+                           corpus generated using simple image parsing
                            binaries & afl-fuzz.
 
   - clang_asm_normalize  - a script that makes it easy to instrument
@@ -34,7 +32,7 @@ Here's a quick overview of the stuff you can find in this directory:
                            mode to speed up certain fuzzing jobs.
 
   - post_library         - an example of how to build postprocessors for AFL.
-  
+
   - socket_fuzzing       - a LD_PRELOAD library 'redirects' a socket to stdin
                            for fuzzing access with afl++
 
diff --git a/examples/custom_mutators/Makefile b/examples/custom_mutators/Makefile
index a83e87fe..463cefb1 100644
--- a/examples/custom_mutators/Makefile
+++ b/examples/custom_mutators/Makefile
@@ -1,2 +1,7 @@
-all:
+all: libexamplemutator.so
+
+libexamplemutator.so:
 	$(CC) $(CFLAGS) -fPIC -shared -g -I ../../include example.c -o libexamplemutator.so
+
+clean:
+	rm -rf libexamplemutator.so
diff --git a/examples/custom_mutators/example.c b/examples/custom_mutators/example.c
index a9764f5b..c8200b26 100644
--- a/examples/custom_mutators/example.c
+++ b/examples/custom_mutators/example.c
@@ -142,32 +142,25 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
 size_t afl_custom_pre_save(my_mutator_t *data, uint8_t *buf, size_t buf_size,
                            uint8_t **out_buf) {
 
-  if (data->pre_save_size < buf_size + 5) {
+  uint8_t *pre_save_buf = maybe_grow(BUF_PARAMS(data, pre_save), buf_size + 5);
+  if (!pre_save_buf) {
 
-    data->pre_save_buf = maybe_grow(BUF_PARAMS(data, pre_save), buf_size + 5);
-    if (!data->pre_save_buf) {
-
-      perror("custom mutator realloc failed.");
-      *out_buf = NULL;
-      return 0;
-
-    }
-
-    data->pre_save_size = buf_size + 5;
+    perror("custom mutator realloc failed.");
+    *out_buf = NULL;
+    return 0;
 
   }
 
-  *out_buf = data->pre_save_buf;
+  memcpy(pre_save_buf + 5, buf, buf_size);
+  pre_save_buf[0] = 'A';
+  pre_save_buf[1] = 'F';
+  pre_save_buf[2] = 'L';
+  pre_save_buf[3] = '+';
+  pre_save_buf[4] = '+';
 
-  memcpy(*out_buf + 5, buf, buf_size);
-  size_t out_buf_size = buf_size + 5;
-  *out_buf[0] = 'A';
-  *out_buf[1] = 'F';
-  *out_buf[2] = 'L';
-  *out_buf[3] = '+';
-  *out_buf[4] = '+';
+  *out_buf = pre_save_buf;
 
-  return out_buf_size;
+  return buf_size + 5;
 
 }
 
diff --git a/examples/custom_mutators/example.py b/examples/custom_mutators/example.py
index 6bacfa05..9e95eed6 100644
--- a/examples/custom_mutators/example.py
+++ b/examples/custom_mutators/example.py
@@ -17,6 +17,13 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
 import random
 
 
+COMMANDS = [
+    b"GET",
+    b"PUT",
+    b"DEL",
+]
+
+
 def init(seed):
     '''
     Called once when AFLFuzz starts up. Used to seed our RNG.
@@ -27,6 +34,10 @@ def init(seed):
     random.seed(seed)
 
 
+def deinit():
+    pass
+
+
 def fuzz(buf, add_buf, max_size):
     '''
     Called per fuzzing iteration.
@@ -44,8 +55,9 @@ def fuzz(buf, add_buf, max_size):
     @rtype: bytearray
     @return: A new bytearray containing the mutated data
     '''
-    ret = bytearray(buf)
-    # Do something interesting with ret
+    ret = bytearray(100)
+
+    ret[:3] = random.choice(COMMANDS)
 
     return ret
 
@@ -164,11 +176,10 @@ def fuzz(buf, add_buf, max_size):
 #     '''
 #     Called after adding a new test case to the queue
 #
-#     @type filename_new_queue: str 
+#     @type filename_new_queue: str
 #     @param filename_new_queue: File name of the new queue entry
 #
 #     @type filename_orig_queue: str
 #     @param filename_orig_queue: File name of the original queue entry
 #     '''
 #     pass
-
diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h
index a265c1a3..fcbc09e5 100644
--- a/include/afl-fuzz.h
+++ b/include/afl-fuzz.h
@@ -566,9 +566,6 @@ typedef struct afl_state {
 
   /* Custom mutators */
   struct custom_mutator *mutator;
-#ifdef USE_PYTHON
-  struct custom_mutator *py_mutator;
-#endif
 
   /* cmplog forkserver ids */
   s32 cmplog_fsrv_ctl_fd, cmplog_fsrv_st_fd;
diff --git a/llvm_mode/Makefile b/llvm_mode/Makefile
index 5b6fcca4..006d115d 100644
--- a/llvm_mode/Makefile
+++ b/llvm_mode/Makefile
@@ -329,7 +329,7 @@ install: all
 	install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH)
 ifndef AFL_TRACE_PC
 	if [ -f ../afl-clang-fast -a -f ../libLLVMInsTrim.so -a -f ../afl-llvm-rt.o ]; then set -e; install -m 755 ../afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 ../libLLVMInsTrim.so ../afl-llvm-pass.so ../afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
-	if [ -f ../afl-clang-lto -a -f ../afl-ld ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-ld $${DESTDIR}$(HELPER_PATH); ln -sf afl-ld $${DESTDIR}$(HELPER_PATH)/ld; install -m 755 ../afl-llvm-lto-instrumentation.so $${DESTDIR}$(HELPER_PATH); install -m 755 ../afl-llvm-lto-whitelist.so $${DESTDIR}$(HELPER_PATH); fi
+	if [ -f afl-clang-lto -a -f afl-ld ]; then set -e; install -m 755 afl-clang-lto $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 afl-ld $${DESTDIR}$(HELPER_PATH); ln -sf afl-ld $${DESTDIR}$(HELPER_PATH)/ld; install -m 755 afl-llvm-lto-instrumentation.so $${DESTDIR}$(HELPER_PATH); install -m 755 afl-llvm-lto-whitelist.so $${DESTDIR}$(HELPER_PATH); fi
 else
 	if [ -f ../afl-clang-fast -a -f ../afl-llvm-rt.o ]; then set -e; install -m 755 ../afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 ../afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
 endif
diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c
index 754b2190..1a5528a2 100644
--- a/src/afl-fuzz-mutators.c
+++ b/src/afl-fuzz-mutators.c
@@ -193,8 +193,8 @@ void load_custom_mutator(afl_state_t *afl, const char *fn) {
 
   /* Initialize the custom mutator */
   if (afl->mutator->afl_custom_init)
-    afl->mutator->data =
-        afl->mutator->afl_custom_init(afl, rand_below(afl, 0xFFFFFFFF));
+    afl->mutator->data = afl->mutator->afl_custom_init(
+      afl, rand_below(afl, 0xFFFFFFFF));
 
 }
 
diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c
index b1bbad0a..2e49e19b 100644
--- a/src/afl-fuzz-one.c
+++ b/src/afl-fuzz-one.c
@@ -1621,8 +1621,6 @@ custom_mutator_stage:
     if (unlikely(!mutated_buf))
       FATAL("Error in custom_fuzz. Size returned: %zd", mutated_size);
 
-    if (mutated_size > len) afl->out_size = mutated_size;
-
     if (mutated_size > 0) {
 
       if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) {
@@ -1649,7 +1647,7 @@ custom_mutator_stage:
 
     }
 
-    out_buf = ck_maybe_grow(BUF_PARAMS(out), len);
+    /* `(afl->)out_buf` may have been changed by the call to custom_fuzz */
     memcpy(out_buf, in_buf, len);
 
   }
diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c
index 91e5b084..12c3a09d 100644
--- a/src/afl-fuzz-python.c
+++ b/src/afl-fuzz-python.c
@@ -133,8 +133,8 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) {
   if (py_module != NULL) {
 
     u8 py_notrim = 0, py_idx;
+    /* init, required */
     py_functions[PY_FUNC_INIT] = PyObject_GetAttrString(py_module, "init");
-    py_functions[PY_FUNC_DEINIT] = PyObject_GetAttrString(py_module, "deinit");
     py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz");
     py_functions[PY_FUNC_PRE_SAVE] =
         PyObject_GetAttrString(py_module, "pre_save");
@@ -151,6 +151,7 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) {
         PyObject_GetAttrString(py_module, "queue_get");
     py_functions[PY_FUNC_QUEUE_NEW_ENTRY] =
         PyObject_GetAttrString(py_module, "queue_new_entry");
+    py_functions[PY_FUNC_DEINIT] = PyObject_GetAttrString(py_module, "deinit");
 
     for (py_idx = 0; py_idx < PY_FUNC_COUNT; ++py_idx) {
 
@@ -302,6 +303,7 @@ void load_custom_mutator_py(afl_state_t *afl, char *module_name) {
 
   py_mutator_t *py_mutator;
   py_mutator = init_py_module(afl, module_name);
+  afl->mutator->data = py_mutator;
   if (!py_mutator) { FATAL("Failed to load python mutator."); }
 
   PyObject **py_functions = py_mutator->py_functions;
diff --git a/test/test-custom-mutator.c b/test/test-custom-mutator.c
new file mode 100644
index 00000000..83baafab
--- /dev/null
+++ b/test/test-custom-mutator.c
@@ -0,0 +1,19 @@
+/**
+ * Reference: https://github.com/bruce30262/libprotobuf-mutator_fuzzing_learning/blob/master/4_libprotobuf_aflpp_custom_mutator/vuln.c
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int main(int argc, char *argv[])
+{
+    char str[100];
+    read(0, str, 100);
+    if( str[6] == 'A') {
+        abort();
+    }
+    return 0;
+}
diff --git a/test/test.sh b/test/test.sh
index ec4e71d0..3e1b6c43 100755
--- a/test/test.sh
+++ b/test/test.sh
@@ -73,7 +73,7 @@ export ASAN_OPTIONS=detect_leaks=0:allocator_may_return_null=1:abort_on_error=1:
 # on OpenBSD we need to work with llvm from /usr/local/bin
 test -e /usr/local/bin/opt && {
   export PATH=/usr/local/bin:${PATH}
-} 
+}
 # on MacOS X we prefer afl-clang over afl-gcc, because
 # afl-gcc does not work there
 test `uname -s` = 'Darwin' -o `uname -s` = 'FreeBSD' && {
@@ -142,11 +142,11 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc
       CODE=1
     }
     rm -f test-compcov.harden
-  } || { 
+  } || {
     $ECHO "$RED[!] ${AFL_GCC} hardened mode compilation failed"
     CODE=1
   }
-  # now we want to be sure that afl-fuzz is working  
+  # now we want to be sure that afl-fuzz is working
   # make sure core_pattern is set to core on linux
   (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && {
     $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET"
@@ -210,13 +210,13 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc
     unset AFL_QUIET
   }
   rm -f test-instr.plain
- } || { 
+ } || {
   $ECHO "$YELLOW[-] afl is not compiled, cannot test"
   INCOMPLETE=1
  }
-} || { 
+} || {
  $ECHO "$YELLOW[-] not an intel platform, cannot test afl-gcc"
-} 
+}
 
 $ECHO "$BLUE[*] Testing: llvm_mode, afl-showmap, afl-fuzz, afl-cmin and afl-tmin"
 test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
@@ -248,7 +248,7 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
           CODE=1
         }
       }
-    } || { 
+    } || {
       $ECHO "$RED[!] llvm_mode instrumentation failed"
       CODE=1
     }
@@ -265,11 +265,11 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
       CODE=1
     }
     rm -f test-compcov.harden
-  } || { 
+  } || {
     $ECHO "$RED[!] llvm_mode hardened mode compilation failed"
     CODE=1
   }
-  # now we want to be sure that afl-fuzz is working  
+  # now we want to be sure that afl-fuzz is working
   (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && {
     $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET"
     true
@@ -373,7 +373,7 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && {
       $ECHO "$RED[!] llvm_mode whitelist feature failed"
       CODE=1
     }
-  } || { 
+  } || {
     $ECHO "$RED[!] llvm_mode whitelist feature compilation failed"
     CODE=1
   }
@@ -426,7 +426,7 @@ test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && {
           CODE=1
         }
       }
-    } || { 
+    } || {
       $ECHO "$RED[!] llvm_mode LTO instrumentation failed"
       CODE=1
     }
@@ -447,7 +447,7 @@ test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && {
 #      $ECHO "$RED[!] llvm_mode LTO whitelist feature failed"
 #      CODE=1
 #    }
-#  } || { 
+#  } || {
 #    $ECHO "$RED[!] llvm_mode LTO whitelist feature compilation failed"
 #    CODE=1
 #  }
@@ -483,7 +483,7 @@ test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && {
       diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && {
         $ECHO "$RED[!] gcc_plugin instrumentation should be different on different input but is not"
         CODE=1
-      } || { 
+      } || {
         $ECHO "$GREEN[+] gcc_plugin instrumentation present and working correctly"
         TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain.gccpi 2>&1 | grep Captur | awk '{print$3}'`
         test "$TUPLES" -gt 3 -a "$TUPLES" -lt 7 && {
@@ -516,7 +516,7 @@ test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && {
     $ECHO "$RED[!] gcc_plugin hardened mode compilation failed"
     CODE=1
   }
-  # now we want to be sure that afl-fuzz is working  
+  # now we want to be sure that afl-fuzz is working
   (test "$(uname -s)" = "Linux" && test "$(sysctl kernel.core_pattern)" != "kernel.core_pattern = core" && {
     $ECHO "$YELLOW[-] we should not run afl-fuzz with enabled core dumps. Run 'sudo sh afl-system-config'.$RESET"
     true
@@ -552,11 +552,11 @@ test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && {
   test -e test-compcov && {
     echo 1 | ../afl-showmap -m ${MEM_LIMIT} -o - -r -- ./test-compcov 2>&1 | grep -q "Captured 1 tuples" && {
       $ECHO "$GREEN[+] gcc_plugin whitelist feature works correctly"
-    } || { 
+    } || {
       $ECHO "$RED[!] gcc_plugin whitelist feature failed"
       CODE=1
     }
-  } || { 
+  } || {
     $ECHO "$RED[!] gcc_plugin whitelist feature compilation failed"
     CODE=1
   }
@@ -585,7 +585,7 @@ test -e ../libtokencap.so && {
   AFL_TOKEN_FILE=token.out LD_PRELOAD=../libtokencap.so DYLD_INSERT_LIBRARIES=../libtokencap.so DYLD_FORCE_FLAT_NAMESPACE=1 ./test-compcov foobar > /dev/null 2>&1
   grep -q BUGMENOT token.out > /dev/null 2>&1 && {
     $ECHO "$GREEN[+] libtokencap did successfully capture tokens"
-  } || { 
+  } || {
     $ECHO "$RED[!] libtokencap did not capture tokens"
     CODE=1
   }
@@ -604,7 +604,7 @@ test -e ../libdislocator.so && {
     $ECHO "$RED[!] libdislocator did not detect the memory corruption"
     CODE=1
   } || {
-    $ECHO "$GREEN[+] libdislocator did successfully detect the memory corruption" 
+    $ECHO "$GREEN[+] libdislocator did successfully detect the memory corruption"
   }
   rm -f test.out core test-compcov.core core.test-compcov
 } || {
@@ -700,7 +700,7 @@ test -e ../afl-qemu-trace && {
         test -e ../libcompcov.so && {
           $ECHO "$GREY[*] running afl-fuzz for qemu_mode compcov, this will take approx 10 seconds"
           {
-            export AFL_PRELOAD=../libcompcov.so 
+            export AFL_PRELOAD=../libcompcov.so
             export AFL_COMPCOV_LEVEL=2
             ../afl-fuzz -m ${MEM_LIMIT} -V10 -Q -i in -o out -- ./test-compcov >>errors 2>&1
             unset AFL_PRELOAD
@@ -720,10 +720,10 @@ test -e ../afl-qemu-trace && {
           INCOMPLETE=1
         }
         rm -f errors
-      } || { 
+      } || {
        $ECHO "$YELLOW[-] not an intel or arm platform, cannot test qemu_mode compcov"
       }
-      
+
       test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc" -o "$SYS" = "aarch64" -o ! "${SYS%%arm*}" && {
         $ECHO "$GREY[*] running afl-fuzz for persistent qemu_mode, this will take approx 10 seconds"
         {
@@ -757,9 +757,9 @@ test -e ../afl-qemu-trace && {
           exit 1
         }
         rm -rf in out errors
-      } || { 
+      } || {
        $ECHO "$YELLOW[-] not an intel or arm platform, cannot test persistent qemu_mode"
-      } 
+      }
 
       test -e ../qemu_mode/unsigaction/unsigaction32.so && {
         ${AFL_CC} -o test-unsigaction32 -m32 test-unsigaction.c >> errors 2>&1 && {
@@ -824,7 +824,7 @@ test -e ../afl-qemu-trace && {
     $ECHO "$RED[!] gcc compilation of test targets failed - what is going on??"
     CODE=1
   }
-  
+
   rm -f test-instr test-compcov
 } || {
   $ECHO "$YELLOW[-] qemu_mode is not compiled, cannot test"
@@ -887,12 +887,88 @@ test -d ../unicorn_mode/unicornafl && {
     $ECHO "$RED[!] missing sample binaries in unicorn_mode/samples/ - what is going on??"
     CODE=1
   }
-  
+
 } || {
   $ECHO "$YELLOW[-] unicorn_mode is not compiled, cannot test"
   INCOMPLETE=1
 }
 
+$ECHO "$BLUE[*] Testing: custom mutator"
+unset AFL_CC  # Test case "gcc_plugin" sets AFL_CC to "gcc". We reset it to use the default compiler
+test `uname -s` = 'Darwin' && {
+  CUSTOM_MUTATOR_PATH=$( realpath ../examples/custom_mutators )
+} || {
+  CUSTOM_MUTATOR_PATH=$( readlink -f ../examples/custom_mutators )
+}
+test -e test-custom-mutator.c -a -e ${CUSTOM_MUTATOR_PATH}/example.c -a -e ${CUSTOM_MUTATOR_PATH}/example.py && {
+  # Compile the vulnerable program
+  ../afl-clang-fast -o test-custom-mutator test-custom-mutator.c > /dev/null 2>&1
+  # Compile the custom mutator
+  make -C ../examples/custom_mutators libexamplemutator.so > /dev/null 2>&1
+  test -e test-custom-mutator -a -e ${CUSTOM_MUTATOR_PATH}/libexamplemutator.so && {
+    # Create input directory
+    mkdir -p in
+    echo "00000" > in/in
+
+    # Run afl-fuzz w/ the C mutator
+    $ECHO "$GREY[*] running afl-fuzz for the C mutator, this will take approx 10 seconds"
+    {
+      AFL_CUSTOM_MUTATOR_LIBRARY=${CUSTOM_MUTATOR_PATH}/libexamplemutator.so ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1
+    } >>errors 2>&1
+
+    # Check results
+    test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && {  # TODO: update here
+      $ECHO "$GREEN[+] afl-fuzz is working correctly with the C mutator"
+    } || {
+      echo CUT------------------------------------------------------------------CUT
+      cat errors
+      echo CUT------------------------------------------------------------------CUT
+      $ECHO "$RED[!] afl-fuzz is not working correctly with the C mutator"
+      CODE=1
+    }
+
+    # Clean
+    rm -rf out errors
+
+    # Run afl-fuzz w/ the Python mutator
+    $ECHO "$GREY[*] running afl-fuzz for the Python mutator, this will take approx 10 seconds"
+    {
+      export PYTHONPATH=${CUSTOM_MUTATOR_PATH}
+      export AFL_PYTHON_MODULE=example
+      ../afl-fuzz -V10 -m ${MEM_LIMIT} -i in -o out -- ./test-custom-mutator >>errors 2>&1
+      unset PYTHONPATH
+      unset AFL_PYTHON_MODULE
+    } >>errors 2>&1
+
+    # Check results
+    test -n "$( ls out/crashes/id:000000* 2>/dev/null )" && {  # TODO: update here
+      $ECHO "$GREEN[+] afl-fuzz is working correctly with the Python mutator"
+    } || {
+      echo CUT------------------------------------------------------------------CUT
+      cat errors
+      echo CUT------------------------------------------------------------------CUT
+      $ECHO "$RED[!] afl-fuzz is not working correctly with the Python mutator"
+      CODE=1
+    }
+
+    # Clean
+    rm -rf in out errors
+    rm -rf ${CUSTOM_MUTATOR_PATH}/__pycache__/
+  } || {
+    ls .
+    ls ${CUSTOM_MUTATOR_PATH}
+    $ECHO "$RED[!] cannot compile the test program or the custom mutator"
+    CODE=1
+  }
+
+  make -C ../examples/custom_mutators clean > /dev/null 2>&1
+  rm -f test-custom-mutator
+} || {
+  $ECHO "$YELLOW[-] no custom mutators in $CUSTOM_MUTATOR_PATH, cannot test"
+  INCOMPLETE=1
+}
+unset CUSTOM_MUTATOR_PATH
+
 $ECHO "$GREY[*] all test cases completed.$RESET"
 test "$INCOMPLETE" = "0" && $ECHO "$GREEN[+] all test cases executed"
 test "$INCOMPLETE" = "1" && $ECHO "$YELLOW[-] not all test cases were executed"