mirror of
https://github.com/google/honggfuzz.git
synced 2026-10-12 02:14:31 +00:00
1876 lines
59 KiB
C
1876 lines
59 KiB
C
/*
|
|
*
|
|
* honggfuzz - run->dynfile->datafer mangling routines
|
|
* -----------------------------------------
|
|
*
|
|
* Author:
|
|
* Robert Swiecki <[email protected]>
|
|
*
|
|
* 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);
|
|
}
|
|
}
|