about summary refs log tree commit diff
diff options
context:
space:
mode:
authorvan Hauser <vh@thc.org>2020-11-10 14:08:21 +0100
committervan Hauser <vh@thc.org>2020-11-10 14:08:21 +0100
commit8e1047f5efaece663bba9b8ef86d181198db5101 (patch)
treec2c2b38af0833f815a6b28b0c435fbe19fc65344
parent166130324898071a08e178dfeb901af44168236e (diff)
downloadafl++-8e1047f5efaece663bba9b8ef86d181198db5101.tar.gz
support custom mutator introspection
-rw-r--r--docs/Changelog.md1
-rw-r--r--docs/custom_mutators.md10
-rw-r--r--include/afl-fuzz.h33
-rw-r--r--src/afl-fuzz-bitmap.c81
-rw-r--r--src/afl-fuzz-mutators.c7
-rw-r--r--src/afl-fuzz-one.c8
-rw-r--r--src/afl-fuzz-python.c33
7 files changed, 159 insertions, 14 deletions
diff --git a/docs/Changelog.md b/docs/Changelog.md
index 50c1d48a..a69f2ff4 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -37,6 +37,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
     - added NO_SPLICING compile option and makefile define
     - added INTROSPECTION make target that writes all mutations to
       out/NAME/introspection.txt
+    - added INTROSPECTION support for custom modules
     - print special compile time options used in help output
   - instrumentation
     - We received an enhanced gcc_plugin module from AdaCore, thank you
diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md
index 81ee9de4..2516e511 100644
--- a/docs/custom_mutators.md
+++ b/docs/custom_mutators.md
@@ -42,6 +42,7 @@ size_t afl_custom_havoc_mutation(void *data, unsigned char *buf, size_t buf_size
 unsigned char afl_custom_havoc_mutation_probability(void *data);
 unsigned char afl_custom_queue_get(void *data, const unsigned char *filename);
 void afl_custom_queue_new_entry(void *data, const unsigned char *filename_new_queue, const unsigned int *filename_orig_queue);
+const char* afl_custom_introspection(my_mutator_t *data);
 void afl_custom_deinit(void *data);
 ```
 
@@ -81,6 +82,9 @@ def queue_new_entry(filename_new_queue, filename_orig_queue):
     pass
 ```
 
+def introspection():
+    return string
+
 ### Custom Mutation
 
 - `init`:
@@ -130,6 +134,12 @@ def queue_new_entry(filename_new_queue, filename_orig_queue):
 
     This methods is called after adding a new test case to the queue.
 
+- `introspection` (optional):
+
+    This method is called after a new queue entry, crash or timeout is
+    discovered if compiled with INTROSPECTION. The custom mutator can then
+    return a string (const char *) that reports the exact mutations used.
+
 - `deinit`:
 
     The last method to be called, deinitializing the state.
diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h
index e59d5f90..c355263b 100644
--- a/include/afl-fuzz.h
+++ b/include/afl-fuzz.h
@@ -310,6 +310,7 @@ enum {
   /* 09 */ PY_FUNC_HAVOC_MUTATION_PROBABILITY,
   /* 10 */ PY_FUNC_QUEUE_GET,
   /* 11 */ PY_FUNC_QUEUE_NEW_ENTRY,
+  /* 12 */ PY_FUNC_INTROSPECTION,
   PY_FUNC_COUNT
 
 };
@@ -684,6 +685,8 @@ typedef struct afl_state {
 
   u32 custom_mutators_count;
 
+  struct custom_mutator *current_custom_fuzz;
+
   list_t custom_mutator_list;
 
   /* this is a fixed buffer of size map_size that can be used by any function if
@@ -748,6 +751,15 @@ struct custom_mutator {
   void *(*afl_custom_init)(afl_state_t *afl, unsigned int seed);
 
   /**
+   * When afl-fuzz was compiled with INTROSPECTION=1 then custom mutators can
+   * also give introspection information back with this function.
+   *
+   * @param data pointer returned in afl_custom_init for this fuzz case
+   * @return pointer to a text string (const char*)
+   */
+  const char *(*afl_custom_introspection)(void *data);
+
+  /**
    * This method is called just before fuzzing a queue entry with the custom
    * mutator, and receives the initial buffer. It should return the number of
    * fuzzes to perform.
@@ -953,16 +965,17 @@ u8   trim_case_custom(afl_state_t *, struct queue_entry *q, u8 *in_buf,
 struct custom_mutator *load_custom_mutator_py(afl_state_t *, char *);
 void                   finalize_py_module(void *);
 
-u32    fuzz_count_py(void *, const u8 *, size_t);
-size_t post_process_py(void *, u8 *, size_t, u8 **);
-s32    init_trim_py(void *, u8 *, size_t);
-s32    post_trim_py(void *, u8);
-size_t trim_py(void *, u8 **);
-size_t havoc_mutation_py(void *, u8 *, size_t, u8 **, size_t);
-u8     havoc_mutation_probability_py(void *);
-u8     queue_get_py(void *, const u8 *);
-void   queue_new_entry_py(void *, const u8 *, const u8 *);
-void   deinit_py(void *);
+u32         fuzz_count_py(void *, const u8 *, size_t);
+size_t      post_process_py(void *, u8 *, size_t, u8 **);
+s32         init_trim_py(void *, u8 *, size_t);
+s32         post_trim_py(void *, u8);
+size_t      trim_py(void *, u8 **);
+size_t      havoc_mutation_py(void *, u8 *, size_t, u8 **, size_t);
+u8          havoc_mutation_probability_py(void *);
+u8          queue_get_py(void *, const u8 *);
+const char *introspection_py(void *);
+void        queue_new_entry_py(void *, const u8 *, const u8 *);
+void        deinit_py(void *);
 
 #endif
 
diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c
index 735420c3..132499d6 100644
--- a/src/afl-fuzz-bitmap.c
+++ b/src/afl-fuzz-bitmap.c
@@ -588,8 +588,32 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
     add_to_queue(afl, queue_fn, len, 0);
 
 #ifdef INTROSPECTION
-    fprintf(afl->introspection_file, "QUEUE %s = %s\n", afl->mutation,
-            afl->queue_top->fname);
+    if (afl->mutation[0] != 0) {
+
+      fprintf(afl->introspection_file, "QUEUE %s = %s\n", afl->mutation,
+              afl->queue_top->fname);
+
+    } else if (afl->custom_mutators_count && afl->current_custom_fuzz) {
+
+      LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
+
+        if (afl->current_custom_fuzz == el && el->afl_custom_introspection) {
+
+          const char *ptr = el->afl_custom_introspection(el->data);
+
+          if (ptr != NULL && *ptr != 0) {
+
+            fprintf(afl->introspection_file, "QUEUE CUSTOM %s = %s\n", ptr,
+                    afl->queue_top->fname);
+
+          }
+
+        }
+
+      });
+
+    }
+
 #endif
 
     if (hnb == 2) {
@@ -665,7 +689,32 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
 
       ++afl->unique_tmouts;
 #ifdef INTROSPECTION
-      fprintf(afl->introspection_file, "UNIQUE_TIMEOUT %s\n", afl->mutation);
+      if (afl->mutation[0] != 0) {
+
+        fprintf(afl->introspection_file, "UNIQUE_TIMEOUT %s\n", afl->mutation);
+
+      } else if (afl->custom_mutators_count && afl->current_custom_fuzz) {
+
+        LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
+
+          if (afl->current_custom_fuzz == el && el->afl_custom_introspection) {
+
+            const char *ptr = el->afl_custom_introspection(el->data);
+
+            if (ptr != NULL && *ptr != 0) {
+
+              fprintf(afl->introspection_file,
+                      "UNIQUE_TIMEOUT CUSTOM %s = %s\n", ptr,
+                      afl->queue_top->fname);
+
+            }
+
+          }
+
+        });
+
+      }
+
 #endif
 
       /* Before saving, we make sure that it's a genuine hang by re-running
@@ -751,7 +800,31 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
 
       ++afl->unique_crashes;
 #ifdef INTROSPECTION
-      fprintf(afl->introspection_file, "UNIQUE_CRASH %s\n", afl->mutation);
+      if (afl->mutation[0] != 0) {
+
+        fprintf(afl->introspection_file, "UNIQUE_CRASH %s\n", afl->mutation);
+
+      } else if (afl->custom_mutators_count && afl->current_custom_fuzz) {
+
+        LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
+
+          if (afl->current_custom_fuzz == el && el->afl_custom_introspection) {
+
+            const char *ptr = el->afl_custom_introspection(el->data);
+
+            if (ptr != NULL && *ptr != 0) {
+
+              fprintf(afl->introspection_file, "UNIQUE_CRASH CUSTOM %s = %s\n",
+                      ptr, afl->queue_top->fname);
+
+            }
+
+          }
+
+        });
+
+      }
+
 #endif
       if (unlikely(afl->infoexec)) {
 
diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c
index c4d7233c..1d14f657 100644
--- a/src/afl-fuzz-mutators.c
+++ b/src/afl-fuzz-mutators.c
@@ -166,6 +166,13 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) {
 
   }
 
+  /* "afl_custom_introspection", optional */
+#ifdef INTROSPECTION
+  mutator->afl_custom_introspection = dlsym(dh, "afl_custom_introspection");
+  if (!mutator->afl_custom_introspection)
+    ACTF("optional symbol 'afl_custom_introspection' not found.");
+#endif
+
   /* "afl_custom_fuzz_count", optional */
   mutator->afl_custom_fuzz_count = dlsym(dh, "afl_custom_fuzz_count");
   if (!mutator->afl_custom_fuzz_count)
diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c
index 91bbced6..64365ebb 100644
--- a/src/afl-fuzz-one.c
+++ b/src/afl-fuzz-one.c
@@ -1780,10 +1780,16 @@ custom_mutator_stage:
 
   orig_hit_cnt = afl->queued_paths + afl->unique_crashes;
 
+#ifdef INTROSPECTION
+  afl->mutation[0] = 0;
+#endif
+
   LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
 
     if (el->afl_custom_fuzz) {
 
+      afl->current_custom_fuzz = el;
+
       if (el->afl_custom_fuzz_count)
         afl->stage_max = el->afl_custom_fuzz_count(el->data, out_buf, len);
       else
@@ -1889,6 +1895,8 @@ custom_mutator_stage:
 
   });
 
+  afl->current_custom_fuzz = NULL;
+
   if (!has_custom_fuzz) goto havoc_stage;
 
   new_hit_cnt = afl->queued_paths + afl->unique_crashes;
diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c
index adb92649..fe16bc46 100644
--- a/src/afl-fuzz-python.c
+++ b/src/afl-fuzz-python.c
@@ -163,6 +163,8 @@ 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_INTROSPECTION] =
+        PyObject_GetAttrString(py_module, "introspection");
     py_functions[PY_FUNC_DEINIT] = PyObject_GetAttrString(py_module, "deinit");
     if (!py_functions[PY_FUNC_DEINIT])
       FATAL("deinit function not found in python module");
@@ -381,6 +383,15 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl,
 
   }
 
+  #ifdef INTROSPECTION
+  if (py_functions[PY_FUNC_INTROSPECTION]) {
+
+    mutator->afl_custom_introspection = introspection_py;
+
+  }
+
+  #endif
+
   OKF("Python mutator '%s' installed successfully.", module_name);
 
   /* Initialize the custom mutator */
@@ -679,6 +690,28 @@ u8 havoc_mutation_probability_py(void *py_mutator) {
 
 }
 
+const char *introspection_py(void *py_mutator) {
+
+  PyObject *py_args, *py_value;
+
+  py_args = PyTuple_New(0);
+  py_value = PyObject_CallObject(
+      ((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_INTROSPECTION],
+      py_args);
+  Py_DECREF(py_args);
+
+  if (py_value == NULL) {
+
+    return NULL;
+
+  } else {
+
+    return PyByteArray_AsString(py_value);
+
+  }
+
+}
+
 u8 queue_get_py(void *py_mutator, const u8 *filename) {
 
   PyObject *py_args, *py_value;