diff options
Diffstat (limited to 'custom_mutators')
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 |