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