aboutsummaryrefslogtreecommitdiff
path: root/custom_mutators
diff options
context:
space:
mode:
Diffstat (limited to 'custom_mutators')
-rw-r--r--custom_mutators/README.md10
-rw-r--r--custom_mutators/autotokens/Makefile26
-rw-r--r--custom_mutators/autotokens/README34
-rw-r--r--custom_mutators/autotokens/autotokens.cpp1101
-rw-r--r--custom_mutators/examples/custom_mutator_helpers.h342
-rw-r--r--custom_mutators/examples/custom_send.c63
-rw-r--r--custom_mutators/examples/example.c116
-rw-r--r--custom_mutators/examples/post_library_gif.so.c53
-rw-r--r--custom_mutators/examples/post_library_png.so.c24
-rw-r--r--custom_mutators/examples/simple_example.c28
-rwxr-xr-xcustom_mutators/gramatron/build_gramatron_mutator.sh4
-rwxr-xr-xcustom_mutators/grammar_mutator/build_grammar_mutator.sh4
-rw-r--r--custom_mutators/libafl_base/Cargo.toml2
-rw-r--r--custom_mutators/libafl_base/src/lib.rs36
-rw-r--r--custom_mutators/rust/custom_mutator-sys/Cargo.toml6
-rw-r--r--custom_mutators/rust/custom_mutator-sys/build.rs4
-rw-r--r--custom_mutators/rust/custom_mutator-sys/src/lib.rs2
-rw-r--r--custom_mutators/rust/custom_mutator/Cargo.toml2
-rw-r--r--custom_mutators/rust/custom_mutator/src/lib.rs109
-rw-r--r--custom_mutators/rust/example/Cargo.toml2
-rw-r--r--custom_mutators/rust/example_lain/Cargo.toml2
21 files changed, 1396 insertions, 574 deletions
diff --git a/custom_mutators/README.md b/custom_mutators/README.md
index 0289e150..8d01856f 100644
--- a/custom_mutators/README.md
+++ b/custom_mutators/README.md
@@ -11,6 +11,16 @@ The `./examples` folder contains examples for custom mutators in python and C.
In `./rust`, you will find rust bindings, including a simple example in `./rust/example` and an example for structured fuzzing, based on lain, in`./rust/example_lain`.
+## The AFL++ grammar agnostic grammar mutator
+
+In `./autotokens` you find a token-level fuzzer that does not need to know
+anything about the grammar of an input as long as it is in ascii and allows
+whitespace.
+It is very fast and effective.
+
+If you are looking for an example of how to effectively create a custom
+mutator take a look at this one.
+
## The AFL++ Grammar Mutator
If you use git to clone AFL++, then the following will incorporate our
diff --git a/custom_mutators/autotokens/Makefile b/custom_mutators/autotokens/Makefile
new file mode 100644
index 00000000..0daba17d
--- /dev/null
+++ b/custom_mutators/autotokens/Makefile
@@ -0,0 +1,26 @@
+ifdef debug
+ CPPLAGS += -fsanitize=address
+ CXXFLAGS += -Wall
+ CC := clang
+ CXX := clang++
+endif
+ifdef DEBUG
+ CPPFLAGS += -fsanitize=address
+ CXXFLAGS += -Wall
+ CC := clang
+ CXX := clang++
+endif
+
+all: autotokens.so
+
+afl-fuzz-queue.o: ../../src/afl-fuzz-queue.c
+ $(CC) -D_STANDALONE_MODULE=1 -I../../include -g -O3 $(CPPFLAGS) -fPIC -c -o ./afl-fuzz-queue.o ../../src/afl-fuzz-queue.c
+
+afl-common.o: ../../src/afl-common.c
+ $(CC) -I../../include -g -O3 $(CPPFLAGS) -DBIN_PATH=\"dummy\" -Wno-pointer-sign -fPIC -c -o ./afl-common.o ../../src/afl-common.c
+
+autotokens.so: afl-fuzz-queue.o afl-common.o autotokens.cpp
+ $(CXX) -Wno-deprecated -g -O3 $(CXXFLAGS) $(CPPFLAGS) -shared -fPIC -o autotokens.so -I../../include autotokens.cpp ./afl-fuzz-queue.o ../../src/afl-performance.o ./afl-common.o
+
+clean:
+ rm -f autotokens.so *.o *~ core
diff --git a/custom_mutators/autotokens/README b/custom_mutators/autotokens/README
new file mode 100644
index 00000000..cca168fd
--- /dev/null
+++ b/custom_mutators/autotokens/README
@@ -0,0 +1,34 @@
+# Autotokens
+
+This implements an improved autotoken grammar fuzzing idea presented in
+[Token-Level Fuzzing][https://www.usenix.org/system/files/sec21-salls.pdf].
+It is a grammar fuzzer without actually knowing the grammar, but only works
+with text based inputs.
+
+It is recommended to run with together in an instance with `CMPLOG`.
+
+If you have a dictionary (`-x`) this improves this custom grammar mutator.
+
+If **not** running with `CMPLOG`, it is possible to set
+`AFL_CUSTOM_MUTATOR_ONLY` to concentrate on grammar bug classes.
+
+Do **not** set `AFL_DISABLE_TRIM` with this custom mutator!
+
+## Configuration via environment variables
+
+`AUTOTOKENS_ONLY_FAV` - only use this mutator on favorite queue items
+`AUTOTOKENS_COMMENT` - what character or string starts a comment which will be
+ removed. Default: `/* ... */`
+`AUTOTOKENS_FUZZ_COUNT_SHIFT` - reduce the number of fuzzing performed, shifting
+ the value by this number, e.g. 1.
+`AUTOTOKENS_AUTO_DISABLE` - disable this module if the seeds are not ascii
+ (or no input and no (ascii) dictionary)
+`AUTOTOKENS_LEARN_DICT` - learn from dictionaries?
+ 0 = none
+ 1 = only -x or autodict
+ 2 = -x, autodict and `CMPLOG`
+`AUTOTOKENS_CHANGE_MIN` - minimum number of mutations (1-256, default 8)
+`AUTOTOKENS_CHANGE_MAX` - maximum number of mutations (1-4096, default 64)
+`AUTOTOKENS_CREATE_FROM_THIN_AIR` - if only one small start file is present and
+ a dictionary loaded then create one initial
+ structure based on the dictionary.
diff --git a/custom_mutators/autotokens/autotokens.cpp b/custom_mutators/autotokens/autotokens.cpp
new file mode 100644
index 00000000..8135aba1
--- /dev/null
+++ b/custom_mutators/autotokens/autotokens.cpp
@@ -0,0 +1,1101 @@
+/*
+ token level fuzzing custom mutator for afl++
+ (c) by Marc Heuse <mh@mh-sec.de>
+ License: Apache 2.0
+*/
+
+extern "C" {
+
+#include "afl-fuzz.h"
+
+}
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <iostream>
+#include <fstream>
+#include <unordered_map>
+#include <vector>
+#include <regex>
+
+#define AUTOTOKENS_DEBUG 0
+#define AUTOTOKENS_ONLY_FAV 0
+#define AUTOTOKENS_CHANGE_MIN 8
+#define AUTOTOKENS_CHANGE_MAX 64
+#define AUTOTOKENS_SIZE_MIN 8
+#define AUTOTOKENS_SIZE_MAX 65535
+#define AUTOTOKENS_SPLICE_MIN 4
+#define AUTOTOKENS_SPLICE_MAX 64
+#define AUTOTOKENS_CREATE_FROM_THIN_AIR 0
+#define AUTOTOKENS_FUZZ_COUNT_SHIFT 0
+#define AUTOTOKENS_AUTO_DISABLE 0
+// 0 = no learning, 1 only from -x dict/autodict, 2 also from cmplog
+#define AUTOTOKENS_LEARN_DICT 1
+#ifndef AUTOTOKENS_SPLICE_DISABLE
+ #define AUTOTOKENS_SPLICE_DISABLE 0
+#endif
+#ifndef AFL_TXT_MAX_LEN
+ #define AFL_TXT_MAX_LEN 65535
+#endif
+
+#if AUTOTOKENS_SPLICE_MIN >= AUTOTOKENS_SIZE_MIN
+ #error SPLICE_MIN must be lower than SIZE_MIN
+#endif
+
+using namespace std;
+
+typedef struct my_mutator {
+
+ afl_state *afl;
+
+} my_mutator_t;
+
+#undef DEBUGF
+#define DEBUGF \
+ if (unlikely(debug)) fprintf
+#define IFDEBUG if (unlikely(debug))
+
+static afl_state *afl_ptr;
+static int module_disabled = 0;
+static int auto_disable = AUTOTOKENS_AUTO_DISABLE;
+static int debug = AUTOTOKENS_DEBUG;
+static int only_fav = AUTOTOKENS_ONLY_FAV;
+static int learn_dictionary_tokens = AUTOTOKENS_LEARN_DICT;
+static int fuzz_count_shift = AUTOTOKENS_FUZZ_COUNT_SHIFT;
+static int create_from_thin_air = AUTOTOKENS_CREATE_FROM_THIN_AIR;
+static int change_min = AUTOTOKENS_CHANGE_MIN;
+static int change_max = AUTOTOKENS_CHANGE_MAX;
+static u32 current_id;
+static u32 valid_structures;
+static u32 whitespace_ids;
+static u32 extras_cnt, a_extras_cnt;
+static u64 all_spaces, all_tabs, all_lf, all_ws;
+static u64 all_structure_items;
+static u64 fuzz_count;
+static unordered_map<string, vector<u32> *> file_mapping;
+static unordered_map<u32, vector<u32> *> id_mapping;
+static unordered_map<string, u32> token_to_id;
+static unordered_map<u32, string> id_to_token;
+static string output;
+static regex *regex_comment_custom;
+// multiline requires g++-11 libs :(
+static regex regex_comment_star(
+ "/\\*([:print:]|\n)*?\\*/",
+ regex_constants::optimize /* | regex_constants::multiline */);
+static regex regex_word("[A-Za-z0-9_$.-]+", regex::optimize);
+static regex regex_whitespace(R"([ \t]+)", regex::optimize);
+static vector<u32> *s; // the structure of the currently selected input
+
+// FUNCTIONS
+
+/* This function is called once after everything is set up but before
+ any fuzzing attempt has been performed.
+ This is called in afl_custom_queue_get() */
+static void first_run(void *data) {
+
+ (void)(data);
+
+ /* For auto-loading this module we check here if we can analyze from the
+ input if the inputs look like text inputs and disable the module if
+ not. */
+
+ if (afl_ptr->custom_only || !auto_disable) { return; }
+
+ if (unlikely(afl_ptr->active_items == 1 &&
+ afl_ptr->queue_cur->len < AFL_TXT_MIN_LEN)) {
+
+ if (afl_ptr->extras_cnt > 8) {
+
+ u32 valid = 0;
+
+ while (extras_cnt < afl_ptr->extras_cnt) {
+
+ u32 ok = 1, l = afl_ptr->extras[extras_cnt].len;
+ u8 *buf, *ptr = afl_ptr->extras[extras_cnt].data;
+
+ for (u32 i = 0; i < l; ++i) {
+
+ if (!isascii((int)ptr[i]) && !isprint((int)ptr[i])) {
+
+ ok = 0;
+ break;
+
+ }
+
+ }
+
+ if (ok) {
+
+ buf = (u8 *)malloc(afl_ptr->extras[extras_cnt].len + 1);
+ memcpy(buf, afl_ptr->extras[extras_cnt].data,
+ afl_ptr->extras[extras_cnt].len);
+ buf[afl_ptr->extras[extras_cnt].len] = 0;
+ token_to_id[(char *)buf] = current_id;
+ id_to_token[current_id] = (char *)buf;
+ ++current_id;
+ ++valid;
+
+ }
+
+ ++extras_cnt;
+
+ }
+
+ if ((valid * 100) / afl_ptr->extras_cnt <= 70) { module_disabled = 1; }
+
+ DEBUGF(stderr, "DICT: total %u, valid %u, %u <= 70 == disable\n",
+ afl_ptr->extras_cnt, valid,
+ (u32)((valid * 100) / afl_ptr->extras_cnt));
+
+ } else {
+
+ module_disabled = 1;
+
+ }
+
+ return;
+
+ }
+
+ u32 is_ascii = 0, valid = 0;
+
+ for (u32 i = 0; i < afl_ptr->queued_items; ++i) {
+
+ struct queue_entry *q;
+
+ q = afl_ptr->queue_buf[i];
+
+ if (!q->disabled && q->len >= AUTOTOKENS_SIZE_MIN &&
+ q->len <= AFL_TXT_MAX_LEN) {
+
+ ++valid;
+ u8 *input = queue_testcase_get(afl_ptr, q);
+
+ u32 valid_chars = 0;
+ for (u32 i = 0; i < q->len; ++i) {
+
+ if (isascii((int)input[i]) || isprint((int)input[i])) { ++valid_chars; }
+
+ }
+
+ // we want at least 99% of text characters ...
+ if (((q->len * AFL_TXT_MIN_PERCENT) / 100) <= valid_chars) {
+
+ ++is_ascii;
+ q->is_ascii = 1;
+
+ }
+
+ }
+
+ }
+
+ if ((is_ascii * 100) / valid <= 70) { module_disabled = 1; }
+
+ DEBUGF(stderr, "seeds: total %u, valid %u, ascii %u, %u <= 70 == disabled\n",
+ afl_ptr->active_items, valid, is_ascii,
+ (u32)((is_ascii * 100) / valid));
+
+}
+
+static u32 good_whitespace_or_singleval() {
+
+ u32 i = rand_below(afl_ptr, current_id);
+ if (id_to_token[i].size() == 1) { return i; }
+ i = rand_below(afl_ptr, all_ws);
+ if (i < all_spaces) {
+
+ return 0;
+
+ } else if (i < all_tabs) {
+
+ return 1;
+
+ } else
+
+ return 2; // linefeed
+
+}
+
+extern "C" u32 afl_custom_fuzz_count(void *data, const u8 *buf,
+ size_t buf_size) {
+
+ (void)(data);
+
+ if (s == NULL) return 0;
+
+ u32 shift = unlikely(afl_ptr->custom_only) ? 7 : 8;
+ u32 stage_max = (u32)((HAVOC_CYCLES * afl_ptr->queue_cur->perf_score) /
+ afl_ptr->havoc_div) >>
+ shift;
+ if (fuzz_count_shift) { stage_max >>= (u32)fuzz_count_shift; };
+ DEBUGF(stderr, "fuzz count: %u\n", stage_max);
+
+ return stage_max;
+
+}
+
+extern "C" size_t afl_custom_fuzz(my_mutator_t *data, u8 *buf, size_t buf_size,
+ u8 **out_buf, u8 *add_buf,
+ size_t add_buf_size, size_t max_size) {
+
+ (void)(data);
+
+ if (unlikely(s == NULL)) {
+
+ *out_buf = NULL;
+ return 0;
+
+ }
+
+ vector<u32> m = *s; // copy of the structure we will modify
+ u32 i, m_size = (u32)m.size();
+
+ u32 rounds =
+ MIN(change_max,
+ MAX(change_min,
+ MIN(m_size >> 3, HAVOC_CYCLES * afl_ptr->queue_cur->perf_score *
+ afl_ptr->havoc_div / 256)));
+ // DEBUGF(stderr, "structure size: %lu, rounds: %u \n", m.size(), rounds);
+
+#if AUTOTOKENS_SPLICE_DISABLE == 1
+ #define AUTOTOKENS_MUT_MAX 18
+#else
+ #define AUTOTOKENS_MUT_MAX 27
+#endif
+
+ u32 max_rand = AUTOTOKENS_MUT_MAX, new_item, pos;
+
+ for (i = 0; i < rounds; ++i) {
+
+ switch (rand_below(afl_ptr, max_rand)) {
+
+ /* CHANGE/MUTATE single item */
+ case 0 ... 9: {
+
+ pos = rand_below(afl_ptr, m_size);
+ u32 cur_item = m[pos];
+ do {
+
+ new_item = rand_below(afl_ptr, current_id);
+
+ } while (unlikely(
+
+ new_item == cur_item ||
+ ((whitespace_ids < new_item && whitespace_ids >= cur_item) ||
+ (whitespace_ids >= new_item && whitespace_ids < cur_item))));
+
+ // DEBUGF(stderr, "MUT: %u -> %u\n", cur_item, new_item);
+ m[pos] = new_item;
+ break;
+
+ }
+
+ /* INSERT (m_size +1 so we insert also after last place) */
+ case 10 ... 13: {
+
+ do {
+
+ new_item = rand_below(afl_ptr, current_id);
+
+ } while (unlikely(new_item >= whitespace_ids));
+
+ u32 pos = rand_below(afl_ptr, m_size + 1);
+ m.insert(m.begin() + pos, new_item);
+ ++m_size;
+ // DEBUGF(stderr, "INS: %u at %u\n", new_item, pos);
+
+ break;
+
+ }
+
+#if AUTOTOKENS_SPLICE_DISABLE != 1
+ /* SPLICING */
+ case 14 ... 22: {
+
+ u32 strategy = rand_below(afl_ptr, 4), dst_off, n;
+ auto src = id_mapping[rand_below(afl_ptr, valid_structures)];
+ u32 src_size = src->size();
+ u32 src_off = rand_below(afl_ptr, src_size - AUTOTOKENS_SPLICE_MIN);
+ u32 rand_r = 1 + MAX(AUTOTOKENS_SPLICE_MIN,
+ MIN(AUTOTOKENS_SPLICE_MAX, src_size - src_off));
+
+ switch (strategy) {
+
+ // insert
+ case 0: {
+
+ dst_off = rand_below(afl_ptr, m_size);
+ n = AUTOTOKENS_SPLICE_MIN +
+ rand_below(afl_ptr, MIN(AUTOTOKENS_SPLICE_MAX,
+ rand_r - AUTOTOKENS_SPLICE_MIN));
+ m.insert(m.begin() + dst_off, src->begin() + src_off,
+ src->begin() + src_off + n);
+ m_size += n;
+ // DEBUGF(stderr, "SPLICE-INS: %u at %u\n", n, dst_off);
+
+ break;
+
+ }
+
+ // overwrite
+ default: {
+
+ dst_off = rand_below(afl_ptr, m_size - AUTOTOKENS_SPLICE_MIN);
+ n = AUTOTOKENS_SPLICE_MIN +
+ rand_below(
+ afl_ptr,
+ MIN(AUTOTOKENS_SPLICE_MAX - AUTOTOKENS_SPLICE_MIN,
+ MIN(m_size - dst_off - AUTOTOKENS_SPLICE_MIN,
+ src_size - src_off - AUTOTOKENS_SPLICE_MIN)));
+
+ copy(src->begin() + src_off, src->begin() + src_off + n,
+ m.begin() + dst_off);
+
+ // DEBUGF(stderr, "SPLICE-MUT: %u at %u\n", n, dst_off);
+ break;
+
+ }
+
+ }
+
+ break;
+
+ }
+
+#endif
+
+ /* ERASE - only if large enough */
+ default: {
+
+ if (m_size > 8) {
+
+ do {
+
+ pos = rand_below(afl_ptr, m_size);
+
+ } while (unlikely(m[pos] < whitespace_ids));
+
+ m.erase(m.begin() + pos);
+ --m_size;
+
+ } else {
+
+ // if the data is already too small do not try to make it smaller
+ // again this run.
+
+ max_rand -= 4;
+
+ }
+
+ break;
+
+ }
+
+ }
+
+ }
+
+ /* Now we create the output */
+
+ output = "";
+ u32 prev_size = 1, was_whitespace = 1;
+
+ for (i = 0; i < m_size; ++i) {
+
+ if (likely(i + 1 < m_size)) {
+
+ u32 this_size = id_to_token[m[i]].size();
+ u32 is_whitespace = m[i] < whitespace_ids;
+
+ /* The output we are generating might need repairing.
+ General rule: two items that have a size larger than 2 are strings
+ or identifizers and need a whitespace or an item of length 1 in
+ between. */
+ if (unlikely(!(prev_size == 1 || was_whitespace || this_size == 1 ||
+ is_whitespace))) {
+
+ output += id_to_token[good_whitespace_or_singleval()];
+
+ }
+
+ prev_size = this_size;
+ was_whitespace = is_whitespace;
+
+ }
+
+ output += id_to_token[m[i]];
+
+ }
+
+ u32 mutated_size = (u32)output.size();
+ u8 *mutated_out = (u8 *)output.data();
+
+ if (unlikely(mutated_size > max_size)) { mutated_size = max_size; }
+
+ /*
+ IFDEBUG {
+
+ DEBUGF(stderr, "MUTATED to %u bytes:\n", mutated_size);
+ fwrite(output.data(), 1, mutated_size, stderr);
+ DEBUGF(stderr, "\n---\n");
+
+ }
+
+ */
+
+ *out_buf = mutated_out;
+ ++fuzz_count;
+ return mutated_size;
+
+}
+
+/* I get f*cking stack overflow using C++ regex with a regex of
+ "\"[[:print:]]*?\"" if this matches a long string even with regex::optimize
+ enabled :-( */
+static u8 my_search_string(string::const_iterator cur,
+ string::const_iterator ende,
+ string::const_iterator *match_begin,
+ string::const_iterator *match_end) {
+
+ string::const_iterator start = cur, found_begin;
+ u8 quote_type = 0;
+
+ while (cur < ende) {
+
+ switch (*cur) {
+
+ case '"': {
+
+ if (cur == start || *(cur - 1) != '\\') {
+
+ if (!quote_type) {
+
+ found_begin = cur;
+ quote_type = 1;
+
+ } else if (quote_type == 1) {
+
+ *match_begin = found_begin;
+ *match_end = cur + 1;
+ return 1;
+
+ }
+
+ }
+
+ break;
+
+ }
+
+ case '\'': {
+
+ if (cur == start || *(cur - 1) != '\\') {
+
+ if (!quote_type) {
+
+ found_begin = cur;
+ quote_type = 2;
+
+ } else if (quote_type == 2) {
+
+ *match_begin = found_begin;
+ *match_end = cur + 1;
+ return 1;
+
+ }
+
+ }
+
+ break;
+
+ }
+
+ case '\n':
+ case '\r':
+ case 0: {
+
+ quote_type = 0;
+ break;
+
+ }
+
+ default:
+ if (unlikely(quote_type && !isprint(*cur))) { quote_type = 0; }
+ break;
+
+ }
+
+ ++cur;
+
+ }
+
+ return 0;
+
+}
+
+/* We are not using afl_custom_queue_new_entry() because not every corpus entry
+ will be necessarily fuzzed with this custom mutator.
+ So we use afl_custom_queue_get() instead. */
+
+extern "C" unsigned char afl_custom_queue_get(void *data,
+ const unsigned char *filename) {
+
+ static int learn_state = 0;
+ static int is_first_run = 1;
+ (void)(data);
+
+ if (unlikely(is_first_run)) {
+
+ is_first_run = 0;
+ first_run(data);
+
+ if (module_disabled) {
+
+ WARNF("Autotokens custom module is disabled.");
+
+ } else if (auto_disable) {
+
+ OKF("Autotokens custom module is enabled.");
+
+ }
+
+ }
+
+ if (likely(module_disabled) ||
+ (unlikely(!afl_ptr->custom_only) && !create_from_thin_air &&
+ ((afl_ptr->shm.cmplog_mode && !afl_ptr->queue_cur->is_ascii) ||
+ (only_fav && !afl_ptr->queue_cur->favored)))) {
+
+ s = NULL;
+ DEBUGF(stderr,
+ "cmplog not ascii or only_fav and not favorite or disabled\n");
+ return 1;
+
+ }
+
+ // check if there are new dictionary entries and add them to the tokens
+ if (unlikely(learn_state < learn_dictionary_tokens) &&
+ likely(valid_structures || create_from_thin_air)) {
+
+ if (unlikely(!learn_state)) { learn_state = 1; }
+
+ while (extras_cnt < afl_ptr->extras_cnt) {
+
+ u32 ok = 1, l = afl_ptr->extras[extras_cnt].len;
+ u8 *buf, *ptr = afl_ptr->extras[extras_cnt].data;
+
+ for (u32 i = 0; i < l; ++i) {
+
+ if (!isascii((int)ptr[i]) && !isprint((int)ptr[i])) {
+
+ ok = 0;
+ break;
+
+ }
+
+ }
+
+ if (ok) {
+
+ buf = (u8 *)malloc(afl_ptr->extras[extras_cnt].len + 1);
+ memcpy(buf, afl_ptr->extras[extras_cnt].data,
+ afl_ptr->extras[extras_cnt].len);
+ buf[afl_ptr->extras[extras_cnt].len] = 0;
+ token_to_id[(char *)buf] = current_id;
+ id_to_token[current_id] = (char *)buf;
+ ++current_id;
+
+ }
+
+ ++extras_cnt;
+
+ }
+
+ while (a_extras_cnt < afl_ptr->a_extras_cnt) {
+
+ u32 ok = 1, l = afl_ptr->a_extras[a_extras_cnt].len;
+ u8 *ptr = afl_ptr->a_extras[a_extras_cnt].data;
+
+ for (u32 i = 0; i < l; ++i) {
+
+ if (!isascii((int)ptr[i]) && !isprint((int)ptr[i])) {
+
+ ok = 0;
+ break;
+
+ }
+
+ }
+
+ if (ok) {
+
+ token_to_id[(char *)ptr] = current_id;
+ id_to_token[current_id] = (char *)ptr;
+ ++current_id;
+
+ }
+
+ ++a_extras_cnt;
+
+ }
+
+ }
+
+ vector<u32> *structure = NULL;
+ string fn = (char *)filename;
+ auto entry = file_mapping.find(fn);
+
+ // if there is only one active queue item at start and it is very small
+ // the we create once a structure randomly.
+ if (unlikely(create_from_thin_air)) {
+
+ if (current_id > whitespace_ids + 6 && afl_ptr->active_items == 1 &&
+ afl_ptr->queue_cur->len < AFL_TXT_MIN_LEN) {
+
+ DEBUGF(stderr, "Creating an entry from thin air...\n");
+ structure = new vector<u32>();
+ u32 item, prev, cnt = current_id >> 1;
+ structure->reserve(cnt + 4);
+ for (u32 i = 0; i < cnt; i++) {
+
+ item = rand_below(afl_ptr, current_id);
+ if (i && id_to_token[item].length() > 1 &&
+ id_to_token[prev].length() > 1) {
+
+ structure->push_back(good_whitespace_or_singleval());
+
+ }
+
+ structure->push_back(item);
+ prev = item;
+
+ }
+
+ s = structure;
+ file_mapping[fn] = structure;
+ id_mapping[valid_structures] = structure;
+ ++valid_structures;
+ all_structure_items += structure->size();
+
+ return 1;
+
+ }
+
+ create_from_thin_air = 0;
+
+ }
+
+ if (entry == file_mapping.end()) {
+
+ // this input file was not analyzed for tokens yet, so let's do it!
+ size_t len = afl_ptr->queue_cur->len;
+
+ if (len < AFL_TXT_MIN_LEN) {
+
+ file_mapping[fn] = structure; // NULL ptr so we don't read the file again
+ s = NULL;
+ DEBUGF(stderr, "Too short (%lu) %s\n", len, filename);
+ return 1;
+
+ } else if (len > AFL_TXT_MAX_LEN) {
+
+ file_mapping[fn] = structure; // NULL ptr so we don't read the file again
+ s = NULL;
+ DEBUGF(stderr, "Too long (%lu) %s\n", len, filename);
+ return 1;
+
+ }
+
+ u8 *input_buf = queue_testcase_get(afl_ptr, afl_ptr->queue_cur);
+ string input((char *)input_buf, afl_ptr->queue_cur->len);
+
+ if (!afl_ptr->shm.cmplog_mode) {
+
+ // not running with CMPLOG? bad choice, but whatever ...
+ // we only want text inputs, so we have to check it ourselves.
+
+ u32 valid_chars = 0;
+ for (u32 i = 0; i < len; ++i) {
+
+ if (isascii((int)input[i]) || isprint((int)input[i])) { ++valid_chars; }
+
+ }
+
+ // we want at least 95% of text characters ...
+ if (((len * AFL_TXT_MIN_PERCENT) / 100) > valid_chars) {
+
+ file_mapping[fn] = NULL;
+ s = NULL;
+ DEBUGF(stderr, "Not text (%lu) %s\n", len, filename);
+ return 1;
+
+ }
+
+ }
+
+ // DEBUGF(stderr, "Read %lu bytes for %s\nBefore comment trim:\n%s\n",
+ // input.size(), filename, input.c_str());
+
+ if (regex_comment_custom) {
+
+ input = regex_replace(input, *regex_comment_custom, "$2");
+
+ } else {
+
+ input = regex_replace(input, regex_comment_star, "");
+
+ }
+
+ DEBUGF(stderr, "After replace %lu bytes for %s\n%s\n", input.size(),
+ filename, input.c_str());
+
+ u32 spaces = count(input.begin(), input.end(), ' ');
+ u32 tabs = count(input.begin(), input.end(), '\t');
+ u32 linefeeds = count(input.begin(), input.end(), '\n');
+ bool ends_with_linefeed = input[input.length() - 1] == '\n';
+
+ DEBUGF(stderr, "spaces=%u tabs=%u linefeeds=%u ends=%u\n", spaces, tabs,
+ linefeeds, ends_with_linefeed);
+
+ all_spaces += spaces;
+ all_tabs += tabs;
+ all_lf += linefeeds;
+ all_ws = all_spaces + all_tabs + all_lf;
+
+ // now extract all tokens
+ vector<string> tokens;
+ string::const_iterator cur = input.begin(), ende = input.end(), found, prev,
+ match_begin, match_end;
+
+ DEBUGF(stderr, "START!\n");
+
+ while (my_search_string(cur, ende, &match_begin, &match_end)) {
+
+ prev = cur;
+ found = match_begin;
+ cur = match_end;
+
+ IFDEBUG {
+
+ string foo(match_begin, match_end);
+ DEBUGF(stderr,
+ "string %s found at start %lu offset %lu continue at %lu\n",
+ foo.c_str(), prev - input.begin(), found - prev,
+ cur - input.begin());
+
+ }
+
+ if (prev < found) { // there are items between search start and find
+ while (prev < found) {
+
+ if (isspace(*prev)) {
+
+ auto start = prev;
+ while (isspace(*prev)) {
+
+ ++prev;
+
+ }
+
+ tokens.push_back(std::string(start, prev));
+ DEBUGF(stderr, "WHITESPACE %ld \"%s\"\n", prev - start,
+ tokens[tokens.size() - 1].c_str());
+
+ } else if (isalnum(*prev) || *prev == '$' || *prev == '_') {
+
+ auto start = prev;
+ while (isalnum(*prev) || *prev == '$' || *prev == '_' ||
+ *prev == '.' || *prev == '/') {
+
+ ++prev;
+
+ }
+
+ tokens.push_back(string(start, prev));
+ DEBUGF(stderr, "IDENTIFIER %ld \"%s\"\n", prev - start,
+ tokens[tokens.size() - 1].c_str());
+
+ } else {
+
+ tokens.push_back(string(prev, prev + 1));
+ DEBUGF(stderr, "OTHER \"%c\"\n", *prev);
+ ++prev;
+
+ }
+
+ }
+
+ }
+
+ tokens.push_back(string(match_begin, match_end));
+ DEBUGF(stderr, "TOK: %s\n", tokens[tokens.size() - 1].c_str());
+
+ }
+
+ DEBUGF(stderr, "AFTER all strings\n");
+
+ if (cur < ende) {
+
+ while (cur < ende) {
+
+ if (isspace(*cur)) {
+
+ auto start = cur;
+ while (isspace(*cur)) {
+
+ ++cur;
+
+ }
+
+ tokens.push_back(std::string(start, cur));
+ DEBUGF(stderr, "WHITESPACE %ld \"%s\"\n", cur - start,
+ tokens[tokens.size() - 1].c_str());
+
+ } else if (isalnum(*cur) || *cur == '$' || *cur == '_') {
+
+ auto start = cur;
+ while (isalnum(*cur) || *cur == '$' || *cur == '_' || *cur == '.' ||
+ *cur == '/') {
+
+ ++cur;
+
+ }
+
+ tokens.push_back(std::string(start, cur));
+ DEBUGF(stderr, "IDENTIFIER %ld \"%s\"\n", cur - start,
+ tokens[tokens.size() - 1].c_str());
+
+ } else {
+
+ tokens.push_back(std::string(cur, cur + 1));
+ DEBUGF(stderr, "OTHER \"%c\"\n", *cur);
+ ++cur;
+
+ }
+
+ }
+
+ }
+
+ IFDEBUG {
+
+ DEBUGF(stderr, "DUMPING TOKENS:\n");
+ for (u32 i = 0; i < tokens.size(); ++i) {
+
+ DEBUGF(stderr, "%s", tokens[i].c_str());
+
+ }
+
+ DEBUGF(stderr, "---------------------------\n");
+
+ }
+
+ if (tokens.size() < AUTOTOKENS_SIZE_MIN) {
+
+ file_mapping[fn] = NULL;
+ s = NULL;
+ DEBUGF(stderr, "too few tokens\n");
+ return 1;
+
+ }
+
+ /* Now we transform the tokens into an ID list and saved that */
+
+ structure = new vector<u32>();
+ u32 id;
+
+ for (u32 i = 0; i < tokens.size(); ++i) {
+
+ if ((id = token_to_id[tokens[i]]) == 0) {
+
+ // First time we see this token, add it to the list
+ token_to_id[tokens[i]] = current_id;
+ id_to_token[current_id] = tokens[i];
+ structure->push_back(current_id);
+ ++current_id;
+
+ } else {
+
+ structure->push_back(id);
+
+ }
+
+ }
+
+ // save the token structure to the file mapping
+ file_mapping[fn] = structure;
+ id_mapping[valid_structures] = structure;
+ ++valid_structures;
+ s = structure;
+ all_structure_items += structure->size();
+
+ // we are done!
+ DEBUGF(stderr, "DONE! We have %lu tokens in the structure\n",
+ structure->size());
+
+ } else {
+
+ if (entry->second == NULL) {
+
+ DEBUGF(stderr, "Skipping %s\n", filename);
+ s = NULL;
+ return 1;
+
+ }
+
+ s = entry->second;
+ DEBUGF(stderr, "OK %s\n", filename);
+
+ }
+
+ return 1; // we always fuzz unless non-ascii or too small
+
+}
+
+extern "C" my_mutator_t *afl_custom_init(afl_state *afl, unsigned int seed) {
+
+ (void)(seed);
+ my_mutator_t *data = (my_mutator_t *)calloc(1, sizeof(my_mutator_t));
+ if (!data) {
+
+ perror("afl_custom_init alloc");
+ return NULL;
+
+ }
+
+ if (getenv("AUTOTOKENS_DEBUG")) { debug = 1; }
+ if (getenv("AUTOTOKENS_AUTO_DISABLE")) { auto_disable = 1; }
+ if (getenv("AUTOTOKENS_ONLY_FAV")) { only_fav = 1; }
+ if (getenv("AUTOTOKENS_CREATE_FROM_THIN_AIR")) { create_from_thin_air = 1; }
+
+ if (getenv("AUTOTOKENS_LEARN_DICT")) {
+
+ learn_dictionary_tokens = atoi(getenv("AUTOTOKENS_LEARN_DICT"));
+ if (learn_dictionary_tokens < 0 || learn_dictionary_tokens > 2) {
+
+ learn_dictionary_tokens = AUTOTOKENS_LEARN_DICT;
+
+ }
+
+ }
+
+ if (getenv("AUTOTOKENS_FUZZ_COUNT_SHIFT")) {
+
+ fuzz_count_shift = atoi(getenv("AUTOTOKENS_FUZZ_COUNT_SHIFT"));
+ if (fuzz_count_shift < 0 || fuzz_count_shift > 16) { fuzz_count_shift = 0; }
+
+ }
+
+ if (getenv("AUTOTOKENS_CHANGE_MIN")) {
+
+ change_min = atoi(getenv("AUTOTOKENS_CHANGE_MIN"));
+ if (change_min < 1 || change_min > 256) {
+
+ change_min = AUTOTOKENS_CHANGE_MIN;
+
+ }
+
+ }
+
+ if (getenv("AUTOTOKENS_CHANGE_MAX")) {
+
+ change_max = atoi(getenv("AUTOTOKENS_CHANGE_MAX"));
+ if (change_max < 1 || change_max > 4096) {
+
+ change_max = AUTOTOKENS_CHANGE_MAX;
+
+ }
+
+ }
+
+ if (change_max < change_min) { change_max = change_min + 1; }
+
+ if (getenv("AUTOTOKENS_COMMENT")) {
+
+ char buf[256];
+ snprintf(buf, sizeof(buf), "(%s.*)([\r\n]?)", getenv("AUTOTOKENS_COMMENT"));
+ regex_comment_custom = new regex(buf, regex::optimize);
+
+ }
+
+ data->afl = afl_ptr = afl;
+
+ // set common whitespace tokens
+ // we deliberately do not put uncommon ones here to these will count as
+ // identifier tokens.
+ token_to_id[" "] = current_id;
+ id_to_token[current_id] = " ";
+ ++current_id;
+ token_to_id["\t"] = current_id;
+ id_to_token[current_id] = "\t";
+ ++current_id;
+ token_to_id["\n"] = current_id;
+ id_to_token[current_id] = "\n";
+ ++current_id;
+ token_to_id["\r\n"] = current_id;
+ id_to_token[current_id] = "\r\n";
+ ++current_id;
+ token_to_id[" \n"] = current_id;
+ id_to_token[current_id] = " \n";
+ ++current_id;
+ token_to_id[" "] = current_id;
+ id_to_token[current_id] = " ";
+ ++current_id;
+ token_to_id["\t\t"] = current_id;
+ id_to_token[current_id] = "\t\t";
+ ++current_id;
+ token_to_id["\n\n"] = current_id;
+ id_to_token[current_id] = "\n\n";
+ ++current_id;
+ token_to_id["\r\n\r\n"] = current_id;
+ id_to_token[current_id] = "\r\n\r\n";
+ ++current_id;
+ token_to_id[" "] = current_id;
+ id_to_token[current_id] = " ";
+ ++current_id;
+ token_to_id["\t\t\t\t"] = current_id;
+ id_to_token[current_id] = "\t\t\t\t";
+ ++current_id;
+ token_to_id["\n\n\n\n"] = current_id;
+ id_to_token[current_id] = "\n\n\n\n";
+ ++current_id;
+ whitespace_ids = current_id;
+ token_to_id["\""] = current_id;
+ id_to_token[current_id] = "\"";
+ ++current_id;
+ token_to_id["'"] = current_id;
+ id_to_token[current_id] = "'";
+ ++current_id;
+
+ return data;
+
+}
+
+extern "C" void afl_custom_splice_optout(my_mutator_t *data) {
+
+ (void)(data);
+
+}
+
+extern "C" void afl_custom_deinit(my_mutator_t *data) {
+
+ /* we use this to print statistics at exit :-)
+ needs to be stderr as stdout is filtered */
+
+ if (module_disabled) { return; }
+
+ fprintf(stderr,
+ "\n\nAutotoken mutator statistics:\n"
+ " Number of all seen tokens: %u\n"
+ " Number of input structures: %u\n"
+ " Number of all items in structures: %llu\n"
+ " Number of total fuzzes: %llu\n\n",
+ current_id - 1, valid_structures, all_structure_items, fuzz_count);
+
+ free(data);
+
+}
+
diff --git a/custom_mutators/examples/custom_mutator_helpers.h b/custom_mutators/examples/custom_mutator_helpers.h
deleted file mode 100644
index 62e6efba..00000000
--- a/custom_mutators/examples/custom_mutator_helpers.h
+++ /dev/null
@@ -1,342 +0,0 @@
-#ifndef CUSTOM_MUTATOR_HELPERS
-#define CUSTOM_MUTATOR_HELPERS
-
-#include "config.h"
-#include "types.h"
-#include <stdlib.h>
-
-#define INITIAL_GROWTH_SIZE (64)
-
-#define RAND_BELOW(limit) (rand() % (limit))
-
-/* Use in a struct: creates a name_buf and a name_size variable. */
-#define BUF_VAR(type, name) \
- type * name##_buf; \
- size_t name##_size;
-/* this fills in `&structptr->something_buf, &structptr->something_size`. */
-#define BUF_PARAMS(struct, name) \
- (void **)&struct->name##_buf, &struct->name##_size
-
-typedef struct {
-
-} afl_t;
-
-static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) {
-
- static s8 interesting_8[] = {INTERESTING_8};
- static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16};
- static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32};
-
- switch (RAND_BELOW(12)) {
-
- case 0: {
-
- /* Flip a single bit somewhere. Spooky! */
-
- s32 bit_idx = ((RAND_BELOW(end - begin) + begin) << 3) + RAND_BELOW(8);
-
- out_buf[bit_idx >> 3] ^= 128 >> (bit_idx & 7);
-
- break;
-
- }
-
- case 1: {
-
- /* Set byte to interesting value. */
-
- u8 val = interesting_8[RAND_BELOW(sizeof(interesting_8))];
- out_buf[(RAND_BELOW(end - begin) + begin)] = val;
-
- break;
-
- }
-
- case 2: {
-
- /* Set word to interesting value, randomly choosing endian. */
-
- if (end - begin < 2) break;
-
- s32 byte_idx = (RAND_BELOW(end - begin) + begin);
-
- if (byte_idx >= end - 1) break;
-
- switch (RAND_BELOW(2)) {
-
- case 0:
- *(u16 *)(out_buf + byte_idx) =
- interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)];
- break;
- case 1:
- *(u16 *)(out_buf + byte_idx) =
- SWAP16(interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]);
- break;
-
- }
-
- break;
-
- }
-
- case 3: {
-
- /* Set dword to interesting value, randomly choosing endian. */
-
- if (end - begin < 4) break;
-
- s32 byte_idx = (RAND_BELOW(end - begin) + begin);
-
- if (byte_idx >= end - 3) break;
-
- switch (RAND_BELOW(2)) {
-
- case 0:
- *(u32 *)(out_buf + byte_idx) =
- interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)];
- break;
- case 1:
- *(u32 *)(out_buf + byte_idx) =
- SWAP32(interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
- break;
-
- }
-
- break;
-
- }
-
- case 4: {
-
- /* Set qword to interesting value, randomly choosing endian. */
-
- if (end - begin < 8) break;
-
- s32 byte_idx = (RAND_BELOW(end - begin) + begin);
-
- if (byte_idx >= end - 7) break;
-
- switch (RAND_BELOW(2)) {
-
- case 0:
- *(u64 *)(out_buf + byte_idx) =
- (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)];
- break;
- case 1:
- *(u64 *)(out_buf + byte_idx) = SWAP64(
- (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
- break;
-
- }
-
- break;
-
- }
-
- case 5: {
-
- /* Randomly subtract from byte. */
-
- out_buf[(RAND_BELOW(end - begin) + begin)] -= 1 + RAND_BELOW(ARITH_MAX);
-
- break;
-
- }
-
- case 6: {
-
- /* Randomly add to byte. */
-
- out_buf[(RAND_BELOW(end - begin) + begin)] += 1 + RAND_BELOW(ARITH_MAX);
-
- break;
-
- }
-
- case 7: {
-
- /* Randomly subtract from word, random endian. */
-
- if (end - begin < 2) break;
-
- s32 byte_idx = (RAND_BELOW(end - begin) + begin);
-
- if (byte_idx >= end - 1) break;
-
- if (RAND_BELOW(2)) {
-
- *(u16 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX);
-
- } else {
-
- u16 num = 1 + RAND_BELOW(ARITH_MAX);
-
- *(u16 *)(out_buf + byte_idx) =
- SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) - num);
-
- }
-
- break;
-
- }
-
- case 8: {
-
- /* Randomly add to word, random endian. */
-
- if (end - begin < 2) break;
-
- s32 byte_idx = (RAND_BELOW(end - begin) + begin);
-
- if (byte_idx >= end - 1) break;
-
- if (RAND_BELOW(2)) {
-
- *(u16 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX);
-
- } else {
-
- u16 num = 1 + RAND_BELOW(ARITH_MAX);
-
- *(u16 *)(out_buf + byte_idx) =
- SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) + num);
-
- }
-
- break;
-
- }
-
- case 9: {
-
- /* Randomly subtract from dword, random endian. */
-
- if (end - begin < 4) break;
-
- s32 byte_idx = (RAND_BELOW(end - begin) + begin);
-
- if (byte_idx >= end - 3) break;
-
- if (RAND_BELOW(2)) {
-
- *(u32 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX);
-
- } else {
-
- u32 num = 1 + RAND_BELOW(ARITH_MAX);
-
- *(u32 *)(out_buf + byte_idx) =
- SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) - num);
-
- }
-
- break;
-
- }
-
- case 10: {
-
- /* Randomly add to dword, random endian. */
-
- if (end - begin < 4) break;
-
- s32 byte_idx = (RAND_BELOW(end - begin) + begin);
-
- if (byte_idx >= end - 3) break;
-
- if (RAND_BELOW(2)) {
-
- *(u32 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX);
-
- } else {
-
- u32 num = 1 + RAND_BELOW(ARITH_MAX);
-
- *(u32 *)(out_buf + byte_idx) =
- SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) + num);
-
- }
-
- break;
-
- }
-
- case 11: {
-
- /* Just set a random byte to a random value. Because,
- why not. We use XOR with 1-255 to eliminate the
- possibility of a no-op. */
-
- out_buf[(RAND_BELOW(end - begin) + begin)] ^= 1 + RAND_BELOW(255);
-
- break;
-
- }
-
- }
-
-}
-
-/* This function calculates the next power of 2 greater or equal its argument.
- @return The rounded up power of 2 (if no overflow) or 0 on overflow.
-*/
-static inline size_t next_pow2(size_t in) {
-
- if (in == 0 || in > (size_t)-1)
- return 0; /* avoid undefined behaviour under-/overflow */
- size_t out = in - 1;
- out |= out >> 1;
- out |= out >> 2;
- out |= out >> 4;
- out |= out >> 8;
- out |= out >> 16;
- return out + 1;
-
-}
-
-/* This function makes sure *size is > size_needed after call.
- It will realloc *buf otherwise.
- *size will grow exponentially as per:
- https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/
- Will return NULL and free *buf if size_needed is <1 or realloc failed.
- @return For convenience, this function returns *buf.
- */
-static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) {
-
- /* No need to realloc */
- if (likely(size_needed && *size >= size_needed)) return *buf;
-
- /* No initial size was set */
- if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE;
-
- /* grow exponentially */
- size_t next_size = next_pow2(size_needed);
-
- /* handle overflow */
- if (!next_size) { next_size = size_needed; }
-
- /* alloc */
- *buf = realloc(*buf, next_size);
- *size = *buf ? next_size : 0;
-
- return *buf;
-
-}
-
-/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */
-static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2,
- size_t *size2) {
-
- void * scratch_buf = *buf1;
- size_t scratch_size = *size1;
- *buf1 = *buf2;
- *size1 = *size2;
- *buf2 = scratch_buf;
- *size2 = scratch_size;
-
-}
-
-#undef INITIAL_GROWTH_SIZE
-
-#endif
-
diff --git a/custom_mutators/examples/custom_send.c b/custom_mutators/examples/custom_send.c
new file mode 100644
index 00000000..9cc4b160
--- /dev/null
+++ b/custom_mutators/examples/custom_send.c
@@ -0,0 +1,63 @@
+//
+// This is an example on how to use afl_custom_send
+// It writes each mutated data set to /tmp/foo
+// You can modify this to send to IPC, shared memory, etc.
+//
+// cc -O3 -fPIC -shared -g -o custom_send.so -I../../include custom_send.c
+// cd ../..
+// afl-cc -o test-instr test-instr.c
+// AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/examples/custom_send.so \
+// afl-fuzz -i in -o out -- ./test-instr -f /tmp/foo
+//
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "afl-fuzz.h"
+
+typedef struct my_mutator {
+
+ afl_state_t *afl;
+
+} my_mutator_t;
+
+my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
+
+ my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
+ if (!data) {
+
+ perror("afl_custom_init alloc");
+ return NULL;
+
+ }
+
+ data->afl = afl;
+
+ return data;
+
+}
+
+void afl_custom_fuzz_send(my_mutator_t *data, uint8_t *buf, size_t buf_size) {
+
+ int fd = open("/tmp/foo", O_CREAT | O_NOFOLLOW | O_TRUNC | O_RDWR, 0644);
+
+ if (fd >= 0) {
+
+ (void)write(fd, buf, buf_size);
+ close(fd);
+
+ }
+
+ return;
+
+}
+
+void afl_custom_deinit(my_mutator_t *data) {
+
+ free(data);
+
+}
+
diff --git a/custom_mutators/examples/example.c b/custom_mutators/examples/example.c
index 3f299508..42c7469c 100644
--- a/custom_mutators/examples/example.c
+++ b/custom_mutators/examples/example.c
@@ -6,8 +6,8 @@
Dominik Maier <mail@dmnk.co>
*/
-// You need to use -I /path/to/AFLplusplus/include
-#include "custom_mutator_helpers.h"
+// You need to use -I/path/to/AFLplusplus/include -I.
+#include "afl-fuzz.h"
#include <stdint.h>
#include <stdlib.h>
@@ -26,19 +26,14 @@ static const char *commands[] = {
typedef struct my_mutator {
- afl_t *afl;
+ afl_state_t *afl;
// any additional data here!
size_t trim_size_current;
int trimmming_steps;
int cur_step;
- // Reused buffers:
- BUF_VAR(u8, fuzz);
- BUF_VAR(u8, data);
- BUF_VAR(u8, havoc);
- BUF_VAR(u8, trim);
- BUF_VAR(u8, post_process);
+ u8 *mutated_out, *post_process_buf, *trim_buf;
} my_mutator_t;
@@ -53,7 +48,7 @@ typedef struct my_mutator {
* There may be multiple instances of this mutator in one afl-fuzz run!
* Return NULL on error.
*/
-my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
+my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
srand(seed); // needed also by surgical_havoc_mutate()
@@ -65,6 +60,27 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
}
+ if ((data->mutated_out = (u8 *)malloc(MAX_FILE)) == NULL) {
+
+ perror("afl_custom_init malloc");
+ return NULL;
+
+ }
+
+ if ((data->post_process_buf = (u8 *)malloc(MAX_FILE)) == NULL) {
+
+ perror("afl_custom_init malloc");
+ return NULL;
+
+ }
+
+ if ((data->trim_buf = (u8 *)malloc(MAX_FILE)) == NULL) {
+
+ perror("afl_custom_init malloc");
+ return NULL;
+
+ }
+
data->afl = afl;
return data;
@@ -96,29 +112,14 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
// the fuzzer
size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_size;
- // maybe_grow is optimized to be quick for reused buffers.
- u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), mutated_size);
- if (!mutated_out) {
-
- *out_buf = NULL;
- perror("custom mutator allocation (maybe_grow)");
- return 0; /* afl-fuzz will very likely error out after this. */
-
- }
+ memcpy(data->mutated_out, buf, buf_size);
// Randomly select a command string to add as a header to the packet
- memcpy(mutated_out, commands[rand() % 3], 3);
+ memcpy(data->mutated_out, commands[rand() % 3], 3);
- // Mutate the payload of the packet
- int i;
- for (i = 0; i < 8; ++i) {
+ if (mutated_size > max_size) { mutated_size = max_size; }
- // Randomly perform one of the (no len modification) havoc mutations
- surgical_havoc_mutate(mutated_out, 3, mutated_size);
-
- }
-
- *out_buf = mutated_out;
+ *out_buf = data->mutated_out;
return mutated_size;
}
@@ -142,24 +143,16 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
size_t afl_custom_post_process(my_mutator_t *data, uint8_t *buf,
size_t buf_size, uint8_t **out_buf) {
- uint8_t *post_process_buf =
- maybe_grow(BUF_PARAMS(data, post_process), buf_size + 5);
- if (!post_process_buf) {
+ if (buf_size + 5 > MAX_FILE) { buf_size = MAX_FILE - 5; }
- perror("custom mutator realloc failed.");
- *out_buf = NULL;
- return 0;
-
- }
+ memcpy(data->post_process_buf + 5, buf, buf_size);
+ data->post_process_buf[0] = 'A';
+ data->post_process_buf[1] = 'F';
+ data->post_process_buf[2] = 'L';
+ data->post_process_buf[3] = '+';
+ data->post_process_buf[4] = '+';
- memcpy(post_process_buf + 5, buf, buf_size);
- post_process_buf[0] = 'A';
- post_process_buf[1] = 'F';
- post_process_buf[2] = 'L';
- post_process_buf[3] = '+';
- post_process_buf[4] = '+';
-
- *out_buf = post_process_buf;
+ *out_buf = data->post_process_buf;
return buf_size + 5;
@@ -195,13 +188,6 @@ int32_t afl_custom_init_trim(my_mutator_t *data, uint8_t *buf,
data->cur_step = 0;
- if (!maybe_grow(BUF_PARAMS(data, trim), buf_size)) {
-
- perror("init_trim grow");
- return -1;
-
- }
-
memcpy(data->trim_buf, buf, buf_size);
data->trim_size_current = buf_size;
@@ -282,27 +268,11 @@ int32_t afl_custom_post_trim(my_mutator_t *data, int success) {
size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size,
u8 **out_buf, size_t max_size) {
- if (buf_size == 0) {
-
- *out_buf = maybe_grow(BUF_PARAMS(data, havoc), 1);
- if (!*out_buf) {
-
- perror("custom havoc: maybe_grow");
- return 0;
-
- }
+ *out_buf = buf; // in-place mutation
- **out_buf = rand() % 256;
- buf_size = 1;
-
- } else {
-
- // We reuse buf here. It's legal and faster.
- *out_buf = buf;
-
- }
+ if (buf_size <= sizeof(size_t)) { return buf_size; }
- size_t victim = rand() % buf_size;
+ size_t victim = rand() % (buf_size - sizeof(size_t));
(*out_buf)[victim] += rand() % 10;
return buf_size;
@@ -369,9 +339,7 @@ uint8_t afl_custom_queue_new_entry(my_mutator_t *data,
void afl_custom_deinit(my_mutator_t *data) {
free(data->post_process_buf);
- free(data->havoc_buf);
- free(data->data_buf);
- free(data->fuzz_buf);
+ free(data->mutated_out);
free(data->trim_buf);
free(data);
diff --git a/custom_mutators/examples/post_library_gif.so.c b/custom_mutators/examples/post_library_gif.so.c
index 9cd224f4..6737c627 100644
--- a/custom_mutators/examples/post_library_gif.so.c
+++ b/custom_mutators/examples/post_library_gif.so.c
@@ -45,9 +45,8 @@
1) If you don't want to modify the test case, simply set `*out_buf = in_buf`
and return the original `len`.
- NOTE: the following is currently NOT true, we abort in this case!
2) If you want to skip this test case altogether and have AFL generate a
- new one, return 0 or set `*out_buf = NULL`.
+ new one, return 0.
Use this sparingly - it's faster than running the target program
with patently useless inputs, but still wastes CPU time.
@@ -59,8 +58,6 @@
Note that the buffer will *not* be freed for you. To avoid memory leaks,
you need to free it or reuse it on subsequent calls (as shown below).
- *** Feel free to reuse the original 'in_buf' BUFFER and return it. ***
-
Alright. The example below shows a simple postprocessor that tries to make
sure that all input files start with "GIF89a".
@@ -72,7 +69,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "alloc-inl.h"
+#include "afl-fuzz.h"
/* Header that must be present at the beginning of every test case: */
@@ -80,8 +77,7 @@
typedef struct post_state {
- unsigned char *buf;
- size_t size;
+ size_t size;
} post_state_t;
@@ -95,15 +91,6 @@ void *afl_custom_init(void *afl) {
}
- state->buf = calloc(sizeof(unsigned char), 4096);
- if (!state->buf) {
-
- free(state);
- perror("calloc");
- return NULL;
-
- }
-
return state;
}
@@ -113,6 +100,10 @@ void *afl_custom_init(void *afl) {
size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
unsigned int len, unsigned char **out_buf) {
+ /* we do in-place modification as we do not increase the size */
+
+ *out_buf = in_buf;
+
/* Skip execution altogether for buffers shorter than 6 bytes (just to
show how it's done). We can trust len to be sane. */
@@ -120,34 +111,7 @@ size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
/* Do nothing for buffers that already start with the expected header. */
- if (!memcmp(in_buf, HEADER, strlen(HEADER))) {
-
- *out_buf = in_buf;
- return len;
-
- }
-
- /* Allocate memory for new buffer, reusing previous allocation if
- possible. Note we have to use afl-fuzz's own realloc!
- Note that you should only do this if you need to grow the buffer,
- otherwise work with in_buf, and assign it to *out_buf instead. */
-
- *out_buf = afl_realloc(out_buf, len);
-
- /* If we're out of memory, the most graceful thing to do is to return the
- original buffer and give up on modifying it. Let AFL handle OOM on its
- own later on. */
-
- if (!*out_buf) {
-
- *out_buf = in_buf;
- return len;
-
- }
-
- if (len > strlen(HEADER))
- memcpy(*out_buf + strlen(HEADER), in_buf + strlen(HEADER),
- len - strlen(HEADER));
+ if (!memcmp(in_buf, HEADER, strlen(HEADER))) { return len; }
/* Insert the new header. */
@@ -162,7 +126,6 @@ size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
/* Gets called afterwards */
void afl_custom_deinit(post_state_t *data) {
- free(data->buf);
free(data);
}
diff --git a/custom_mutators/examples/post_library_png.so.c b/custom_mutators/examples/post_library_png.so.c
index cd65b1bc..652da497 100644
--- a/custom_mutators/examples/post_library_png.so.c
+++ b/custom_mutators/examples/post_library_png.so.c
@@ -30,7 +30,7 @@
#include <string.h>
#include <zlib.h>
#include <arpa/inet.h>
-#include "alloc-inl.h"
+#include "afl-fuzz.h"
/* A macro to round an integer up to 4 kB. */
@@ -53,7 +53,7 @@ void *afl_custom_init(void *afl) {
}
- state->buf = calloc(sizeof(unsigned char), 4096);
+ state->buf = calloc(sizeof(unsigned char), MAX_FILE);
if (!state->buf) {
free(state);
@@ -80,21 +80,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
}
- /* This is not a good way to do it, if you do not need to grow the buffer
- then just work with in_buf instead for speed reasons.
- But we want to show how to grow a buffer, so this is how it's done: */
-
- unsigned int pos = 8;
- unsigned char *new_buf = afl_realloc(out_buf, UP4K(len));
-
- if (!new_buf) {
-
- *out_buf = in_buf;
- return len;
-
- }
-
- memcpy(new_buf, in_buf, len);
+ unsigned int pos = 8;
/* Minimum size of a zero-length PNG chunk is 12 bytes; if we
don't have that, we can bail out. */
@@ -124,7 +110,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
if (real_cksum != file_cksum) {
- *(uint32_t *)(new_buf + pos + 8 + chunk_len) = real_cksum;
+ *(uint32_t *)(data->buf + pos + 8 + chunk_len) = real_cksum;
}
@@ -134,7 +120,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
}
- *out_buf = new_buf;
+ *out_buf = data->buf;
return len;
}
diff --git a/custom_mutators/examples/simple_example.c b/custom_mutators/examples/simple_example.c
index d888ec1f..2c0abe29 100644
--- a/custom_mutators/examples/simple_example.c
+++ b/custom_mutators/examples/simple_example.c
@@ -1,6 +1,6 @@
// This simple example just creates random buffer <= 100 filled with 'A'
// needs -I /path/to/AFLplusplus/include
-#include "custom_mutator_helpers.h"
+#include "afl-fuzz.h"
#include <stdint.h>
#include <stdlib.h>
@@ -13,14 +13,14 @@
typedef struct my_mutator {
- afl_t *afl;
+ afl_state_t *afl;
// Reused buffers:
- BUF_VAR(u8, fuzz);
+ u8 *fuzz_buf;
} my_mutator_t;
-my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
+my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
srand(seed);
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
@@ -31,6 +31,14 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
}
+ data->fuzz_buf = (u8 *)malloc(MAX_FILE);
+ if (!data->fuzz_buf) {
+
+ perror("afl_custom_init malloc");
+ return NULL;
+
+ }
+
data->afl = afl;
return data;
@@ -44,18 +52,10 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
int size = (rand() % 100) + 1;
if (size > max_size) size = max_size;
- u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), size);
- if (!mutated_out) {
-
- *out_buf = NULL;
- perror("custom mutator allocation (maybe_grow)");
- return 0; /* afl-fuzz will very likely error out after this. */
-
- }
- memset(mutated_out, _FIXED_CHAR, size);
+ memset(data->fuzz_buf, _FIXED_CHAR, size);
- *out_buf = mutated_out;
+ *out_buf = data->fuzz_buf;
return size;
}
diff --git a/custom_mutators/gramatron/build_gramatron_mutator.sh b/custom_mutators/gramatron/build_gramatron_mutator.sh
index 9952e7f5..c830329e 100755
--- a/custom_mutators/gramatron/build_gramatron_mutator.sh
+++ b/custom_mutators/gramatron/build_gramatron_mutator.sh
@@ -11,7 +11,7 @@
# Adapted for AFLplusplus by Dominik Maier <mail@dmnk.co>
#
# Copyright 2017 Battelle Memorial Institute. All rights reserved.
-# Copyright 2019-2022 AFLplusplus Project. All rights reserved.
+# Copyright 2019-2023 AFLplusplus Project. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -125,7 +125,7 @@ else
}
fi
-test -d json-c/.git || { echo "[-] not checked out, please install git or check your internet connection." ; exit 1 ; }
+test -e json-c/.git || { echo "[-] not checked out, please install git or check your internet connection." ; exit 1 ; }
echo "[+] Got json-c."
test -e json-c/.libs/libjson-c.a || {
diff --git a/custom_mutators/grammar_mutator/build_grammar_mutator.sh b/custom_mutators/grammar_mutator/build_grammar_mutator.sh
index 5121b07f..593cd2dc 100755
--- a/custom_mutators/grammar_mutator/build_grammar_mutator.sh
+++ b/custom_mutators/grammar_mutator/build_grammar_mutator.sh
@@ -14,7 +14,7 @@
# <andreafioraldi@gmail.com>
#
# Copyright 2017 Battelle Memorial Institute. All rights reserved.
-# Copyright 2019-2022 AFLplusplus Project. All rights reserved.
+# Copyright 2019-2023 AFLplusplus Project. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -119,7 +119,7 @@ else
}
fi
-test -f grammar_mutator/.git || { echo "[-] not checked out, please install git or check your internet connection." ; exit 1 ; }
+test -e grammar_mutator/.git || { echo "[-] not checked out, please install git or check your internet connection." ; exit 1 ; }
echo "[+] Got grammar mutator."
cd "grammar_mutator" || exit 1
diff --git a/custom_mutators/libafl_base/Cargo.toml b/custom_mutators/libafl_base/Cargo.toml
index 6e40fc39..ac6b0c8f 100644
--- a/custom_mutators/libafl_base/Cargo.toml
+++ b/custom_mutators/libafl_base/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-libafl = { git = "https://github.com/AFLplusplus/LibAFL.git", rev = "62614ce1016c86e3f00f35b56399292ceabd486b" }
+libafl = { git = "https://github.com/AFLplusplus/LibAFL.git", rev = "266677bb88abe75165430f34e7de897c35560504" }
custom_mutator = { path = "../rust/custom_mutator", features = ["afl_internals"] }
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
diff --git a/custom_mutators/libafl_base/src/lib.rs b/custom_mutators/libafl_base/src/lib.rs
index 6f2db8ca..bae11e1f 100644
--- a/custom_mutators/libafl_base/src/lib.rs
+++ b/custom_mutators/libafl_base/src/lib.rs
@@ -1,5 +1,4 @@
#![cfg(unix)]
-#![allow(unused_variables)]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{
@@ -18,10 +17,12 @@ use libafl::{
scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens},
Mutator,
},
- state::{HasCorpus, HasMaxSize, HasMetadata, HasRand, State},
+ prelude::UsesInput,
+ state::{HasCorpus, HasMaxSize, HasMetadata, HasRand, State, UsesState},
Error,
};
+#[allow(clippy::identity_op)]
const MAX_FILE: usize = 1 * 1024 * 1024;
static mut AFL: Option<&'static afl_state> = None;
@@ -64,24 +65,32 @@ impl<'de> Deserialize<'de> for AFLCorpus {
}
}
-impl Corpus<BytesInput> for AFLCorpus {
+impl UsesState for AFLCorpus {
+ type State = AFLState;
+}
+
+impl Corpus for AFLCorpus {
#[inline]
fn count(&self) -> usize {
afl().queued_items as usize
}
#[inline]
- fn add(&mut self, testcase: Testcase<BytesInput>) -> Result<usize, Error> {
+ fn add(&mut self, _testcase: Testcase<BytesInput>) -> Result<usize, Error> {
unimplemented!();
}
#[inline]
- fn replace(&mut self, idx: usize, testcase: Testcase<BytesInput>) -> Result<(), Error> {
+ fn replace(
+ &mut self,
+ _idx: usize,
+ _testcase: Testcase<BytesInput>,
+ ) -> Result<Testcase<Self::Input>, Error> {
unimplemented!();
}
#[inline]
- fn remove(&mut self, idx: usize) -> Result<Option<Testcase<BytesInput>>, Error> {
+ fn remove(&mut self, _idx: usize) -> Result<Option<Testcase<BytesInput>>, Error> {
unimplemented!();
}
@@ -92,7 +101,7 @@ impl Corpus<BytesInput> for AFLCorpus {
entries.entry(idx).or_insert_with(|| {
let queue_buf = std::slice::from_raw_parts_mut(afl().queue_buf, self.count());
let entry = queue_buf[idx].as_mut().unwrap();
- let fname = CStr::from_ptr((entry.fname as *mut i8).as_ref().unwrap())
+ let fname = CStr::from_ptr((entry.fname.cast::<i8>()).as_ref().unwrap())
.to_str()
.unwrap()
.to_owned();
@@ -127,9 +136,10 @@ pub struct AFLState {
}
impl AFLState {
+ #[must_use]
pub fn new(seed: u32) -> Self {
Self {
- rand: StdRand::with_seed(seed as u64),
+ rand: StdRand::with_seed(u64::from(seed)),
corpus: AFLCorpus::default(),
metadata: SerdeAnyMap::new(),
max_size: MAX_FILE,
@@ -153,7 +163,11 @@ impl HasRand for AFLState {
}
}
-impl HasCorpus<BytesInput> for AFLState {
+impl UsesInput for AFLState {
+ type Input = BytesInput;
+}
+
+impl HasCorpus for AFLState {
type Corpus = AFLCorpus;
#[inline]
@@ -208,7 +222,7 @@ impl CustomMutator for LibAFLBaseCustomMutator {
tokens.push(data.to_vec());
}
if !tokens.is_empty() {
- state.add_metadata(Tokens::new(tokens));
+ state.add_metadata(Tokens::from(tokens));
}
Ok(Self {
state,
@@ -220,7 +234,7 @@ impl CustomMutator for LibAFLBaseCustomMutator {
fn fuzz<'b, 's: 'b>(
&'s mut self,
buffer: &'b mut [u8],
- add_buff: Option<&[u8]>,
+ _add_buff: Option<&[u8]>,
max_size: usize,
) -> Result<Option<&'b [u8]>, Self::Error> {
self.state.set_max_size(max_size);
diff --git a/custom_mutators/rust/custom_mutator-sys/Cargo.toml b/custom_mutators/rust/custom_mutator-sys/Cargo.toml
index 104f7df0..e38c972e 100644
--- a/custom_mutators/rust/custom_mutator-sys/Cargo.toml
+++ b/custom_mutators/rust/custom_mutator-sys/Cargo.toml
@@ -1,12 +1,12 @@
[package]
name = "custom_mutator-sys"
-version = "0.1.0"
+version = "0.1.1"
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
-edition = "2018"
+edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[build-dependencies]
-bindgen = "0.56"
+bindgen = "0.63"
diff --git a/custom_mutators/rust/custom_mutator-sys/build.rs b/custom_mutators/rust/custom_mutator-sys/build.rs
index 3c88a90d..ba4390ff 100644
--- a/custom_mutators/rust/custom_mutator-sys/build.rs
+++ b/custom_mutators/rust/custom_mutator-sys/build.rs
@@ -15,8 +15,8 @@ fn main() {
// The input header we would like to generate
// bindings for.
.header("wrapper.h")
- .whitelist_type("afl_state_t")
- .blacklist_type(r"u\d+")
+ .allowlist_type("afl_state_t")
+ .blocklist_type(r"u\d+")
.opaque_type(r"_.*")
.opaque_type("FILE")
.opaque_type("in_addr(_t)?")
diff --git a/custom_mutators/rust/custom_mutator-sys/src/lib.rs b/custom_mutators/rust/custom_mutator-sys/src/lib.rs
index a38a13a8..719ac994 100644
--- a/custom_mutators/rust/custom_mutator-sys/src/lib.rs
+++ b/custom_mutators/rust/custom_mutator-sys/src/lib.rs
@@ -1,5 +1,7 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
+#![allow(clippy::too_many_lines)]
+#![allow(clippy::used_underscore_binding)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
diff --git a/custom_mutators/rust/custom_mutator/Cargo.toml b/custom_mutators/rust/custom_mutator/Cargo.toml
index 2d3cdbfa..30f764dc 100644
--- a/custom_mutators/rust/custom_mutator/Cargo.toml
+++ b/custom_mutators/rust/custom_mutator/Cargo.toml
@@ -2,7 +2,7 @@
name = "custom_mutator"
version = "0.1.0"
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
-edition = "2018"
+edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/custom_mutators/rust/custom_mutator/src/lib.rs b/custom_mutators/rust/custom_mutator/src/lib.rs
index f872241e..3b635eb5 100644
--- a/custom_mutators/rust/custom_mutator/src/lib.rs
+++ b/custom_mutators/rust/custom_mutator/src/lib.rs
@@ -20,7 +20,7 @@
//! 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.
+//! 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._
@@ -115,7 +115,7 @@ pub mod wrappers {
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) })
+ ManuallyDrop::new(unsafe { Box::from_raw(ptr.cast::<Self>()) })
}
fn into_ptr(self: Box<Self>) -> *const c_void {
@@ -141,27 +141,28 @@ pub mod wrappers {
}
/// panic handler called for every panic
- fn panic_handler(method: &str, panic_info: Box<dyn Any + Send + 'static>) -> ! {
+ 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(|| {
+ let cause = panic_info.downcast_ref::<String>().map_or_else(
+ || {
panic_info
.downcast_ref::<&str>()
.copied()
.unwrap_or("<cause unknown>")
- });
- eprintln!("A panic occurred at {}: {}", method, cause);
+ },
+ String::deref,
+ );
+ eprintln!("A panic occurred at {method}: {cause}");
abort()
}
/// Internal function used in the macro
#[cfg(not(feature = "afl_internals"))]
+ #[must_use]
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),
+ Err(err) => panic_handler("afl_custom_init", &err),
}
}
@@ -176,7 +177,7 @@ pub mod wrappers {
FFIContext::<M>::new(afl, seed).into_ptr()
}) {
Ok(ret) => ret,
- Err(err) => panic_handler("afl_custom_init", err),
+ Err(err) => panic_handler("afl_custom_init", &err),
}
}
@@ -196,32 +197,27 @@ pub mod wrappers {
) -> 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")
- }
+
+ assert!(!buf.is_null(), "null buf passed to afl_custom_fuzz");
+ assert!(!out_buf.is_null(), "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) {
- Some(buffer) => {
- *out_buf = buffer.as_ptr();
- buffer.len()
- }
- None => {
- // return the input buffer with 0-length to let AFL skip this mutation attempt
- *out_buf = buf;
- 0
- }
+ if let Some(buffer) = context.mutator.fuzz(buff_slice, add_buff_slice, max_size) {
+ *out_buf = buffer.as_ptr();
+ buffer.len()
+ } else {
+ // 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),
+ Err(err) => panic_handler("afl_custom_fuzz", &err),
}
}
@@ -237,9 +233,8 @@ pub mod wrappers {
) -> u32 {
match catch_unwind(|| {
let mut context = FFIContext::<M>::from(data);
- if buf.is_null() {
- panic!("null buf passed to afl_custom_fuzz")
- }
+ assert!(!buf.is_null(), "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;
@@ -247,37 +242,39 @@ pub mod wrappers {
mutator.fuzz_count(buf_slice)
}) {
Ok(ret) => ret,
- Err(err) => panic_handler("afl_custom_fuzz_count", err),
+ Err(err) => panic_handler("afl_custom_fuzz_count", &err),
}
}
/// Internal function used in the macro
- pub fn afl_custom_queue_new_entry_<M: RawCustomMutator>(
+ pub unsafe fn afl_custom_queue_new_entry_<M: RawCustomMutator>(
data: *mut c_void,
filename_new_queue: *const c_char,
filename_orig_queue: *const c_char,
) -> bool {
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");
- }
+ assert!(
+ !filename_new_queue.is_null(),
+ "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() {
+ let filename_orig_queue = if filename_orig_queue.is_null() {
+ None
+ } else {
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),
+ Err(err) => panic_handler("afl_custom_queue_new_entry", &err),
}
}
@@ -292,7 +289,7 @@ pub mod wrappers {
ManuallyDrop::into_inner(FFIContext::<M>::from(data));
}) {
Ok(ret) => ret,
- Err(err) => panic_handler("afl_custom_deinit", err),
+ Err(err) => panic_handler("afl_custom_deinit", &err),
}
}
@@ -306,13 +303,13 @@ pub mod wrappers {
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()
+ CStr::from_bytes_with_nul(buf).unwrap().as_ptr()
} else {
null()
}
}) {
Ok(ret) => ret,
- Err(err) => panic_handler("afl_custom_introspection", err),
+ Err(err) => panic_handler("afl_custom_introspection", &err),
}
}
@@ -329,18 +326,18 @@ pub mod wrappers {
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()
+ CStr::from_bytes_with_nul(buf).unwrap().as_ptr()
} else {
null()
}
}) {
Ok(ret) => ret,
- Err(err) => panic_handler("afl_custom_describe", err),
+ Err(err) => panic_handler("afl_custom_describe", &err),
}
}
/// Internal function used in the macro
- pub fn afl_custom_queue_get_<M: RawCustomMutator>(
+ pub unsafe fn afl_custom_queue_get_<M: RawCustomMutator>(
data: *mut c_void,
filename: *const c_char,
) -> u8 {
@@ -348,12 +345,12 @@ pub mod wrappers {
let mut context = FFIContext::<M>::from(data);
assert!(!filename.is_null());
- context.mutator.queue_get(Path::new(OsStr::from_bytes(
+ u8::from(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),
+ Err(err) => panic_handler("afl_custom_queue_get", &err),
}
}
}
@@ -373,7 +370,7 @@ macro_rules! _define_afl_custom_init {
};
}
-/// An exported macro to defined afl_custom_init meant for insternal usage
+/// An exported macro to defined `afl_custom_init` meant for internal usage
#[cfg(not(feature = "afl_internals"))]
#[macro_export]
macro_rules! _define_afl_custom_init {
@@ -444,7 +441,7 @@ macro_rules! export_mutator {
}
#[no_mangle]
- pub extern "C" fn afl_custom_queue_new_entry(
+ pub unsafe 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,
@@ -457,7 +454,7 @@ macro_rules! export_mutator {
}
#[no_mangle]
- pub extern "C" fn afl_custom_queue_get(
+ pub unsafe extern "C" fn afl_custom_queue_get(
data: *mut ::std::os::raw::c_void,
filename: *const ::std::os::raw::c_char,
) -> u8 {
@@ -520,9 +517,10 @@ mod sanity_test {
export_mutator!(ExampleMutator);
}
-#[allow(unused_variables)]
/// A custom mutator.
/// [`CustomMutator::handle_error`] will be called in case any method returns an [`Result::Err`].
+#[allow(unused_variables)]
+#[allow(clippy::missing_errors_doc)]
pub trait CustomMutator {
/// The error type. All methods must return the same error type.
type Error: Debug;
@@ -537,7 +535,7 @@ pub trait CustomMutator {
.map(|v| !v.is_empty())
.unwrap_or(false)
{
- eprintln!("Error in custom mutator: {:?}", err)
+ eprintln!("Error in custom mutator: {err:?}");
}
}
@@ -759,8 +757,7 @@ mod truncate_test {
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
+ "{input:#?} truncated to {max_len} bytes should be {expected_output:#?}, but is {actual_output:#?}"
);
}
}
diff --git a/custom_mutators/rust/example/Cargo.toml b/custom_mutators/rust/example/Cargo.toml
index 070d23b1..9d53ebe5 100644
--- a/custom_mutators/rust/example/Cargo.toml
+++ b/custom_mutators/rust/example/Cargo.toml
@@ -2,7 +2,7 @@
name = "example_mutator"
version = "0.1.0"
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
-edition = "2018"
+edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/custom_mutators/rust/example_lain/Cargo.toml b/custom_mutators/rust/example_lain/Cargo.toml
index 29d606a4..c52bf86f 100644
--- a/custom_mutators/rust/example_lain/Cargo.toml
+++ b/custom_mutators/rust/example_lain/Cargo.toml
@@ -2,7 +2,7 @@
name = "example_lain"
version = "0.1.0"
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
-edition = "2018"
+edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html