| /* |
| * |
| * honggfuzz - run->dynfile->datafer mangling routines |
| * ----------------------------------------- |
| * |
| * Author: |
| * Robert Swiecki <swiecki@google.com> |
| * |
| * Copyright 2010-2018 by Google Inc. 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. You may obtain |
| * a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * permissions and limitations under the License. |
| * |
| */ |
| |
| #include "mangle.h" |
| |
| #include <ctype.h> |
| #include <inttypes.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <time.h> |
| |
| #include "input.h" |
| #include "libhfcommon/common.h" |
| #include "libhfcommon/log.h" |
| #include "libhfcommon/util.h" |
| |
| typedef enum { |
| MANGLE_SHRINK = 0, |
| MANGLE_EXPAND, |
| MANGLE_BIT, |
| MANGLE_INC_BYTE, |
| MANGLE_DEC_BYTE, |
| MANGLE_NEG_BYTE, |
| MANGLE_ADD_SUB, |
| MANGLE_ARITH8, |
| MANGLE_MEM_SET, |
| MANGLE_MEM_CLR, |
| MANGLE_MEM_SWAP, |
| MANGLE_MEM_COPY, |
| MANGLE_BLOCK_MOVE, |
| MANGLE_BLOCK_REPEAT, |
| MANGLE_BLOCK_SWAP, |
| MANGLE_CHUNK_SHUFFLE, |
| MANGLE_BYTES, |
| MANGLE_BYTE_REPEAT, |
| MANGLE_RANDOM_BUF, |
| MANGLE_INTERESTING_VALUES, |
| MANGLE_ASCII_NUM, |
| MANGLE_ASCII_NUM_CHANGE, |
| MANGLE_MAGIC, |
| MANGLE_STATIC_DICT, |
| MANGLE_CONST_FEEDBACK_DICT, |
| MANGLE_CMP_SOLVE, |
| MANGLE_SPLICE, |
| MANGLE_CROSS_OVER, |
| MANGLE_SPECIAL_STRINGS, |
| MANGLE_TLV_MUTATE, |
| MANGLE_TOKEN_SHUFFLE, |
| MANGLE_GRADIENT_CMP, |
| MANGLE_ARITH_CONST, |
| MANGLE_DICT_INSERT, |
| MANGLE_PUNCTUATION, |
| MANGLE_HAVOC, |
| MANGLE_COUNT |
| } mangle_t; |
| |
| 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 |
| * 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; |
| } |
| |
| /* 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; |
| } |
| |
| /* 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 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); |
| |
| 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 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); |
| } |
| |
| 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_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); |
| } |
| } |
| |
| #if 0 |
| 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); |
| } |
| } |
| #endif |
| |
| 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)); |
| |
| 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_BlockMove(run_t* run, bool printable HF_ATTR_UNUSED) { |
| size_t off_from = mangle_getOffSet(run); |
| size_t off_to = mangle_getOffSet(run); |
| size_t len = mangle_getLen(run->dynfile->size); |
| mangle_Move(run, off_from, off_to, len); |
| } |
| |
| 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 { |
| free(tmpbuf); |
| }; |
| memmove(tmpbuf, &run->dynfile->data[off], len); |
| |
| mangle_UseValue(run, tmpbuf, len, printable); |
| } |
| |
| 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_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); |
| 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 const struct { |
| #if __has_attribute(nonstring) |
| const uint8_t val[8] __attribute__((nonstring)); |
| #else |
| const uint8_t val[8]; |
| #endif /* __has_attribute(nonstring) */ |
| 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}, |
| {"\x02\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x03\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x04\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x05\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x06\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x07\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x08\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x09\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x0A\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x0B\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x0C\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x0D\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x0E\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x0F\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x10\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x20\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x40\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x7E\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x7F\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x80\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\x81\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\xC0\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\xFE\x00\x00\x00\x00\x00\x00\x00", 1}, |
| {"\xFF\x00\x00\x00\x00\x00\x00\x00", 1}, |
| /* 2B - NE */ |
| {"\x00\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x01\x01\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x80\x80\x00\x00\x00\x00\x00\x00", 2}, |
| {"\xFF\xFF\x00\x00\x00\x00\x00\x00", 2}, |
| /* 2B - BE */ |
| {"\x00\x01\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x02\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x03\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x04\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x05\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x06\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x07\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x08\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x09\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x0A\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x0B\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x0C\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x0D\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x0E\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x0F\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x10\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x20\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x40\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x7E\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x7F\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x80\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x81\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\xC0\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\xFE\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\xFF\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x7E\xFF\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x7F\xFF\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x80\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x80\x01\x00\x00\x00\x00\x00\x00", 2}, |
| {"\xFF\xFE\x00\x00\x00\x00\x00\x00", 2}, |
| /* 2B - LE */ |
| {"\x00\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x01\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x02\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x03\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x04\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x05\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x06\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x07\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x08\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x09\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x0A\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x0B\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x0C\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x0D\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x0E\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x0F\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x10\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x20\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x40\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x7E\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x7F\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x80\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x81\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\xC0\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\xFE\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\xFF\x00\x00\x00\x00\x00\x00\x00", 2}, |
| {"\xFF\x7E\x00\x00\x00\x00\x00\x00", 2}, |
| {"\xFF\x7F\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x00\x80\x00\x00\x00\x00\x00\x00", 2}, |
| {"\x01\x80\x00\x00\x00\x00\x00\x00", 2}, |
| {"\xFE\xFF\x00\x00\x00\x00\x00\x00", 2}, |
| /* 4B - NE */ |
| {"\x00\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x01\x01\x01\x01\x00\x00\x00\x00", 4}, |
| {"\x80\x80\x80\x80\x00\x00\x00\x00", 4}, |
| {"\xFF\xFF\xFF\xFF\x00\x00\x00\x00", 4}, |
| /* 4B - BE */ |
| {"\x00\x00\x00\x01\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x02\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x03\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x04\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x05\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x06\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x07\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x08\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x09\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x0A\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x0B\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x0C\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x0D\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x0E\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x0F\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x10\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x20\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x40\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x7E\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x7F\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x80\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x81\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\xC0\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\xFE\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\xFF\x00\x00\x00\x00", 4}, |
| {"\x7E\xFF\xFF\xFF\x00\x00\x00\x00", 4}, |
| {"\x7F\xFF\xFF\xFF\x00\x00\x00\x00", 4}, |
| {"\x80\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x80\x00\x00\x01\x00\x00\x00\x00", 4}, |
| {"\xFF\xFF\xFF\xFE\x00\x00\x00\x00", 4}, |
| /* 4B - LE */ |
| {"\x00\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x01\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x02\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x03\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x04\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x05\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x06\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x07\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x08\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x09\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x0A\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x0B\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x0C\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x0D\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x0E\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x0F\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x10\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x20\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x40\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x7E\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x7F\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x80\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\x81\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\xC0\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\xFE\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\xFF\x00\x00\x00\x00\x00\x00\x00", 4}, |
| {"\xFF\xFF\xFF\x7E\x00\x00\x00\x00", 4}, |
| {"\xFF\xFF\xFF\x7F\x00\x00\x00\x00", 4}, |
| {"\x00\x00\x00\x80\x00\x00\x00\x00", 4}, |
| {"\x01\x00\x00\x80\x00\x00\x00\x00", 4}, |
| {"\xFE\xFF\xFF\xFF\x00\x00\x00\x00", 4}, |
| /* 8B - NE */ |
| {"\x00\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x01\x01\x01\x01\x01\x01\x01\x01", 8}, |
| {"\x80\x80\x80\x80\x80\x80\x80\x80", 8}, |
| {"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8}, |
| /* 8B - BE */ |
| {"\x00\x00\x00\x00\x00\x00\x00\x01", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x02", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x03", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x04", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x05", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x06", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x07", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x08", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x09", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x0A", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x0B", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x0C", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x0D", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x0E", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x0F", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x10", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x20", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x40", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x7E", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x7F", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x80", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\x81", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\xC0", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\xFE", 8}, |
| {"\x00\x00\x00\x00\x00\x00\x00\xFF", 8}, |
| {"\x7E\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8}, |
| {"\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8}, |
| {"\x80\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x80\x00\x00\x00\x00\x00\x00\x01", 8}, |
| {"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE", 8}, |
| /* 8B - LE */ |
| {"\x00\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x01\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x02\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x03\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x04\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x05\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x06\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x07\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x08\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x09\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x0A\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x0B\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x0C\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x0D\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x0E\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x0F\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x10\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x20\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x40\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x7E\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x7F\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x80\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\x81\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\xC0\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\xFE\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\xFF\x00\x00\x00\x00\x00\x00\x00", 8}, |
| {"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7E", 8}, |
| {"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F", 8}, |
| {"\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 inline const uint8_t* mangle_FeedbackDict(run_t* run, size_t* len) { |
| fuzz_data_t* cmpf = run->global->feedback.cmpFeedbackMap; |
| uint32_t cnt = ATOMIC_GET(cmpf->dictCnt); |
| if (cnt > 0) { |
| uint32_t max_idx = HF_MIN(cnt, ARRAYSIZE(cmpf->dict)); |
| uint32_t choice = util_rndGet(0, max_idx - 1); |
| *len = (size_t)ATOMIC_GET(cmpf->dict[choice].len); |
| if (*len > 0) { |
| return cmpf->dict[choice].val; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static void mangle_StaticDict(run_t* run, bool printable) { |
| size_t len; |
| const uint8_t* val = mangle_FeedbackDict(run, &len); |
| if (val == NULL) { |
| mangle_Bytes(run, printable); |
| return; |
| } |
| |
| /* 10% of time, for sizes 2/4/8, use bswap'd value */ |
| uint8_t buf[8]; |
| if ((len == 2 || len == 4 || len == 8) && util_rndGet(0, 9) == 0) { |
| memcpy(buf, val, len); |
| if (len == 2) { |
| uint16_t v; |
| memcpy(&v, buf, sizeof(v)); |
| v = __builtin_bswap16(v); |
| memcpy(buf, &v, sizeof(v)); |
| } else if (len == 4) { |
| uint32_t v; |
| memcpy(&v, buf, sizeof(v)); |
| v = __builtin_bswap32(v); |
| memcpy(buf, &v, sizeof(v)); |
| } else { |
| uint64_t v; |
| memcpy(&v, buf, sizeof(v)); |
| v = __builtin_bswap64(v); |
| memcpy(buf, &v, sizeof(v)); |
| } |
| val = buf; |
| } |
| |
| mangle_UseValue(run, val, len, printable); |
| } |
| |
| static void mangle_ConstFeedbackDict(run_t* run, bool printable) { |
| mangle_StaticDict(run, printable); |
| } |
| |
| 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_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; |
| |
| if (util_rnd64() & 1) { |
| len = mangle_Inflate(run, off, len, printable); |
| } |
| |
| memset(&run->dynfile->data[off], val, len); |
| } |
| |
| 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); |
| } |
| |
| if (printable) { |
| util_rndBufPrintable(&run->dynfile->data[off], len); |
| } else { |
| util_rndBuf(&run->dynfile->data[off], 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; |
| } |
| case 2: { |
| int16_t val; |
| util_memcpyInline(&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; |
| util_memcpyInline(&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; |
| util_memcpyInline(&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); |
| } |
| } |
| } |
| |
| 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; |
| } |
| |
| /* Ranges relative to the width of the type */ |
| const uint64_t range8Bit = 16; |
| const uint64_t range16Bit = 4096; |
| const uint64_t range32Bit = 1048576; |
| const uint64_t range64Bit = 268435456; |
| |
| uint64_t range; |
| switch (varLen) { |
| case 1: |
| range = range8Bit; |
| break; |
| case 2: |
| range = range16Bit; |
| break; |
| case 4: |
| range = range32Bit; |
| break; |
| case 8: |
| range = range64Bit; |
| 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; |
| } |
| } |
| |
| 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_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_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_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_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; |
| } |
| } |
| 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'); |
| } |
| |
| enum { OP_INC = 0, OP_DEC, OP_MUL, OP_DIV, OP_RND, OP_ADD_RND, OP_SUB_RND, OP_NOT, OP_COUNT }; |
| |
| switch (util_rndGet(0, OP_COUNT - 1)) { |
| case OP_INC: |
| val++; |
| break; |
| case OP_DEC: |
| val--; |
| break; |
| case OP_MUL: |
| val *= 2; |
| break; |
| case OP_DIV: |
| val /= 2; |
| break; |
| case OP_RND: |
| val = util_rnd64(); |
| break; |
| case OP_ADD_RND: |
| val += util_rndGet(1, 256); |
| break; |
| case OP_SUB_RND: |
| val -= util_rndGet(1, 256); |
| break; |
| case OP_NOT: |
| val = ~(val); |
| break; |
| default: |
| LOG_F("Invalid choice"); |
| }; |
| |
| char buf[64]; |
| snprintf(buf, sizeof(buf), "%" PRIu64, val); |
| size_t new_len = strlen(buf); |
| |
| if (util_rnd64() & 1) { |
| mangle_Insert(run, off, (const uint8_t*)buf, new_len, printable); |
| } else { |
| if (new_len == len) { |
| mangle_Overwrite(run, off, (const uint8_t*)buf, new_len, printable); |
| } else if (new_len > len) { |
| mangle_Inflate(run, off + len, new_len - len, printable); |
| mangle_Overwrite(run, off, (const uint8_t*)buf, new_len, printable); |
| } else { |
| mangle_Overwrite(run, off, (const uint8_t*)buf, new_len, printable); |
| mangle_Move(run, off + len, off + new_len, run->dynfile->size - (off + len)); |
| input_setSize(run, run->dynfile->size - (len - new_len)); |
| } |
| } |
| } |
| |
| static void mangle_Splice(run_t* run, bool printable) { |
| if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) { |
| mangle_Bytes(run, printable); |
| return; |
| } |
| |
| 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; |
| |
| /* Probability distribution (out of 32) |
| * 0: arbitrary size (1/32) |
| * 1-4: small increase (4/32) |
| * 5: large increase (1/32) |
| * 6-9: small decrease (4/32) |
| * 10: large decrease (1/32) |
| * 11-32: no change (21/32) |
| */ |
| 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; |
| default: /* Do nothing */ |
| newsz = oldsz; |
| 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); |
| } |
| } |
| } |
| |
| static void mangle_BlockRepeat(run_t* run, bool printable) { |
| size_t off = mangle_getOffSet(run); |
| size_t len = mangle_getLen(run->dynfile->size - off); |
| |
| len = HF_MIN(len, 1024); |
| |
| uint8_t* tmp = util_Malloc(len); |
| defer { |
| free(tmp); |
| }; |
| memcpy(tmp, run->dynfile->data + off, len); |
| |
| size_t repeats = 0; |
| /* 1/16 chance to repeat a LOT - useful for buffer overflows */ |
| if (util_rnd64() % 16 == 0) { |
| repeats = util_rndGet(16, 256); |
| } else { |
| repeats = util_rndGet(1, 16); |
| } |
| |
| size_t total_add = len * repeats; |
| size_t added = mangle_Inflate(run, off + len, total_add, printable); |
| |
| for (size_t i = 0; i < added; i += len) { |
| size_t copy_len = HF_MIN(len, added - i); |
| memcpy(run->dynfile->data + off + len + i, tmp, copy_len); |
| } |
| } |
| |
| static void mangle_BlockSwap(run_t* run, bool printable HF_ATTR_UNUSED) { |
| if (run->dynfile->size < 8) return; |
| |
| size_t max_len = run->dynfile->size / 4; |
| if (max_len < 1) return; |
| size_t len = util_rndGet(1, HF_MIN(max_len, 256)); |
| |
| size_t space = run->dynfile->size - len * 2; |
| if (space < 1) return; |
| |
| size_t off1 = util_rndGet(0, space); |
| size_t gap = run->dynfile->size - off1 - len * 2; |
| size_t off2 = off1 + len + (gap > 0 ? util_rndGet(0, gap) : 0); |
| |
| if (off2 + len > run->dynfile->size) return; |
| |
| uint8_t* tmp = util_Malloc(len); |
| defer { |
| free(tmp); |
| }; |
| |
| memcpy(tmp, run->dynfile->data + off1, len); |
| memmove(run->dynfile->data + off1, run->dynfile->data + off2, len); |
| memcpy(run->dynfile->data + off2, tmp, len); |
| } |
| |
| static void mangle_CmpSolve(run_t* run, bool printable) { |
| size_t cmp_len; |
| const uint8_t* cmp_val_ptr = mangle_FeedbackDict(run, &cmp_len); |
| if (cmp_val_ptr == NULL) { |
| mangle_Magic(run, printable); |
| return; |
| } |
| |
| if (cmp_len == 0 || cmp_len > 32) { |
| mangle_Magic(run, printable); |
| return; |
| } |
| |
| uint8_t cmp_val[32]; |
| memcpy(cmp_val, cmp_val_ptr, cmp_len); |
| |
| /* Find partial match in input */ |
| for (size_t off = 0; off + cmp_len <= run->dynfile->size; off++) { |
| size_t matches = 0; |
| for (size_t i = 0; i < cmp_len; i++) { |
| if (run->dynfile->data[off + i] == cmp_val[i]) matches++; |
| } |
| |
| if (matches > 0 && matches < cmp_len) { |
| /* Gradient - 50% exact, 25% val+1, 25% val-1 */ |
| uint64_t r = util_rndGet(0, 3); |
| if (r == 1 && cmp_len <= 8) { |
| /* Increment as little-endian integer */ |
| for (size_t i = 0; i < cmp_len; i++) { |
| if (++cmp_val[i] != 0) break; |
| } |
| } else if (r == 2 && cmp_len <= 8) { |
| /* Decrement as little-endian integer */ |
| for (size_t i = 0; i < cmp_len; i++) { |
| if (cmp_val[i]-- != 0) break; |
| } |
| } |
| mangle_Overwrite(run, off, cmp_val, cmp_len, printable); |
| return; |
| } |
| } |
| |
| mangle_UseValue(run, cmp_val, cmp_len, printable); |
| } |
| |
| static void mangle_InterestingValues(run_t* run, bool printable) { |
| static const struct { |
| const uint8_t val[8]; |
| const size_t len; |
| } interestingVals[] = { |
| /* 8-bit */ |
| {{0x00}, 1}, |
| {{0x01}, 1}, |
| {{0x7f}, 1}, |
| {{0x80}, 1}, |
| {{0xff}, 1}, |
| |
| /* 16-bit */ |
| {{0x7f, 0xff}, 2}, |
| {{0x80, 0x00}, 2}, |
| {{0xff, 0xff}, 2}, |
| {{0x00, 0x01}, 2}, |
| {{0x00, 0x00}, 2}, |
| |
| /* 32-bit */ |
| {{0x7f, 0xff, 0xff, 0xff}, 4}, |
| {{0x80, 0x00, 0x00, 0x00}, 4}, |
| {{0xff, 0xff, 0xff, 0xff}, 4}, |
| {{0x00, 0x00, 0x00, 0x01}, 4}, |
| {{0x00, 0x00, 0x00, 0x00}, 4}, |
| |
| /* 64-bit */ |
| {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 8}, |
| {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 8}, |
| {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 8}, |
| {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, 8}, |
| {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 8}, |
| }; |
| |
| size_t choice = util_rndGet(0, ARRAYSIZE(interestingVals) - 1); |
| mangle_UseValue(run, interestingVals[choice].val, interestingVals[choice].len, printable); |
| } |
| |
| static void mangle_SpecialStrings(run_t* run, bool printable) { |
| static const char* const strings[] = { |
| /* Format strings */ |
| "%s", |
| "%n", |
| "%x", |
| "%p", |
| "%9999999s", |
| "%08x", |
| /* SQL Injection / Quote imbalance */ |
| "'", |
| "\"", |
| "`", |
| "1=1", |
| "--", |
| "/*", |
| "*/", |
| " OR ", |
| " AND ", |
| "UNION SELECT", |
| /* Path */ |
| "../", |
| "..\\", |
| "../../../../../../../../etc/passwd", |
| "boot.ini", |
| "/bin/sh", |
| /* XML/HTML */ |
| "<", |
| ">", |
| "<script>", |
| "javascript:", |
| "CDATA", |
| "<!--", |
| "-->", |
| /* JSON/Misc */ |
| "null", |
| "true", |
| "false", |
| "NaN", |
| "Infinity", |
| "undefined", |
| "{}", |
| "[]", |
| /* Command Injection */ |
| "|", |
| ";", |
| "`", |
| "$(", |
| "&&", |
| "||", |
| /* Terminator/Separators */ |
| "\n", |
| "\r\n", |
| "\x00", |
| "\xff", |
| }; |
| |
| const char* val = strings[util_rndGet(0, ARRAYSIZE(strings) - 1)]; |
| mangle_UseValue(run, (const uint8_t*)val, strlen(val), printable); |
| } |
| |
| static void mangle_ChunkShuffle(run_t* run, bool printable HF_ATTR_UNUSED) { |
| if (run->dynfile->size < 8) return; |
| |
| size_t chunk_size = util_rndGet(1, 4); |
| size_t num_chunks = run->dynfile->size / chunk_size; |
| if (num_chunks < 2) return; |
| |
| size_t max_swaps = num_chunks / 2; |
| if (max_swaps < 1) max_swaps = 1; |
| size_t swaps = util_rndGet(1, max_swaps); |
| for (size_t s = 0; s < swaps; s++) { |
| size_t i = util_rndGet(0, num_chunks - 1); |
| size_t j = util_rndGet(0, num_chunks - 1); |
| if (i == j) continue; |
| |
| for (size_t k = 0; k < chunk_size; k++) { |
| uint8_t tmp = run->dynfile->data[i * chunk_size + k]; |
| run->dynfile->data[i * chunk_size + k] = run->dynfile->data[j * chunk_size + k]; |
| run->dynfile->data[j * chunk_size + k] = tmp; |
| } |
| } |
| } |
| |
| static void mangle_Arith8(run_t* run, bool printable) { |
| size_t off = mangle_getOffSet(run); |
| int8_t delta = (int8_t)util_rndGet(1, 35) * (util_rnd64() & 1 ? 1 : -1); |
| run->dynfile->data[off] = (uint8_t)((int8_t)run->dynfile->data[off] + delta); |
| if (printable) { |
| util_turnToPrintable(&run->dynfile->data[off], 1); |
| } |
| } |
| |
| /* |
| * TLV (Tag-Length-Value) mutation - detects length fields and mutates them |
| * Common in binary protocols, ASN.1, network packets, file formats |
| */ |
| static void mangle_TlvMutate(run_t* run, bool printable) { |
| if (run->dynfile->size < 4) { |
| mangle_Bytes(run, printable); |
| return; |
| } |
| |
| /* Scan for potential length fields: byte that matches distance to some boundary */ |
| /* Limit scan to first 4KB or 10% of file to avoid O(N) penalty on large inputs */ |
| size_t scan_limit = HF_MIN(run->dynfile->size - 2, 4096); |
| if (run->dynfile->size > 40960) { |
| scan_limit = HF_MAX(scan_limit, run->dynfile->size / 10); |
| } |
| |
| for (size_t off = 0; off < scan_limit; off++) { |
| uint8_t b1 = run->dynfile->data[off]; |
| uint16_t b2 = 0; |
| size_t len_size = 1; |
| |
| if (off + 1 < run->dynfile->size) { |
| b2 = (uint16_t)run->dynfile->data[off] << 8 | run->dynfile->data[off + 1]; |
| len_size = 2; |
| } |
| |
| /* Check if b1 or b2 could be a length field pointing within remaining data */ |
| size_t remaining = run->dynfile->size - off - 1; |
| bool found = false; |
| |
| if (b1 > 0 && b1 <= remaining) { |
| /* 1-byte length field candidate */ |
| found = true; |
| len_size = 1; |
| } else if (len_size == 2 && b2 > 0 && b2 <= remaining && b2 < run->dynfile->size) { |
| /* 2-byte length field candidate (big-endian) */ |
| found = true; |
| } |
| |
| /* Found a candidate - mutate it with 1/8 probability */ |
| if (found && util_rnd64() % 8 == 0) { |
| /* Mutate the length field */ |
| uint8_t mutations[] = { |
| 0x00, /* Zero length */ |
| 0x01, /* Minimal */ |
| 0x7f, /* Max signed byte */ |
| 0x80, /* Min negative as signed */ |
| 0xff, /* Max byte */ |
| (uint8_t)(remaining & 0xff), /* Exact remaining */ |
| (uint8_t)((remaining + 1) & 0xff), /* Off by one */ |
| (uint8_t)((remaining * 2) & 0xff), /* Double */ |
| }; |
| uint8_t new_len = mutations[util_rndGet(0, ARRAYSIZE(mutations) - 1)]; |
| run->dynfile->data[off] = new_len; |
| if (printable) { |
| util_turnToPrintable(&run->dynfile->data[off], 1); |
| } |
| return; |
| } |
| } |
| |
| /* Fallback: insert a TLV-like structure */ |
| uint8_t tlv[4] = { |
| (uint8_t)util_rndGet(0, 255), /* Tag */ |
| (uint8_t)util_rndGet(1, 16), /* Length */ |
| (uint8_t)util_rndGet(0, 255), /* Value byte 1 */ |
| (uint8_t)util_rndGet(0, 255), /* Value byte 2 */ |
| }; |
| mangle_UseValue(run, tlv, sizeof(tlv), printable); |
| } |
| |
| /* |
| * Token-based mutation - split on common delimiters and shuffle/modify tokens |
| * Effective for text protocols, config files, command lines |
| */ |
| static void mangle_TokenShuffle(run_t* run, bool printable HF_ATTR_UNUSED) { |
| if (run->dynfile->size < 4) return; |
| |
| /* Find delimiter positions */ |
| static const char delims[] = " \t\n\r,;:|/\\=&?"; |
| size_t token_starts[64]; |
| size_t token_cnt = 0; |
| |
| token_starts[token_cnt++] = 0; |
| for (size_t i = 0; i < run->dynfile->size && token_cnt < ARRAYSIZE(token_starts) - 1; i++) { |
| for (size_t d = 0; d < sizeof(delims) - 1; d++) { |
| if (run->dynfile->data[i] == (uint8_t)delims[d]) { |
| if (i + 1 < run->dynfile->size) { |
| token_starts[token_cnt++] = i + 1; |
| } |
| break; |
| } |
| } |
| } |
| |
| if (token_cnt < 2) return; |
| |
| /* Swap two random tokens */ |
| size_t idx1 = util_rndGet(0, token_cnt - 2); |
| size_t idx2 = util_rndGet(idx1 + 1, token_cnt - 1); |
| |
| size_t start1 = token_starts[idx1]; |
| size_t end1 = token_starts[idx1 + 1]; |
| size_t start2 = token_starts[idx2]; |
| size_t end2 = (idx2 + 1 < token_cnt) ? token_starts[idx2 + 1] : run->dynfile->size; |
| |
| size_t len1 = end1 - start1; |
| size_t len2 = end2 - start2; |
| |
| if (len1 == 0 || len2 == 0 || len1 > 256 || len2 > 256) return; |
| |
| /* Simple swap: copy both tokens, then write back swapped */ |
| uint8_t* tmp1 = util_Malloc(len1); |
| defer { |
| free(tmp1); |
| }; |
| uint8_t* tmp2 = util_Malloc(len2); |
| defer { |
| free(tmp2); |
| }; |
| |
| memcpy(tmp1, &run->dynfile->data[start1], len1); |
| memcpy(tmp2, &run->dynfile->data[start2], len2); |
| |
| /* If same length, simple swap */ |
| if (len1 == len2) { |
| memcpy(&run->dynfile->data[start1], tmp2, len2); |
| memcpy(&run->dynfile->data[start2], tmp1, len1); |
| } |
| /* Different lengths - move middle block then insert tokens */ |
| else { |
| /* |
| * Layout: [Prefix][Token1][Middle][Token2][Suffix] |
| * Want: [Prefix][Token2][Middle][Token1][Suffix] |
| * |
| * 1. Copy Token2 to Start1 |
| * 2. Move Middle from End1 to Start1+Len2 |
| * 3. Copy Token1 to Start1+Len2+MiddleLen |
| */ |
| |
| size_t mid_len = start2 - end1; |
| |
| /* Step 2: Move Middle first (using memmove for safety) */ |
| /* Dest: start1 + len2. Src: end1 (which is start1+len1). Len: mid_len */ |
| memmove(&run->dynfile->data[start1 + len2], &run->dynfile->data[end1], mid_len); |
| |
| /* Step 1: Copy Token2 */ |
| memcpy(&run->dynfile->data[start1], tmp2, len2); |
| |
| /* Step 3: Copy Token1 */ |
| /* Dest: start1 + len2 + mid_len */ |
| memcpy(&run->dynfile->data[start1 + len2 + mid_len], tmp1, len1); |
| } |
| } |
| |
| /* |
| * Gradient-guided CMP mutation - focus mutations on bytes that differ in comparisons |
| */ |
| static void mangle_GradientCmp(run_t* run, bool printable) { |
| size_t cmp_len; |
| const uint8_t* cmp_val_ptr = mangle_FeedbackDict(run, &cmp_len); |
| if (cmp_val_ptr == NULL) { |
| mangle_Bytes(run, printable); |
| return; |
| } |
| |
| if (cmp_len == 0 || cmp_len > 32) { |
| mangle_Magic(run, printable); |
| return; |
| } |
| |
| uint8_t cmp_val[32]; |
| memcpy(cmp_val, cmp_val_ptr, cmp_len); |
| |
| /* Find partial match and identify differing bytes */ |
| for (size_t off = 0; off + cmp_len <= run->dynfile->size; off++) { |
| size_t matches = 0; |
| size_t first_diff = cmp_len; |
| uint8_t diff_mask = 0; |
| |
| for (size_t i = 0; i < cmp_len; i++) { |
| if (run->dynfile->data[off + i] == cmp_val[i]) { |
| matches++; |
| } else if (first_diff == cmp_len) { |
| first_diff = i; |
| diff_mask = run->dynfile->data[off + i] ^ cmp_val[i]; |
| } |
| } |
| |
| /* If we have partial progress, focus on the differing byte */ |
| if (matches > 0 && matches < cmp_len && first_diff < cmp_len) { |
| size_t target_off = off + first_diff; |
| |
| /* Gradient strategies */ |
| uint64_t strategy = util_rndGet(0, 5); |
| switch (strategy) { |
| case 0: /* Set to expected value */ |
| run->dynfile->data[target_off] = cmp_val[first_diff]; |
| break; |
| case 1: /* Flip differing bits */ |
| run->dynfile->data[target_off] ^= diff_mask; |
| break; |
| case 2: /* Increment toward target */ |
| if (run->dynfile->data[target_off] < cmp_val[first_diff]) { |
| run->dynfile->data[target_off]++; |
| } else { |
| run->dynfile->data[target_off]--; |
| } |
| break; |
| case 3: /* Binary search toward target */ |
| run->dynfile->data[target_off] = |
| (run->dynfile->data[target_off] + cmp_val[first_diff]) / 2; |
| break; |
| case 4: /* Set entire comparison value */ |
| mangle_Overwrite(run, off, cmp_val, cmp_len, printable); |
| return; |
| case 5: /* Flip single bit in differing byte */ |
| run->dynfile->data[target_off] ^= (1U << util_rndGet(0, 7)); |
| break; |
| } |
| |
| if (printable) { |
| util_turnToPrintable(&run->dynfile->data[target_off], 1); |
| } |
| return; |
| } |
| } |
| |
| /* No partial match found - insert the value */ |
| mangle_UseValue(run, cmp_val, cmp_len, printable); |
| } |
| |
| /* |
| * Arithmetic mutations on discovered constants from CMP feedback |
| */ |
| static void mangle_ArithConst(run_t* run, bool printable) { |
| size_t val_len; |
| const uint8_t* val_ptr = mangle_FeedbackDict(run, &val_len); |
| if (val_ptr == NULL) { |
| mangle_AddSub(run, printable); |
| return; |
| } |
| |
| if (val_len == 0 || val_len > 8) { |
| mangle_AddSub(run, printable); |
| return; |
| } |
| |
| /* Extract value as integer */ |
| uint64_t val = 0; |
| for (size_t i = 0; i < val_len; i++) { |
| val |= ((uint64_t)val_ptr[i]) << (i * 8); |
| } |
| |
| /* Apply arithmetic mutation */ |
| uint64_t op = util_rndGet(0, 7); |
| switch (op) { |
| case 0: |
| val += 1; |
| break; |
| case 1: |
| val -= 1; |
| break; |
| case 2: |
| val *= 2; |
| break; |
| case 3: |
| val /= 2; |
| break; |
| case 4: |
| val ^= 0xff; |
| break; /* Flip low byte */ |
| case 5: |
| val = ~val; |
| break; /* Bitwise NOT */ |
| case 6: |
| val = __builtin_bswap64(val) >> ((8 - val_len) * 8); |
| break; /* Byte swap */ |
| case 7: |
| val += util_rndGet(1, 256); |
| break; |
| } |
| |
| /* Convert back to bytes */ |
| uint8_t result[8]; |
| for (size_t i = 0; i < val_len; i++) { |
| result[i] = (uint8_t)(val >> (i * 8)); |
| } |
| |
| mangle_UseValue(run, result, val_len, printable); |
| } |
| |
| static void mangle_DictionaryInsert(run_t* run, bool printable) { |
| size_t len1; |
| const uint8_t* val1 = mangle_FeedbackDict(run, &len1); |
| if (val1 == NULL) { |
| mangle_Bytes(run, printable); |
| return; |
| } |
| |
| size_t len2; |
| const uint8_t* val2 = mangle_FeedbackDict(run, &len2); |
| if (val2 == NULL) { |
| mangle_Bytes(run, printable); |
| return; |
| } |
| |
| const char* separators[] = { |
| "", " ", "\t", "\n", "\r\n", ",", ";", ":", "=", "&", "|", "(", ")", ".", "\"", "'"}; |
| size_t sep_idx = util_rndGet(0, ARRAYSIZE(separators) - 1); |
| const char* sep = separators[sep_idx]; |
| size_t sep_len = strlen(sep); |
| |
| size_t total_len = len1 + sep_len + len2; |
| |
| uint8_t* buf = util_Malloc(total_len); |
| defer { |
| free(buf); |
| }; |
| |
| memcpy(buf, val1, len1); |
| memcpy(buf + len1, sep, sep_len); |
| memcpy(buf + len1 + sep_len, val2, len2); |
| |
| mangle_UseValue(run, buf, total_len, printable); |
| } |
| |
| static void mangle_Punctuation(run_t* run, bool printable) { |
| static const char punct[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; |
| size_t len = util_rndGet(1, 4); |
| uint8_t buf[4]; |
| |
| for (size_t i = 0; i < len && i < sizeof(buf); i++) { |
| buf[i] = (uint8_t)punct[util_rndGet(0, sizeof(punct) - 2)]; |
| } |
| |
| mangle_UseValue(run, buf, len, printable); |
| } |
| |
| static void mangle_CrossOver(run_t* run, bool printable) { |
| if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) { |
| mangle_Bytes(run, printable); |
| return; |
| } |
| |
| if (run->dynfile->size < 2) { |
| mangle_Bytes(run, printable); |
| return; |
| } |
| |
| /* Use diverse input selection for better coverage combination */ |
| size_t other_sz = 0; |
| const uint8_t* other = input_getDiverseInputAsBuf(run, &other_sz); |
| if (!other || other_sz == 0) { |
| mangle_Bytes(run, printable); |
| return; |
| } |
| |
| size_t crossover_point = util_rndGet(1, run->dynfile->size - 1); |
| size_t other_point = util_rndGet(0, other_sz - 1); |
| size_t copy_len = HF_MIN(run->dynfile->size - crossover_point, other_sz - other_point); |
| |
| if (copy_len > 0) { |
| mangle_Overwrite(run, crossover_point, &other[other_point], copy_len, printable); |
| } |
| } |
| |
| /* |
| * Havoc mode - used when fuzzing is stagnating to escape local minima |
| */ |
| static void mangle_Havoc(run_t* run, bool printable) { |
| /* Number of mutations: 16-128 */ |
| size_t num_mutations = util_rndGet(16, 128); |
| |
| for (size_t i = 0; i < num_mutations; i++) { |
| /* Pick a random simple mutation */ |
| uint64_t choice = util_rndGet(0, 21); |
| switch (choice) { |
| case 0: |
| mangle_Bit(run, printable); |
| break; |
| case 1: |
| mangle_IncByte(run, printable); |
| break; |
| case 2: |
| mangle_DecByte(run, printable); |
| break; |
| case 3: |
| mangle_NegByte(run, printable); |
| break; |
| case 4: |
| mangle_Bytes(run, printable); |
| break; |
| case 5: |
| mangle_Magic(run, printable); |
| break; |
| case 6: |
| mangle_AddSub(run, printable); |
| break; |
| case 7: |
| mangle_MemSet(run, printable); |
| break; |
| case 8: |
| mangle_MemSwap(run, printable); |
| break; |
| case 9: |
| mangle_MemCopy(run, printable); |
| break; |
| case 10: |
| mangle_Expand(run, printable); |
| break; |
| case 11: |
| mangle_Shrink(run, printable); |
| break; |
| case 12: |
| mangle_Arith8(run, printable); |
| break; |
| case 13: |
| mangle_BlockMove(run, printable); |
| break; |
| case 14: |
| mangle_ByteRepeat(run, printable); |
| break; |
| case 15: |
| mangle_RandomBuf(run, printable); |
| break; |
| case 16: |
| mangle_StaticDict(run, printable); |
| break; |
| case 17: |
| mangle_ConstFeedbackDict(run, printable); |
| break; |
| case 18: |
| mangle_DictionaryInsert(run, printable); |
| break; |
| case 19: |
| mangle_CmpSolve(run, printable); |
| break; |
| case 20: |
| mangle_Splice(run, printable); |
| break; |
| case 21: |
| mangle_CrossOver(run, printable); |
| break; |
| } |
| } |
| } |
| |
| /* |
| * Mutation scheduling |
| */ |
| |
| typedef enum { TIER_DATA = 0, TIER_ARITH = 1, TIER_SPLICE = 2, TIER_OTHER = 3 } tier_t; |
| |
| /* Mutation tier arrays - shared between picker and stagnation booster */ |
| static const mangle_t tierData[] = { |
| MANGLE_INTERESTING_VALUES, |
| MANGLE_MAGIC, |
| MANGLE_STATIC_DICT, |
| MANGLE_CONST_FEEDBACK_DICT, |
| MANGLE_CMP_SOLVE, |
| MANGLE_SPECIAL_STRINGS, |
| MANGLE_GRADIENT_CMP, |
| MANGLE_ARITH_CONST, |
| MANGLE_DICT_INSERT, |
| MANGLE_PUNCTUATION, |
| }; |
| |
| static const mangle_t tierArith[] = { |
| MANGLE_BIT, |
| MANGLE_INC_BYTE, |
| MANGLE_DEC_BYTE, |
| MANGLE_NEG_BYTE, |
| MANGLE_ADD_SUB, |
| MANGLE_ARITH8, |
| }; |
| |
| static const mangle_t tierSplice[] = {MANGLE_SPLICE, MANGLE_CROSS_OVER}; |
| |
| static const mangle_t tierStructure[] = { |
| MANGLE_CHUNK_SHUFFLE, |
| MANGLE_BLOCK_REPEAT, |
| MANGLE_BLOCK_SWAP, |
| MANGLE_BLOCK_MOVE, |
| MANGLE_TLV_MUTATE, |
| MANGLE_TOKEN_SHUFFLE, |
| }; |
| |
| static inline mangle_t mangle_pickFromList(const mangle_t* list, size_t cnt) { |
| return cnt > 0 ? list[util_rndGet(0, cnt - 1)] : (mangle_t)util_rndGet(0, MANGLE_COUNT - 1); |
| } |
| |
| static inline mangle_t mangle_sanitize(run_t* run, mangle_t m) { |
| if ((unsigned)m >= MANGLE_COUNT) { |
| return (mangle_t)util_rndGet(0, MANGLE_COUNT - 1); |
| } |
| |
| static const struct { |
| const uint8_t needs; |
| const mangle_t fallback; |
| } reqs[MANGLE_COUNT] = { |
| [MANGLE_STATIC_DICT] = {1, MANGLE_MAGIC}, |
| [MANGLE_CONST_FEEDBACK_DICT] = {2, MANGLE_MAGIC}, |
| [MANGLE_CMP_SOLVE] = {2, MANGLE_MAGIC}, |
| [MANGLE_SPLICE] = {4, MANGLE_RANDOM_BUF}, |
| [MANGLE_CROSS_OVER] = {4, MANGLE_BYTES}, |
| [MANGLE_GRADIENT_CMP] = {2, MANGLE_MAGIC}, |
| [MANGLE_ARITH_CONST] = {2, MANGLE_ADD_SUB}, |
| [MANGLE_DICT_INSERT] = {1, MANGLE_PUNCTUATION}, |
| }; |
| |
| uint8_t need = reqs[m].needs; |
| if (!need) return m; |
| |
| if ((need & 1) && run->global->mutate.dictionaryCnt == 0) return reqs[m].fallback; |
| if ((need & 4) && run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) |
| return reqs[m].fallback; |
| |
| return m; |
| } |
| |
| static mangle_t mangle_pickWeighted(run_t* run, uint8_t* tier_out) { |
| /* |
| * Adaptive weights - start with defaults, adjust based on success rate. |
| * Use a simplified momentum-like approach where recent success bumps the weight |
| */ |
| uint8_t w[4] = {40, 25, 20, 15}; |
| |
| for (int i = 0; i < 4; i++) { |
| uint64_t tries = ATOMIC_GET(run->global->mutate.stats[i].tries); |
| if (tries < 500) { |
| continue; /* Not enough data yet */ |
| } |
| |
| uint64_t hits = ATOMIC_GET(run->global->mutate.stats[i].successes); |
| uint64_t rate = (hits * 10000) / tries; /* x10000 for precision */ |
| |
| /* |
| * Baseline success rate is low (fuzzing is hard), so even small rates are good. |
| * Adjust weights proportionally to performance relative to others. |
| */ |
| if (rate > 50) { /* > 0.5% success rate is very good. */ |
| w[i] = HF_MIN(w[i] + 15, 90); /* Increased boost. */ |
| } else if (rate > 10) { /* > 0.1% */ |
| w[i] = HF_MIN(w[i] + 5, 70); |
| } else if (rate < 1) { /* < 0.01% */ |
| w[i] = HF_MAX(w[i] / 2, 5); |
| } |
| } |
| |
| /* Roll weighted random */ |
| uint16_t sum = w[0] + w[1] + w[2] + w[3]; |
| uint8_t roll = util_rndGet(0, sum - 1); |
| |
| mangle_t choice; |
| uint8_t tier; |
| |
| if (roll < w[0]) { |
| choice = mangle_pickFromList(tierData, ARRAYSIZE(tierData)); |
| tier = TIER_DATA; |
| } else if (roll < w[0] + w[1]) { |
| choice = mangle_pickFromList(tierArith, ARRAYSIZE(tierArith)); |
| tier = TIER_ARITH; |
| } else if (roll < w[0] + w[1] + w[2]) { |
| choice = mangle_pickFromList(tierSplice, ARRAYSIZE(tierSplice)); |
| tier = TIER_SPLICE; |
| } else { |
| choice = (mangle_t)util_rndGet(0, MANGLE_COUNT - 1); |
| tier = TIER_OTHER; |
| } |
| |
| *tier_out = tier; |
| return mangle_sanitize(run, choice); |
| } |
| |
| /* Dispatch table - enum -> function pointer */ |
| static void (*const mangleFuncs[MANGLE_COUNT])(run_t*, bool) = { |
| [MANGLE_SHRINK] = mangle_Shrink, |
| [MANGLE_EXPAND] = mangle_Expand, |
| [MANGLE_BIT] = mangle_Bit, |
| [MANGLE_INC_BYTE] = mangle_IncByte, |
| [MANGLE_DEC_BYTE] = mangle_DecByte, |
| [MANGLE_NEG_BYTE] = mangle_NegByte, |
| [MANGLE_ADD_SUB] = mangle_AddSub, |
| [MANGLE_ARITH8] = mangle_Arith8, |
| [MANGLE_MEM_SET] = mangle_MemSet, |
| [MANGLE_MEM_CLR] = mangle_MemClr, |
| [MANGLE_MEM_SWAP] = mangle_MemSwap, |
| [MANGLE_MEM_COPY] = mangle_MemCopy, |
| [MANGLE_BLOCK_MOVE] = mangle_BlockMove, |
| [MANGLE_BLOCK_REPEAT] = mangle_BlockRepeat, |
| [MANGLE_BLOCK_SWAP] = mangle_BlockSwap, |
| [MANGLE_CHUNK_SHUFFLE] = mangle_ChunkShuffle, |
| [MANGLE_BYTES] = mangle_Bytes, |
| [MANGLE_BYTE_REPEAT] = mangle_ByteRepeat, |
| [MANGLE_RANDOM_BUF] = mangle_RandomBuf, |
| [MANGLE_INTERESTING_VALUES] = mangle_InterestingValues, |
| [MANGLE_ASCII_NUM] = mangle_ASCIINum, |
| [MANGLE_ASCII_NUM_CHANGE] = mangle_ASCIINumChange, |
| [MANGLE_MAGIC] = mangle_Magic, |
| [MANGLE_STATIC_DICT] = mangle_StaticDict, |
| [MANGLE_CONST_FEEDBACK_DICT] = mangle_ConstFeedbackDict, |
| [MANGLE_CMP_SOLVE] = mangle_CmpSolve, |
| [MANGLE_SPLICE] = mangle_Splice, |
| [MANGLE_CROSS_OVER] = mangle_CrossOver, |
| [MANGLE_SPECIAL_STRINGS] = mangle_SpecialStrings, |
| [MANGLE_TLV_MUTATE] = mangle_TlvMutate, |
| [MANGLE_TOKEN_SHUFFLE] = mangle_TokenShuffle, |
| [MANGLE_GRADIENT_CMP] = mangle_GradientCmp, |
| [MANGLE_ARITH_CONST] = mangle_ArithConst, |
| [MANGLE_DICT_INSERT] = mangle_DictionaryInsert, |
| [MANGLE_PUNCTUATION] = mangle_Punctuation, |
| [MANGLE_HAVOC] = mangle_Havoc, |
| }; |
| |
| static inline void mangle_dispatch(run_t* run, mangle_t m, bool printable) { |
| mangleFuncs[mangle_sanitize(run, m)](run, printable); |
| } |
| |
| void mangle_mangleContent(run_t* run) { |
| if (run->mutationsPerRun == 0U) { |
| return; |
| } |
| |
| bool printable = run->global->cfg.only_printable; |
| |
| if (run->dynfile->size == 0U) { |
| mangle_Resize(run, printable); |
| } |
| |
| time_t stagnation = time(NULL) - ATOMIC_GET(run->global->timing.lastCovUpdate); |
| uint64_t base = run->mutationsPerRun; |
| |
| run->mutationTiers = 0; |
| |
| const time_t timeStagnated = 10; |
| const time_t timeStuck = 60; |
| const time_t timeGivenUp = 300; |
| |
| /* Scale mutation count with stagnation */ |
| uint8_t mult = 1, cap = 16, min = 1; |
| if (stagnation > timeGivenUp) { |
| mult = 4; |
| cap = 64; |
| min = 2; |
| } else if (stagnation > timeStuck) { |
| mult = 2; |
| cap = 32; |
| } |
| |
| uint64_t count = util_rndGet(min, HF_MIN(base * mult, cap)); |
| |
| /* |
| * Extra mutations when stagnating. |
| * If we are stuck, we want to try more specific strategies (dictionaries, splices) |
| */ |
| if (stagnation > timeStagnated) { |
| if (util_rnd64() % 3 == 0) { |
| run->mutationTiers |= (1 << TIER_DATA); |
| mangle_dispatch(run, MANGLE_CMP_SOLVE, printable); |
| } |
| if (util_rnd64() % 2 == 0) { |
| run->mutationTiers |= (1 << TIER_SPLICE); |
| mangle_dispatch(run, MANGLE_SPLICE, printable); |
| } |
| /* Try gradient-guided CMP mutations */ |
| if (util_rnd64() % 4 == 0) { |
| run->mutationTiers |= (1 << TIER_DATA); |
| mangle_dispatch(run, MANGLE_GRADIENT_CMP, printable); |
| } |
| } |
| if (stagnation > timeStuck && util_rnd64() % 3 == 0) { |
| run->mutationTiers |= (1 << TIER_SPLICE); |
| mangle_dispatch(run, MANGLE_CROSS_OVER, printable); |
| } |
| if (stagnation > timeGivenUp && util_rnd64() % 8 == 0) { |
| run->mutationTiers |= (1 << TIER_OTHER); |
| mangle_dispatch( |
| run, mangle_pickFromList(tierStructure, ARRAYSIZE(tierStructure)), printable); |
| } |
| /* Havoc mode - when extremely stuck, go wild */ |
| if (stagnation > timeGivenUp * 2 && util_rnd64() % 16 == 0) { |
| run->mutationTiers |= (1 << TIER_OTHER); |
| mangle_dispatch(run, MANGLE_HAVOC, printable); |
| return; /* Havoc does many mutations internally */ |
| } |
| |
| /* Main mutation loop */ |
| for (uint64_t i = 0; i < count; i++) { |
| uint8_t tier; |
| mangle_t m = mangle_pickWeighted(run, &tier); |
| |
| /* |
| * Boost data mutations when stagnating - if stuck for >30s, |
| * 25% chance to force a data mutation (dictionaries, magic values) |
| */ |
| if (stagnation > (timeStuck / 2) && util_rnd64() % 4 == 0) { |
| m = mangle_sanitize(run, mangle_pickFromList(tierData, ARRAYSIZE(tierData))); |
| tier = TIER_DATA; |
| } |
| |
| run->mutationTiers |= (1 << tier); |
| ATOMIC_POST_INC(run->global->mutate.stats[tier].tries); |
| mangle_dispatch(run, m, printable); |
| } |
| } |