forked from OSchip/llvm-project
Refactor mutation strategies into a standalone library
This change introduces libMutagen/libclang_rt.mutagen.a as a subset of libFuzzer/libclang_rt.fuzzer.a. This library contains only the fuzzing strategies used by libFuzzer to produce new test inputs from provided inputs, dictionaries, and SanitizerCoverage feedback. Most of this change is simply moving sections of code to one side or the other of the library boundary. The only meaningful new code is: * The Mutagen.h interface and its implementation in Mutagen.cpp. * The following methods in MutagenDispatcher.cpp: * UseCmp * UseMemmem * SetCustomMutator * SetCustomCrossOver * LateInitialize (similar to the MutationDispatcher's original constructor) * Mutate_AddWordFromTORC (uses callbacks instead of accessing TPC directly) * StartMutationSequence * MutationSequence * DictionaryEntrySequence * RecommendDictionary * RecommendDictionaryEntry * FuzzerMutate.cpp (which now justs sets callbacks and handles printing) * MutagenUnittest.cpp (which adds tests of Mutagen.h) A note on performance: This change was tested with a 100 passes of test/fuzzer/LargeTest.cpp with 1000 runs per pass, both with and without the change. The running time distribution was qualitatively similar both with and without the change, and the average difference was within 30 microseconds (2.240 ms/run vs 2.212 ms/run, respectively). Both times were much higher than observed with the fully optimized system clang (~0.38 ms/run), most likely due to the combination of CMake "dev mode" settings (e.g. CMAKE_BUILD_TYPE="Debug", LLVM_ENABLE_LTO=OFF, etc.). The difference between the two versions built similarly seems to be "in the noise" and suggests no meaningful performance degradation. Reviewed By: morehouse Differential Revision: https://reviews.llvm.org/D102447
This commit is contained in:
parent
6505c63040
commit
c4a41cd77c
|
@ -1,5 +1,4 @@
|
|||
set(LIBFUZZER_SOURCES
|
||||
FuzzerCrossOver.cpp
|
||||
FuzzerDataFlowTrace.cpp
|
||||
FuzzerDriver.cpp
|
||||
FuzzerExtFunctionsDlsym.cpp
|
||||
|
@ -29,7 +28,6 @@ set(LIBFUZZER_HEADERS
|
|||
FuzzerCorpus.h
|
||||
FuzzerDataFlowTrace.h
|
||||
FuzzerDefs.h
|
||||
FuzzerDictionary.h
|
||||
FuzzerExtFunctions.def
|
||||
FuzzerExtFunctions.h
|
||||
FuzzerFlags.def
|
||||
|
@ -84,6 +82,32 @@ else()
|
|||
endif()
|
||||
endif()
|
||||
|
||||
macro(partially_link_libcxx name dir arch)
|
||||
if(${arch} MATCHES "i386")
|
||||
set(EMULATION_ARGUMENT "-m" "elf_i386")
|
||||
else()
|
||||
set(EMULATION_ARGUMENT "")
|
||||
endif()
|
||||
set(cxx_${arch}_merge_dir "${CMAKE_CURRENT_BINARY_DIR}/cxx_${arch}_merge.dir")
|
||||
file(MAKE_DIRECTORY ${cxx_${arch}_merge_dir})
|
||||
add_custom_command(TARGET clang_rt.${name}-${arch} POST_BUILD
|
||||
COMMAND ${CMAKE_LINKER} ${EMULATION_ARGUMENT} --whole-archive "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>" --no-whole-archive ${dir}/lib/libc++.a -r -o ${name}.o
|
||||
COMMAND ${CMAKE_OBJCOPY} --localize-hidden ${name}.o
|
||||
COMMAND ${CMAKE_COMMAND} -E remove "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>"
|
||||
COMMAND ${CMAKE_AR} qcs "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>" ${name}.o
|
||||
WORKING_DIRECTORY ${cxx_${arch}_merge_dir}
|
||||
)
|
||||
endmacro()
|
||||
|
||||
add_subdirectory(mutagen)
|
||||
foreach(X IN LISTS LIBFUZZER_MUTAGEN_SOURCES)
|
||||
list(APPEND LIBFUZZER_SOURCES "mutagen/${X}")
|
||||
endforeach()
|
||||
foreach(X IN LISTS LIBFUZZER_MUTAGEN_HEADERS)
|
||||
list(APPEND LIBFUZZER_HEADERS "mutagen/${X}")
|
||||
endforeach()
|
||||
include_directories(.)
|
||||
|
||||
add_compiler_rt_component(fuzzer)
|
||||
|
||||
add_compiler_rt_object_libraries(RTfuzzer
|
||||
|
@ -135,23 +159,6 @@ add_compiler_rt_runtime(clang_rt.fuzzer_interceptors
|
|||
if(OS_NAME MATCHES "Linux|Fuchsia" AND
|
||||
COMPILER_RT_LIBCXX_PATH AND
|
||||
COMPILER_RT_LIBCXXABI_PATH)
|
||||
macro(partially_link_libcxx name dir arch)
|
||||
if(${arch} MATCHES "i386")
|
||||
set(EMULATION_ARGUMENT "-m" "elf_i386")
|
||||
else()
|
||||
set(EMULATION_ARGUMENT "")
|
||||
endif()
|
||||
set(cxx_${arch}_merge_dir "${CMAKE_CURRENT_BINARY_DIR}/cxx_${arch}_merge.dir")
|
||||
file(MAKE_DIRECTORY ${cxx_${arch}_merge_dir})
|
||||
add_custom_command(TARGET clang_rt.${name}-${arch} POST_BUILD
|
||||
COMMAND ${CMAKE_LINKER} ${EMULATION_ARGUMENT} --whole-archive "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>" --no-whole-archive ${dir}/lib/libc++.a -r -o ${name}.o
|
||||
COMMAND ${CMAKE_OBJCOPY} --localize-hidden ${name}.o
|
||||
COMMAND ${CMAKE_COMMAND} -E remove "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>"
|
||||
COMMAND ${CMAKE_AR} qcs "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>" ${name}.o
|
||||
WORKING_DIRECTORY ${cxx_${arch}_merge_dir}
|
||||
)
|
||||
endmacro()
|
||||
|
||||
foreach(arch ${FUZZER_SUPPORTED_ARCH})
|
||||
get_target_flags_for_arch(${arch} TARGET_CFLAGS)
|
||||
set(LIBCXX_${arch}_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libcxx_fuzzer_${arch})
|
||||
|
|
|
@ -15,21 +15,18 @@
|
|||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
template <class T> T Min(T a, T b) { return a < b ? a : b; }
|
||||
template <class T> T Max(T a, T b) { return a > b ? a : b; }
|
||||
|
||||
class Random;
|
||||
class Dictionary;
|
||||
class DictionaryEntry;
|
||||
class MutationDispatcher;
|
||||
struct FuzzingOptions;
|
||||
class InputCorpus;
|
||||
struct InputInfo;
|
||||
|
@ -60,6 +57,37 @@ using Set = std::set<T, std::less<T>, fuzzer_allocator<T>>;
|
|||
|
||||
typedef Vector<uint8_t> Unit;
|
||||
typedef Vector<Unit> UnitVector;
|
||||
|
||||
// A simple POD sized array of bytes.
|
||||
template <size_t kMaxSizeT> class FixedWord {
|
||||
public:
|
||||
static const size_t kMaxSize = kMaxSizeT;
|
||||
FixedWord() { memset(Data, 0, kMaxSize); }
|
||||
FixedWord(const uint8_t *B, size_t S) { Set(B, S); }
|
||||
|
||||
void Set(const uint8_t *B, size_t S) {
|
||||
static_assert(kMaxSizeT <= std::numeric_limits<uint8_t>::max(),
|
||||
"FixedWord::kMaxSizeT cannot fit in a uint8_t.");
|
||||
assert(S <= kMaxSize);
|
||||
memcpy(Data, B, S);
|
||||
Size = static_cast<uint8_t>(S);
|
||||
}
|
||||
|
||||
bool operator==(const FixedWord<kMaxSize> &w) const {
|
||||
return Size == w.Size && 0 == memcmp(Data, w.Data, Size);
|
||||
}
|
||||
|
||||
static size_t GetMaxSize() { return kMaxSize; }
|
||||
const uint8_t *data() const { return Data; }
|
||||
uint8_t size() const { return Size; }
|
||||
|
||||
private:
|
||||
uint8_t Size = 0;
|
||||
uint8_t Data[kMaxSize];
|
||||
};
|
||||
|
||||
typedef FixedWord<64> Word;
|
||||
|
||||
typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
|
||||
|
||||
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// fuzzer::Dictionary
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_DICTIONARY_H
|
||||
#define LLVM_FUZZER_DICTIONARY_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerUtil.h"
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace fuzzer {
|
||||
// A simple POD sized array of bytes.
|
||||
template <size_t kMaxSizeT> class FixedWord {
|
||||
public:
|
||||
static const size_t kMaxSize = kMaxSizeT;
|
||||
FixedWord() {}
|
||||
FixedWord(const uint8_t *B, size_t S) { Set(B, S); }
|
||||
|
||||
void Set(const uint8_t *B, size_t S) {
|
||||
static_assert(kMaxSizeT <= std::numeric_limits<uint8_t>::max(),
|
||||
"FixedWord::kMaxSizeT cannot fit in a uint8_t.");
|
||||
assert(S <= kMaxSize);
|
||||
memcpy(Data, B, S);
|
||||
Size = static_cast<uint8_t>(S);
|
||||
}
|
||||
|
||||
bool operator==(const FixedWord<kMaxSize> &w) const {
|
||||
return Size == w.Size && 0 == memcmp(Data, w.Data, Size);
|
||||
}
|
||||
|
||||
static size_t GetMaxSize() { return kMaxSize; }
|
||||
const uint8_t *data() const { return Data; }
|
||||
uint8_t size() const { return Size; }
|
||||
|
||||
private:
|
||||
uint8_t Size = 0;
|
||||
uint8_t Data[kMaxSize];
|
||||
};
|
||||
|
||||
typedef FixedWord<64> Word;
|
||||
|
||||
class DictionaryEntry {
|
||||
public:
|
||||
DictionaryEntry() {}
|
||||
DictionaryEntry(Word W) : W(W) {}
|
||||
DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {}
|
||||
const Word &GetW() const { return W; }
|
||||
|
||||
bool HasPositionHint() const { return PositionHint != std::numeric_limits<size_t>::max(); }
|
||||
size_t GetPositionHint() const {
|
||||
assert(HasPositionHint());
|
||||
return PositionHint;
|
||||
}
|
||||
void IncUseCount() { UseCount++; }
|
||||
void IncSuccessCount() { SuccessCount++; }
|
||||
size_t GetUseCount() const { return UseCount; }
|
||||
size_t GetSuccessCount() const {return SuccessCount; }
|
||||
|
||||
void Print(const char *PrintAfter = "\n") {
|
||||
PrintASCII(W.data(), W.size());
|
||||
if (HasPositionHint())
|
||||
Printf("@%zd", GetPositionHint());
|
||||
Printf("%s", PrintAfter);
|
||||
}
|
||||
|
||||
private:
|
||||
Word W;
|
||||
size_t PositionHint = std::numeric_limits<size_t>::max();
|
||||
size_t UseCount = 0;
|
||||
size_t SuccessCount = 0;
|
||||
};
|
||||
|
||||
class Dictionary {
|
||||
public:
|
||||
static const size_t kMaxDictSize = 1 << 14;
|
||||
|
||||
bool ContainsWord(const Word &W) const {
|
||||
return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) {
|
||||
return DE.GetW() == W;
|
||||
});
|
||||
}
|
||||
const DictionaryEntry *begin() const { return &DE[0]; }
|
||||
const DictionaryEntry *end() const { return begin() + Size; }
|
||||
DictionaryEntry & operator[] (size_t Idx) {
|
||||
assert(Idx < Size);
|
||||
return DE[Idx];
|
||||
}
|
||||
void push_back(DictionaryEntry DE) {
|
||||
if (Size < kMaxDictSize)
|
||||
this->DE[Size++] = DE;
|
||||
}
|
||||
void clear() { Size = 0; }
|
||||
bool empty() const { return Size == 0; }
|
||||
size_t size() const { return Size; }
|
||||
|
||||
private:
|
||||
DictionaryEntry DE[kMaxDictSize];
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
// Parses one dictionary entry.
|
||||
// If successful, write the enty to Unit and returns true,
|
||||
// otherwise returns false.
|
||||
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U);
|
||||
// Parses the dictionary file, fills Units, returns true iff all lines
|
||||
// were parsed successfully.
|
||||
bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LLVM_FUZZER_DICTIONARY_H
|
|
@ -19,15 +19,16 @@
|
|||
#include "FuzzerPlatform.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include "mutagen/MutagenDispatcher.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <fstream>
|
||||
|
||||
// This function should be present in the libFuzzer so that the client
|
||||
// binary can test for its existence.
|
||||
|
@ -803,8 +804,9 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
|
|||
ReadCorpora(*Inputs, {}));
|
||||
}
|
||||
|
||||
Random Rand(Seed);
|
||||
auto *MD = new MutationDispatcher(Rand, Options);
|
||||
LLVMMutagenConfiguration Config;
|
||||
ConfigureMutagen(Seed, Options, &Config);
|
||||
auto *MD = new MutationDispatcher(&Config);
|
||||
auto *Corpus = new InputCorpus(Options.OutputCorpus, Entropic);
|
||||
auto *F = new Fuzzer(Callback, *Corpus, *MD, Options);
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "FuzzerOptions.h"
|
||||
#include "FuzzerSHA1.h"
|
||||
#include "FuzzerValueBitMap.h"
|
||||
#include "mutagen/MutagenDispatcher.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
@ -26,8 +27,12 @@
|
|||
#include <string.h>
|
||||
|
||||
namespace fuzzer {
|
||||
namespace {
|
||||
|
||||
using namespace std::chrono;
|
||||
using mutagen::MutationDispatcher;
|
||||
|
||||
} // namespace
|
||||
|
||||
class Fuzzer {
|
||||
public:
|
||||
|
|
|
@ -177,7 +177,7 @@ void Fuzzer::DumpCurrentUnit(const char *Prefix) {
|
|||
if (!CurrentUnitData)
|
||||
return; // Happens when running individual inputs.
|
||||
ScopedDisableMsanInterceptorChecks S;
|
||||
MD.PrintMutationSequence();
|
||||
PrintMutationSequence(MD);
|
||||
Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str());
|
||||
size_t UnitSize = CurrentUnitSize;
|
||||
if (UnitSize <= kMaxUnitSizeToPrint) {
|
||||
|
@ -539,8 +539,9 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
|
|||
TimeOfUnit, UniqFeatureSetTmp, DFT, II);
|
||||
WriteFeatureSetToFile(Options.FeaturesDir, Sha1ToString(NewII->Sha1),
|
||||
NewII->UniqFeatureSet);
|
||||
const auto &MS = MD.MutationSequence();
|
||||
WriteEdgeToMutationGraphFile(Options.MutationGraphFile, NewII, II,
|
||||
MD.MutationSequence());
|
||||
MS.GetString());
|
||||
return true;
|
||||
}
|
||||
if (II && FoundUniqFeaturesOfII &&
|
||||
|
@ -652,7 +653,7 @@ void Fuzzer::PrintStatusForNewUnit(const Unit &U, const char *Text) {
|
|||
PrintStats(Text, "");
|
||||
if (Options.Verbosity) {
|
||||
Printf(" L: %zd/%zd ", U.size(), Corpus.MaxInputSize());
|
||||
MD.PrintMutationSequence(Options.Verbosity >= 2);
|
||||
PrintMutationSequence(MD, Options.Verbosity >= 2);
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
|
@ -898,7 +899,7 @@ void Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) {
|
|||
}
|
||||
|
||||
PrintStats("DONE ", "\n");
|
||||
MD.PrintRecommendedDictionary();
|
||||
PrintRecommendedDictionary(MD);
|
||||
}
|
||||
|
||||
void Fuzzer::MinimizeCrashLoop(const Unit &U) {
|
||||
|
|
|
@ -1,497 +1,76 @@
|
|||
//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===//
|
||||
//===- FuzzerMutate.cpp - Mutation utilities -----------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Mutate a test input.
|
||||
// Mutate utilities.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerOptions.h"
|
||||
#include "FuzzerTracePC.h"
|
||||
#include "FuzzerUtil.h"
|
||||
|
||||
namespace fuzzer {
|
||||
namespace {
|
||||
|
||||
const size_t Dictionary::kMaxDictSize;
|
||||
static const size_t kMaxMutationsToPrint = 10;
|
||||
|
||||
static void PrintASCII(const Word &W, const char *PrintAfter) {
|
||||
PrintASCII(W.data(), W.size(), PrintAfter);
|
||||
void FromTORC4(size_t Idx, uint32_t *A, uint32_t *B) {
|
||||
const auto &X = TPC.TORC4.Get(Idx);
|
||||
*A = X.A;
|
||||
*B = X.B;
|
||||
}
|
||||
|
||||
MutationDispatcher::MutationDispatcher(Random &Rand,
|
||||
const FuzzingOptions &Options)
|
||||
: Rand(Rand), Options(Options) {
|
||||
DefaultMutators.insert(
|
||||
DefaultMutators.begin(),
|
||||
{
|
||||
{&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
|
||||
{&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
|
||||
{&MutationDispatcher::Mutate_InsertRepeatedBytes,
|
||||
"InsertRepeatedBytes"},
|
||||
{&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"},
|
||||
{&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"},
|
||||
{&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"},
|
||||
{&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"},
|
||||
{&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"},
|
||||
{&MutationDispatcher::Mutate_CopyPart, "CopyPart"},
|
||||
{&MutationDispatcher::Mutate_CrossOver, "CrossOver"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
|
||||
"ManualDict"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
|
||||
"PersAutoDict"},
|
||||
});
|
||||
if(Options.UseCmp)
|
||||
DefaultMutators.push_back(
|
||||
{&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
|
||||
|
||||
if (EF->LLVMFuzzerCustomMutator)
|
||||
Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
|
||||
else
|
||||
Mutators = DefaultMutators;
|
||||
|
||||
if (EF->LLVMFuzzerCustomCrossOver)
|
||||
Mutators.push_back(
|
||||
{&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
|
||||
void FromTORC8(size_t Idx, uint64_t *A, uint64_t *B) {
|
||||
const auto &X = TPC.TORC8.Get(Idx);
|
||||
*A = X.A;
|
||||
*B = X.B;
|
||||
}
|
||||
|
||||
static char RandCh(Random &Rand) {
|
||||
if (Rand.RandBool())
|
||||
return static_cast<char>(Rand(256));
|
||||
const char Special[] = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
|
||||
return Special[Rand(sizeof(Special) - 1)];
|
||||
void FromTORCW(size_t Idx, const uint8_t **DataA, size_t *SizeA,
|
||||
const uint8_t **DataB, size_t *SizeB) {
|
||||
const auto &X = TPC.TORCW.Get(Idx);
|
||||
*DataA = X.A.data();
|
||||
*SizeA = X.A.size();
|
||||
*DataB = X.B.data();
|
||||
*SizeB = X.B.size();
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (EF->__msan_unpoison)
|
||||
EF->__msan_unpoison(Data, Size);
|
||||
if (EF->__msan_unpoison_param)
|
||||
EF->__msan_unpoison_param(4);
|
||||
return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize,
|
||||
Rand.Rand<unsigned int>());
|
||||
void FromMMT(size_t Idx, const uint8_t **Data, size_t *Size) {
|
||||
auto W = TPC.MMT.Get(Idx);
|
||||
*Data = W.data();
|
||||
*Size = W.size();
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size == 0)
|
||||
return 0;
|
||||
if (!CrossOverWith) return 0;
|
||||
const Unit &Other = *CrossOverWith;
|
||||
if (Other.empty())
|
||||
return 0;
|
||||
CustomCrossOverInPlaceHere.resize(MaxSize);
|
||||
auto &U = CustomCrossOverInPlaceHere;
|
||||
|
||||
if (EF->__msan_unpoison) {
|
||||
EF->__msan_unpoison(Data, Size);
|
||||
EF->__msan_unpoison(Other.data(), Other.size());
|
||||
EF->__msan_unpoison(U.data(), U.size());
|
||||
}
|
||||
if (EF->__msan_unpoison_param)
|
||||
EF->__msan_unpoison_param(7);
|
||||
size_t NewSize = EF->LLVMFuzzerCustomCrossOver(
|
||||
Data, Size, Other.data(), Other.size(), U.data(), U.size(),
|
||||
Rand.Rand<unsigned int>());
|
||||
|
||||
if (!NewSize)
|
||||
return 0;
|
||||
assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit");
|
||||
memcpy(Data, U.data(), NewSize);
|
||||
return NewSize;
|
||||
void PrintASCII(const Word &W, const char *PrintAfter) {
|
||||
fuzzer::PrintASCII(W.data(), W.size(), PrintAfter);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize || Size == 0) return 0;
|
||||
size_t ShuffleAmount =
|
||||
Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size.
|
||||
size_t ShuffleStart = Rand(Size - ShuffleAmount);
|
||||
assert(ShuffleStart + ShuffleAmount <= Size);
|
||||
std::shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, Rand);
|
||||
return Size;
|
||||
} // namespace
|
||||
|
||||
void ConfigureMutagen(unsigned int Seed, const FuzzingOptions &Options,
|
||||
LLVMMutagenConfiguration *OutConfig) {
|
||||
OutConfig->Seed = Seed;
|
||||
OutConfig->UseCmp = Options.UseCmp;
|
||||
OutConfig->FromTORC4 = FromTORC4;
|
||||
OutConfig->FromTORC8 = FromTORC8;
|
||||
OutConfig->FromTORCW = FromTORCW;
|
||||
OutConfig->UseMemmem = Options.UseMemmem;
|
||||
OutConfig->FromMMT = FromMMT;
|
||||
OutConfig->CustomMutator = EF->LLVMFuzzerCustomMutator;
|
||||
OutConfig->CustomCrossOver = EF->LLVMFuzzerCustomCrossOver;
|
||||
OutConfig->MSanUnpoison = EF->__msan_unpoison;
|
||||
OutConfig->MSanUnpoisonParam = EF->__msan_unpoison_param;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size <= 1) return 0;
|
||||
size_t N = Rand(Size / 2) + 1;
|
||||
assert(N < Size);
|
||||
size_t Idx = Rand(Size - N + 1);
|
||||
// Erase Data[Idx:Idx+N].
|
||||
memmove(Data + Idx, Data + Idx + N, Size - Idx - N);
|
||||
// Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx);
|
||||
return Size - N;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size >= MaxSize) return 0;
|
||||
size_t Idx = Rand(Size + 1);
|
||||
// Insert new value at Data[Idx].
|
||||
memmove(Data + Idx + 1, Data + Idx, Size - Idx);
|
||||
Data[Idx] = RandCh(Rand);
|
||||
return Size + 1;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
const size_t kMinBytesToInsert = 3;
|
||||
if (Size + kMinBytesToInsert >= MaxSize) return 0;
|
||||
size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128);
|
||||
size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert;
|
||||
assert(Size + N <= MaxSize && N);
|
||||
size_t Idx = Rand(Size + 1);
|
||||
// Insert new values at Data[Idx].
|
||||
memmove(Data + Idx + N, Data + Idx, Size - Idx);
|
||||
// Give preference to 0x00 and 0xff.
|
||||
uint8_t Byte = static_cast<uint8_t>(
|
||||
Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255));
|
||||
for (size_t i = 0; i < N; i++)
|
||||
Data[Idx + i] = Byte;
|
||||
return Size + N;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
size_t Idx = Rand(Size);
|
||||
Data[Idx] = RandCh(Rand);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
size_t Idx = Rand(Size);
|
||||
Data[Idx] ^= 1 << Rand(8);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
DictionaryEntry &DE) {
|
||||
const Word &W = DE.GetW();
|
||||
bool UsePositionHint = DE.HasPositionHint() &&
|
||||
DE.GetPositionHint() + W.size() < Size &&
|
||||
Rand.RandBool();
|
||||
if (Rand.RandBool()) { // Insert W.
|
||||
if (Size + W.size() > MaxSize) return 0;
|
||||
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1);
|
||||
memmove(Data + Idx + W.size(), Data + Idx, Size - Idx);
|
||||
memcpy(Data + Idx, W.data(), W.size());
|
||||
Size += W.size();
|
||||
} else { // Overwrite some bytes with W.
|
||||
if (W.size() > Size) return 0;
|
||||
size_t Idx =
|
||||
UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1 - W.size());
|
||||
memcpy(Data + Idx, W.data(), W.size());
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
// Somewhere in the past we have observed a comparison instructions
|
||||
// with arguments Arg1 Arg2. This function tries to guess a dictionary
|
||||
// entry that will satisfy that comparison.
|
||||
// It first tries to find one of the arguments (possibly swapped) in the
|
||||
// input and if it succeeds it creates a DE with a position hint.
|
||||
// Otherwise it creates a DE with one of the arguments w/o a position hint.
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
const void *Arg1, const void *Arg2,
|
||||
const void *Arg1Mutation, const void *Arg2Mutation,
|
||||
size_t ArgSize, const uint8_t *Data,
|
||||
size_t Size) {
|
||||
bool HandleFirst = Rand.RandBool();
|
||||
const void *ExistingBytes, *DesiredBytes;
|
||||
Word W;
|
||||
const uint8_t *End = Data + Size;
|
||||
for (int Arg = 0; Arg < 2; Arg++) {
|
||||
ExistingBytes = HandleFirst ? Arg1 : Arg2;
|
||||
DesiredBytes = HandleFirst ? Arg2Mutation : Arg1Mutation;
|
||||
HandleFirst = !HandleFirst;
|
||||
W.Set(reinterpret_cast<const uint8_t*>(DesiredBytes), ArgSize);
|
||||
const size_t kMaxNumPositions = 8;
|
||||
size_t Positions[kMaxNumPositions];
|
||||
size_t NumPositions = 0;
|
||||
for (const uint8_t *Cur = Data;
|
||||
Cur < End && NumPositions < kMaxNumPositions; Cur++) {
|
||||
Cur =
|
||||
(const uint8_t *)SearchMemory(Cur, End - Cur, ExistingBytes, ArgSize);
|
||||
if (!Cur) break;
|
||||
Positions[NumPositions++] = Cur - Data;
|
||||
}
|
||||
if (!NumPositions) continue;
|
||||
return DictionaryEntry(W, Positions[Rand(NumPositions)]);
|
||||
}
|
||||
DictionaryEntry DE(W);
|
||||
return DE;
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
T Arg1, T Arg2, const uint8_t *Data, size_t Size) {
|
||||
if (Rand.RandBool()) Arg1 = Bswap(Arg1);
|
||||
if (Rand.RandBool()) Arg2 = Bswap(Arg2);
|
||||
T Arg1Mutation = static_cast<T>(Arg1 + Rand(-1, 1));
|
||||
T Arg2Mutation = static_cast<T>(Arg2 + Rand(-1, 1));
|
||||
return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation,
|
||||
sizeof(Arg1), Data, Size);
|
||||
}
|
||||
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
const Word &Arg1, const Word &Arg2, const uint8_t *Data, size_t Size) {
|
||||
return MakeDictionaryEntryFromCMP(Arg1.data(), Arg2.data(), Arg1.data(),
|
||||
Arg2.data(), Arg1.size(), Data, Size);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromTORC(
|
||||
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
Word W;
|
||||
DictionaryEntry DE;
|
||||
switch (Rand(4)) {
|
||||
case 0: {
|
||||
auto X = TPC.TORC8.Get(Rand.Rand<size_t>());
|
||||
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||
} break;
|
||||
case 1: {
|
||||
auto X = TPC.TORC4.Get(Rand.Rand<size_t>());
|
||||
if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool())
|
||||
DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data, Size);
|
||||
else
|
||||
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||
} break;
|
||||
case 2: {
|
||||
auto X = TPC.TORCW.Get(Rand.Rand<size_t>());
|
||||
DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
|
||||
} break;
|
||||
case 3: if (Options.UseMemmem) {
|
||||
auto X = TPC.MMT.Get(Rand.Rand<size_t>());
|
||||
DE = DictionaryEntry(X);
|
||||
} break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
if (!DE.GetW().size()) return 0;
|
||||
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
|
||||
if (!Size) return 0;
|
||||
DictionaryEntry &DERef =
|
||||
CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ %
|
||||
kCmpDictionaryEntriesDequeSize];
|
||||
DERef = DE;
|
||||
CurrentDictionaryEntrySequence.push_back(&DERef);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary(
|
||||
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data,
|
||||
size_t Size, size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
if (D.empty()) return 0;
|
||||
DictionaryEntry &DE = D[Rand(D.size())];
|
||||
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
|
||||
if (!Size) return 0;
|
||||
DE.IncUseCount();
|
||||
CurrentDictionaryEntrySequence.push_back(&DE);
|
||||
return Size;
|
||||
}
|
||||
|
||||
// Overwrites part of To[0,ToSize) with a part of From[0,FromSize).
|
||||
// Returns ToSize.
|
||||
size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize,
|
||||
uint8_t *To, size_t ToSize) {
|
||||
// Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize).
|
||||
size_t ToBeg = Rand(ToSize);
|
||||
size_t CopySize = Rand(ToSize - ToBeg) + 1;
|
||||
assert(ToBeg + CopySize <= ToSize);
|
||||
CopySize = std::min(CopySize, FromSize);
|
||||
size_t FromBeg = Rand(FromSize - CopySize + 1);
|
||||
assert(FromBeg + CopySize <= FromSize);
|
||||
memmove(To + ToBeg, From + FromBeg, CopySize);
|
||||
return ToSize;
|
||||
}
|
||||
|
||||
// Inserts part of From[0,ToSize) into To.
|
||||
// Returns new size of To on success or 0 on failure.
|
||||
size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize,
|
||||
uint8_t *To, size_t ToSize,
|
||||
size_t MaxToSize) {
|
||||
if (ToSize >= MaxToSize) return 0;
|
||||
size_t AvailableSpace = MaxToSize - ToSize;
|
||||
size_t MaxCopySize = std::min(AvailableSpace, FromSize);
|
||||
size_t CopySize = Rand(MaxCopySize) + 1;
|
||||
size_t FromBeg = Rand(FromSize - CopySize + 1);
|
||||
assert(FromBeg + CopySize <= FromSize);
|
||||
size_t ToInsertPos = Rand(ToSize + 1);
|
||||
assert(ToInsertPos + CopySize <= MaxToSize);
|
||||
size_t TailSize = ToSize - ToInsertPos;
|
||||
if (To == From) {
|
||||
MutateInPlaceHere.resize(MaxToSize);
|
||||
memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize);
|
||||
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
|
||||
memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize);
|
||||
} else {
|
||||
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
|
||||
memmove(To + ToInsertPos, From + FromBeg, CopySize);
|
||||
}
|
||||
return ToSize + CopySize;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize || Size == 0) return 0;
|
||||
// If Size == MaxSize, `InsertPartOf(...)` will
|
||||
// fail so there's no point using it in this case.
|
||||
if (Size == MaxSize || Rand.RandBool())
|
||||
return CopyPartOf(Data, Size, Data, Size);
|
||||
else
|
||||
return InsertPartOf(Data, Size, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
size_t B = Rand(Size);
|
||||
while (B < Size && !isdigit(Data[B])) B++;
|
||||
if (B == Size) return 0;
|
||||
size_t E = B;
|
||||
while (E < Size && isdigit(Data[E])) E++;
|
||||
assert(B < E);
|
||||
// now we have digits in [B, E).
|
||||
// strtol and friends don't accept non-zero-teminated data, parse it manually.
|
||||
uint64_t Val = Data[B] - '0';
|
||||
for (size_t i = B + 1; i < E; i++)
|
||||
Val = Val * 10 + Data[i] - '0';
|
||||
|
||||
// Mutate the integer value.
|
||||
switch(Rand(5)) {
|
||||
case 0: Val++; break;
|
||||
case 1: Val--; break;
|
||||
case 2: Val /= 2; break;
|
||||
case 3: Val *= 2; break;
|
||||
case 4: Val = Rand(Val * Val); break;
|
||||
default: assert(0);
|
||||
}
|
||||
// Just replace the bytes with the new ones, don't bother moving bytes.
|
||||
for (size_t i = B; i < E; i++) {
|
||||
size_t Idx = E + B - i - 1;
|
||||
assert(Idx >= B && Idx < E);
|
||||
Data[Idx] = (Val % 10) + '0';
|
||||
Val /= 10;
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) {
|
||||
if (Size < sizeof(T)) return 0;
|
||||
size_t Off = Rand(Size - sizeof(T) + 1);
|
||||
assert(Off + sizeof(T) <= Size);
|
||||
T Val;
|
||||
if (Off < 64 && !Rand(4)) {
|
||||
Val = static_cast<T>(Size);
|
||||
if (Rand.RandBool())
|
||||
Val = Bswap(Val);
|
||||
} else {
|
||||
memcpy(&Val, Data + Off, sizeof(Val));
|
||||
T Add = static_cast<T>(Rand(21));
|
||||
Add -= 10;
|
||||
if (Rand.RandBool())
|
||||
Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes.
|
||||
else
|
||||
Val = Val + Add; // Add assuming current endiannes.
|
||||
if (Add == 0 || Rand.RandBool()) // Maybe negate.
|
||||
Val = -Val;
|
||||
}
|
||||
memcpy(Data + Off, &Val, sizeof(Val));
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
switch (Rand(4)) {
|
||||
case 3: return ChangeBinaryInteger<uint64_t>(Data, Size, Rand);
|
||||
case 2: return ChangeBinaryInteger<uint32_t>(Data, Size, Rand);
|
||||
case 1: return ChangeBinaryInteger<uint16_t>(Data, Size, Rand);
|
||||
case 0: return ChangeBinaryInteger<uint8_t>(Data, Size, Rand);
|
||||
default: assert(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize) return 0;
|
||||
if (Size == 0) return 0;
|
||||
if (!CrossOverWith) return 0;
|
||||
const Unit &O = *CrossOverWith;
|
||||
if (O.empty()) return 0;
|
||||
size_t NewSize = 0;
|
||||
switch(Rand(3)) {
|
||||
case 0:
|
||||
MutateInPlaceHere.resize(MaxSize);
|
||||
NewSize = CrossOver(Data, Size, O.data(), O.size(),
|
||||
MutateInPlaceHere.data(), MaxSize);
|
||||
memcpy(Data, MutateInPlaceHere.data(), NewSize);
|
||||
break;
|
||||
case 1:
|
||||
NewSize = InsertPartOf(O.data(), O.size(), Data, Size, MaxSize);
|
||||
if (!NewSize)
|
||||
NewSize = CopyPartOf(O.data(), O.size(), Data, Size);
|
||||
break;
|
||||
case 2:
|
||||
NewSize = CopyPartOf(O.data(), O.size(), Data, Size);
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
assert(NewSize > 0 && "CrossOver returned empty unit");
|
||||
assert(NewSize <= MaxSize && "CrossOver returned overisized unit");
|
||||
return NewSize;
|
||||
}
|
||||
|
||||
void MutationDispatcher::StartMutationSequence() {
|
||||
CurrentMutatorSequence.clear();
|
||||
CurrentDictionaryEntrySequence.clear();
|
||||
}
|
||||
|
||||
// Copy successful dictionary entries to PersistentAutoDictionary.
|
||||
void MutationDispatcher::RecordSuccessfulMutationSequence() {
|
||||
for (auto DE : CurrentDictionaryEntrySequence) {
|
||||
// PersistentAutoDictionary.AddWithSuccessCountOne(DE);
|
||||
DE->IncSuccessCount();
|
||||
assert(DE->GetW().size());
|
||||
// Linear search is fine here as this happens seldom.
|
||||
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
|
||||
PersistentAutoDictionary.push_back(*DE);
|
||||
}
|
||||
}
|
||||
|
||||
void MutationDispatcher::PrintRecommendedDictionary() {
|
||||
Vector<DictionaryEntry> V;
|
||||
for (auto &DE : PersistentAutoDictionary)
|
||||
if (!ManualDictionary.ContainsWord(DE.GetW()))
|
||||
V.push_back(DE);
|
||||
if (V.empty()) return;
|
||||
void PrintRecommendedDictionary(MutationDispatcher &MD) {
|
||||
auto RecommendedDictionary = MD.RecommendDictionary();
|
||||
if (RecommendedDictionary.empty())
|
||||
return;
|
||||
Printf("###### Recommended dictionary. ######\n");
|
||||
for (auto &DE: V) {
|
||||
for (auto &DE : RecommendedDictionary) {
|
||||
assert(DE.GetW().size());
|
||||
Printf("\"");
|
||||
PrintASCII(DE.GetW(), "\"");
|
||||
|
@ -500,97 +79,12 @@ void MutationDispatcher::PrintRecommendedDictionary() {
|
|||
Printf("###### End of recommended dictionary. ######\n");
|
||||
}
|
||||
|
||||
void MutationDispatcher::PrintMutationSequence(bool Verbose) {
|
||||
Printf("MS: %zd ", CurrentMutatorSequence.size());
|
||||
size_t EntriesToPrint =
|
||||
Verbose ? CurrentMutatorSequence.size()
|
||||
: std::min(kMaxMutationsToPrint, CurrentMutatorSequence.size());
|
||||
for (size_t i = 0; i < EntriesToPrint; i++)
|
||||
Printf("%s-", CurrentMutatorSequence[i].Name);
|
||||
if (!CurrentDictionaryEntrySequence.empty()) {
|
||||
Printf(" DE: ");
|
||||
EntriesToPrint = Verbose ? CurrentDictionaryEntrySequence.size()
|
||||
: std::min(kMaxMutationsToPrint,
|
||||
CurrentDictionaryEntrySequence.size());
|
||||
for (size_t i = 0; i < EntriesToPrint; i++) {
|
||||
Printf("\"");
|
||||
PrintASCII(CurrentDictionaryEntrySequence[i]->GetW(), "\"-");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string MutationDispatcher::MutationSequence() {
|
||||
std::string MS;
|
||||
for (auto M : CurrentMutatorSequence) {
|
||||
MS += M.Name;
|
||||
MS += "-";
|
||||
}
|
||||
return MS;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
return MutateImpl(Data, Size, MaxSize, Mutators);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
return MutateImpl(Data, Size, MaxSize, DefaultMutators);
|
||||
}
|
||||
|
||||
// Mutates Data in place, returns new size.
|
||||
size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
Vector<Mutator> &Mutators) {
|
||||
assert(MaxSize > 0);
|
||||
// Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize),
|
||||
// in which case they will return 0.
|
||||
// Try several times before returning un-mutated data.
|
||||
for (int Iter = 0; Iter < 100; Iter++) {
|
||||
auto M = Mutators[Rand(Mutators.size())];
|
||||
size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize);
|
||||
if (NewSize && NewSize <= MaxSize) {
|
||||
if (Options.OnlyASCII)
|
||||
ToASCII(Data, NewSize);
|
||||
CurrentMutatorSequence.push_back(M);
|
||||
return NewSize;
|
||||
}
|
||||
}
|
||||
*Data = ' ';
|
||||
return 1; // Fallback, should not happen frequently.
|
||||
}
|
||||
|
||||
// Mask represents the set of Data bytes that are worth mutating.
|
||||
size_t MutationDispatcher::MutateWithMask(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
const Vector<uint8_t> &Mask) {
|
||||
size_t MaskedSize = std::min(Size, Mask.size());
|
||||
// * Copy the worthy bytes into a temporary array T
|
||||
// * Mutate T
|
||||
// * Copy T back.
|
||||
// This is totally unoptimized.
|
||||
auto &T = MutateWithMaskTemp;
|
||||
if (T.size() < Size)
|
||||
T.resize(Size);
|
||||
size_t OneBits = 0;
|
||||
for (size_t I = 0; I < MaskedSize; I++)
|
||||
if (Mask[I])
|
||||
T[OneBits++] = Data[I];
|
||||
|
||||
if (!OneBits) return 0;
|
||||
assert(!T.empty());
|
||||
size_t NewSize = Mutate(T.data(), OneBits, OneBits);
|
||||
assert(NewSize <= OneBits);
|
||||
(void)NewSize;
|
||||
// Even if NewSize < OneBits we still use all OneBits bytes.
|
||||
for (size_t I = 0, J = 0; I < MaskedSize; I++)
|
||||
if (Mask[I])
|
||||
Data[I] = T[J++];
|
||||
return Size;
|
||||
}
|
||||
|
||||
void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
|
||||
ManualDictionary.push_back(
|
||||
{W, std::numeric_limits<size_t>::max()});
|
||||
void PrintMutationSequence(MutationDispatcher &MD, bool Verbose) {
|
||||
const auto &MS = MD.MutationSequence();
|
||||
const auto &DS = MD.DictionaryEntrySequence();
|
||||
Printf("MS: %zd %s", MS.size(), MS.GetString(Verbose).c_str());
|
||||
if (!DS.empty())
|
||||
Printf(" DE: %s", DS.GetString(Verbose).c_str());
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
|
|
@ -11,145 +11,23 @@
|
|||
#ifndef LLVM_FUZZER_MUTATE_H
|
||||
#define LLVM_FUZZER_MUTATE_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerOptions.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "mutagen/Mutagen.h"
|
||||
#include "mutagen/MutagenDispatcher.h"
|
||||
|
||||
namespace fuzzer {
|
||||
namespace {
|
||||
|
||||
class MutationDispatcher {
|
||||
public:
|
||||
MutationDispatcher(Random &Rand, const FuzzingOptions &Options);
|
||||
~MutationDispatcher() {}
|
||||
/// Indicate that we are about to start a new sequence of mutations.
|
||||
void StartMutationSequence();
|
||||
/// Print the current sequence of mutations. Only prints the full sequence
|
||||
/// when Verbose is true.
|
||||
void PrintMutationSequence(bool Verbose = true);
|
||||
/// Return the current sequence of mutations.
|
||||
std::string MutationSequence();
|
||||
/// Indicate that the current sequence of mutations was successful.
|
||||
void RecordSuccessfulMutationSequence();
|
||||
/// Mutates data by invoking user-provided mutator.
|
||||
size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by invoking user-provided crossover.
|
||||
size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by shuffling bytes.
|
||||
size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by erasing bytes.
|
||||
size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by inserting a byte.
|
||||
size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by inserting several repeated bytes.
|
||||
size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by changing one byte.
|
||||
size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by changing one bit.
|
||||
size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by copying/inserting a part of data into a different place.
|
||||
size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
using mutagen::MutationDispatcher;
|
||||
|
||||
/// Mutates data by adding a word from the manual dictionary.
|
||||
size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
} // namespace
|
||||
|
||||
/// Mutates data by adding a word from the TORC.
|
||||
size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
void ConfigureMutagen(unsigned int Seed, const FuzzingOptions &Options,
|
||||
LLVMMutagenConfiguration *OutConfig);
|
||||
|
||||
/// Mutates data by adding a word from the persistent automatic dictionary.
|
||||
size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
void PrintRecommendedDictionary(MutationDispatcher &MD);
|
||||
|
||||
/// Tries to find an ASCII integer in Data, changes it to another ASCII int.
|
||||
size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways.
|
||||
size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// CrossOver Data with CrossOverWith.
|
||||
size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Applies one of the configured mutations.
|
||||
/// Returns the new size of data which could be up to MaxSize.
|
||||
size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Applies one of the configured mutations to the bytes of Data
|
||||
/// that have '1' in Mask.
|
||||
/// Mask.size() should be >= Size.
|
||||
size_t MutateWithMask(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
const Vector<uint8_t> &Mask);
|
||||
|
||||
/// Applies one of the default mutations. Provided as a service
|
||||
/// to mutation authors.
|
||||
size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Creates a cross-over of two pieces of Data, returns its size.
|
||||
size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2,
|
||||
size_t Size2, uint8_t *Out, size_t MaxOutSize);
|
||||
|
||||
void AddWordToManualDictionary(const Word &W);
|
||||
|
||||
void PrintRecommendedDictionary();
|
||||
|
||||
void SetCrossOverWith(const Unit *U) { CrossOverWith = U; }
|
||||
|
||||
Random &GetRand() { return Rand; }
|
||||
|
||||
private:
|
||||
struct Mutator {
|
||||
size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
|
||||
const char *Name;
|
||||
};
|
||||
|
||||
size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
Vector<Mutator> &Mutators);
|
||||
|
||||
size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
|
||||
size_t ToSize, size_t MaxToSize);
|
||||
size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
|
||||
size_t ToSize);
|
||||
size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
DictionaryEntry &DE);
|
||||
|
||||
template <class T>
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2,
|
||||
const uint8_t *Data, size_t Size);
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(const Word &Arg1, const Word &Arg2,
|
||||
const uint8_t *Data, size_t Size);
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(const void *Arg1, const void *Arg2,
|
||||
const void *Arg1Mutation,
|
||||
const void *Arg2Mutation,
|
||||
size_t ArgSize,
|
||||
const uint8_t *Data, size_t Size);
|
||||
|
||||
Random &Rand;
|
||||
const FuzzingOptions Options;
|
||||
|
||||
// Dictionary provided by the user via -dict=DICT_FILE.
|
||||
Dictionary ManualDictionary;
|
||||
// Persistent dictionary modified by the fuzzer, consists of
|
||||
// entries that led to successful discoveries in the past mutations.
|
||||
Dictionary PersistentAutoDictionary;
|
||||
|
||||
Vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
|
||||
|
||||
static const size_t kCmpDictionaryEntriesDequeSize = 16;
|
||||
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
|
||||
size_t CmpDictionaryEntriesDequeIdx = 0;
|
||||
|
||||
const Unit *CrossOverWith = nullptr;
|
||||
Vector<uint8_t> MutateInPlaceHere;
|
||||
Vector<uint8_t> MutateWithMaskTemp;
|
||||
// CustomCrossOver needs its own buffer as a custom implementation may call
|
||||
// LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere.
|
||||
Vector<uint8_t> CustomCrossOverInPlaceHere;
|
||||
|
||||
Vector<Mutator> Mutators;
|
||||
Vector<Mutator> DefaultMutators;
|
||||
Vector<Mutator> CurrentMutatorSequence;
|
||||
};
|
||||
void PrintMutationSequence(MutationDispatcher &MD, bool Verbose = true);
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#ifndef LLVM_FUZZER_RANDOM_H
|
||||
#define LLVM_FUZZER_RANDOM_H
|
||||
|
||||
#include <cassert>
|
||||
#include <random>
|
||||
|
||||
namespace fuzzer {
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include "FuzzerBuiltinsMsvc.h"
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "FuzzerPlatform.h"
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#define LLVM_FUZZER_TRACE_PC
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerUtil.h"
|
||||
#include "FuzzerValueBitMap.h"
|
||||
|
||||
#include <set>
|
||||
|
@ -40,7 +40,7 @@ struct TableOfRecentCompares {
|
|||
Table[Idx].B = Arg2;
|
||||
}
|
||||
|
||||
Pair Get(size_t I) { return Table[I % kSize]; }
|
||||
const Pair &Get(size_t I) { return Table[I % kSize]; }
|
||||
|
||||
Pair Table[kSize];
|
||||
};
|
||||
|
|
|
@ -47,6 +47,15 @@ void PrintMemoryProfile();
|
|||
|
||||
unsigned NumberOfCpuCores();
|
||||
|
||||
// Parses one dictionary entry.
|
||||
// If successful, write the enty to Unit and returns true,
|
||||
// otherwise returns false.
|
||||
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U);
|
||||
|
||||
// Parses the dictionary file, fills Units, returns true iff all lines
|
||||
// were parsed successfully.
|
||||
bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units);
|
||||
|
||||
// Platform specific functions.
|
||||
void SetSignalHandler(const FuzzingOptions& Options);
|
||||
|
||||
|
@ -63,9 +72,6 @@ bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput);
|
|||
FILE *OpenProcessPipe(const char *Command, const char *Mode);
|
||||
int CloseProcessPipe(FILE *F);
|
||||
|
||||
const void *SearchMemory(const void *haystack, size_t haystacklen,
|
||||
const void *needle, size_t needlelen);
|
||||
|
||||
std::string CloneArgsWithoutX(const Vector<std::string> &Args,
|
||||
const char *X1, const char *X2);
|
||||
|
||||
|
|
|
@ -528,11 +528,6 @@ bool ExecuteCommand(const Command &BaseCmd, std::string *CmdOutput) {
|
|||
return Ret == 0;
|
||||
}
|
||||
|
||||
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||
size_t PattLen) {
|
||||
return memmem(Data, DataLen, Patt, PattLen);
|
||||
}
|
||||
|
||||
// In fuchsia, accessing /dev/null is not supported. There's nothing
|
||||
// similar to a file that discards everything that is written to it.
|
||||
// The way of doing something similar in fuchsia is by using
|
||||
|
|
|
@ -170,11 +170,6 @@ int CloseProcessPipe(FILE *F) {
|
|||
return pclose(F);
|
||||
}
|
||||
|
||||
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||
size_t PattLen) {
|
||||
return memmem(Data, DataLen, Patt, PattLen);
|
||||
}
|
||||
|
||||
std::string DisassembleCmd(const std::string &FileName) {
|
||||
return "objdump -d " + FileName;
|
||||
}
|
||||
|
|
|
@ -182,27 +182,6 @@ bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) {
|
|||
return _pclose(Pipe) == 0;
|
||||
}
|
||||
|
||||
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||
size_t PattLen) {
|
||||
// TODO: make this implementation more efficient.
|
||||
const char *Cdata = (const char *)Data;
|
||||
const char *Cpatt = (const char *)Patt;
|
||||
|
||||
if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen)
|
||||
return NULL;
|
||||
|
||||
if (PattLen == 1)
|
||||
return memchr(Data, *Cpatt, DataLen);
|
||||
|
||||
const char *End = Cdata + DataLen - PattLen + 1;
|
||||
|
||||
for (const char *It = Cdata; It < End; ++It)
|
||||
if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0)
|
||||
return It;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string DisassembleCmd(const std::string &FileName) {
|
||||
Vector<std::string> command_vector;
|
||||
command_vector.push_back("dumpbin /summary > nul");
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#!/bin/sh
|
||||
LIBFUZZER_SRC_DIR=$(dirname $0)
|
||||
LIBMUTAGEN_SRC_DIR=$LIBFUZZER_SRC_DIR/mutagen
|
||||
CXX="${CXX:-clang}"
|
||||
for f in $LIBFUZZER_SRC_DIR/*.cpp; do
|
||||
$CXX -g -O2 -fno-omit-frame-pointer -std=c++11 $f -c &
|
||||
for f in $LIBFUZZER_SRC_DIR/*.cpp $LIBMUTAGEN_SRC_DIR/*.cpp; do
|
||||
$CXX -O2 -fno-omit-frame-pointer -std=c++17 $f -c -I$LIBFUZZER_SRC_DIR &
|
||||
done
|
||||
wait
|
||||
rm -f libFuzzer.a
|
||||
ar ru libFuzzer.a Fuzzer*.o
|
||||
rm -f Fuzzer*.o
|
||||
|
||||
ar ru libFuzzer.a Fuzzer*.o Mutagen*.o
|
||||
rm -f Fuzzer*.o Mutagen*.o
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
set(MUTAGEN_SOURCES
|
||||
Mutagen.cpp
|
||||
MutagenCrossOver.cpp
|
||||
MutagenDispatcher.cpp
|
||||
MutagenUtilPosix.cpp
|
||||
MutagenUtilWindows.cpp)
|
||||
|
||||
set(MUTAGEN_HEADERS
|
||||
Mutagen.h
|
||||
MutagenDictionary.h
|
||||
MutagenDispatcher.h
|
||||
MutagenUtil.h)
|
||||
|
||||
# Expose the files in this library to libFuzzer for optimized, direct inclusion.
|
||||
set(LIBFUZZER_MUTAGEN_SOURCES ${MUTAGEN_SOURCES} PARENT_SCOPE)
|
||||
set(LIBFUZZER_MUTAGEN_HEADERS ${MUTAGEN_HEADERS} PARENT_SCOPE)
|
||||
|
||||
# Reuse the following variables from libFuzzer:
|
||||
# FUZZER_SUPPORTED_ARCH
|
||||
# FUZZER_SUPPORTED_OS
|
||||
# LIBFUZZER_CFLAGS
|
||||
# LIBFUZZER_DEPS
|
||||
include_directories(..)
|
||||
|
||||
add_compiler_rt_component(mutagen)
|
||||
|
||||
add_compiler_rt_object_libraries(RTmutagen
|
||||
OS ${FUZZER_SUPPORTED_OS}
|
||||
ARCHS ${FUZZER_SUPPORTED_ARCH}
|
||||
SOURCES ${MUTAGEN_SOURCES}
|
||||
ADDITIONAL_HEADERS ${MUTAGEN_HEADERS}
|
||||
CFLAGS ${LIBFUZZER_CFLAGS}
|
||||
DEPS ${LIBFUZZER_DEPS})
|
||||
|
||||
add_compiler_rt_runtime(clang_rt.mutagen
|
||||
STATIC
|
||||
OS ${FUZZER_SUPPORTED_OS}
|
||||
ARCHS ${FUZZER_SUPPORTED_ARCH}
|
||||
OBJECT_LIBS RTmutagen
|
||||
CFLAGS ${LIBFUZZER_CFLAGS}
|
||||
PARENT_TARGET mutagen)
|
||||
|
||||
if(OS_NAME MATCHES "Linux|Fuchsia" AND
|
||||
COMPILER_RT_LIBCXX_PATH AND
|
||||
COMPILER_RT_LIBCXXABI_PATH)
|
||||
foreach(arch ${FUZZER_SUPPORTED_ARCH})
|
||||
get_target_flags_for_arch(${arch} TARGET_CFLAGS)
|
||||
set(LIBCXX_${arch}_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libcxx_mutagen_${arch})
|
||||
add_custom_libcxx(libcxx_mutagen_${arch} ${LIBCXX_${arch}_PREFIX}
|
||||
CFLAGS ${TARGET_CFLAGS}
|
||||
CMAKE_ARGS -DCMAKE_CXX_COMPILER_WORKS=ON
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
-DLIBCXXABI_ENABLE_EXCEPTIONS=OFF
|
||||
-DLIBCXX_ABI_NAMESPACE=__Fuzzer)
|
||||
target_compile_options(RTmutagen.${arch} PRIVATE -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1)
|
||||
add_dependencies(RTmutagen.${arch} libcxx_mutagen_${arch}-build)
|
||||
partially_link_libcxx(mutagen ${LIBCXX_${arch}_PREFIX} ${arch})
|
||||
endforeach()
|
||||
endif()
|
|
@ -0,0 +1,100 @@
|
|||
//===- Mutagen.cpp - Interface header for the mutagen -----------*- C++ -* ===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Define the interface between libMutagen and its consumers.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Mutagen.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "MutagenDispatcher.h"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
namespace mutagen {
|
||||
namespace {
|
||||
|
||||
MutationDispatcher *MD = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
MutationDispatcher *GetMutationDispatcherForTest() { return MD; }
|
||||
|
||||
} // namespace mutagen
|
||||
|
||||
using fuzzer::Unit;
|
||||
using mutagen::MD;
|
||||
using mutagen::MutationDispatcher;
|
||||
using mutagen::Word;
|
||||
|
||||
extern "C" {
|
||||
|
||||
ATTRIBUTE_INTERFACE void
|
||||
LLVMMutagenConfigure(const LLVMMutagenConfiguration *Config) {
|
||||
if (MD)
|
||||
delete MD;
|
||||
MD = new MutationDispatcher(Config);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE void LLVMMutagenResetSequence() {
|
||||
MD->StartMutationSequence();
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE void LLVMMutagenSetCrossOverWith(const uint8_t *Data,
|
||||
size_t Size) {
|
||||
static Unit CrossOverWith;
|
||||
Unit U(Data, Data + Size);
|
||||
CrossOverWith = std::move(U);
|
||||
MD->SetCrossOverWith(&CrossOverWith);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE size_t LLVMMutagenMutate(uint8_t *Data, size_t Size,
|
||||
size_t Max) {
|
||||
return MD->Mutate(Data, Size, Max);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE size_t LLVMMutagenDefaultMutate(uint8_t *Data, size_t Size,
|
||||
size_t Max) {
|
||||
return MD->DefaultMutate(Data, Size, Max);
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE void LLVMMutagenRecordSequence() {
|
||||
MD->RecordSuccessfulMutationSequence();
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE size_t LLVMMutagenGetMutationSequence(int Verbose,
|
||||
char *Out, size_t Max,
|
||||
size_t *OutNumItems) {
|
||||
const auto &Seq = MD->MutationSequence();
|
||||
if (OutNumItems)
|
||||
*OutNumItems = Seq.size();
|
||||
return snprintf(Out, Max, "%s", Seq.GetString(Verbose).c_str());
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE void LLVMMutagenAddWordToDictionary(const uint8_t *Data,
|
||||
size_t Size) {
|
||||
MD->AddWordToManualDictionary(Word(Data, std::min(Size, Word::GetMaxSize())));
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE size_t LLVMMutagenGetDictionaryEntrySequence(
|
||||
int Verbose, char *Out, size_t Max, size_t *OutNumItems) {
|
||||
const auto &Seq = MD->DictionaryEntrySequence();
|
||||
if (OutNumItems)
|
||||
*OutNumItems = Seq.size();
|
||||
return snprintf(Out, Max, "%s", Seq.GetString(Verbose).c_str());
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE size_t LLVMMutagenRecommendDictionary() {
|
||||
return MD->RecommendDictionary().size();
|
||||
}
|
||||
|
||||
ATTRIBUTE_INTERFACE const char *
|
||||
LLVMMutagenRecommendDictionaryEntry(size_t *OutUseCount) {
|
||||
return MD->RecommendDictionaryEntry(OutUseCount);
|
||||
}
|
||||
|
||||
} // extern "C"
|
|
@ -0,0 +1,119 @@
|
|||
//===- Mutagen.h - Interface header for the mutagen -------------*- C++ -* ===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Define the interface between libMutagen and its consumers.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_MUTAGEN_H
|
||||
#define LLVM_FUZZER_MUTAGEN_H
|
||||
|
||||
#include "FuzzerPlatform.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#define MAX_WORD_SIZE 64
|
||||
|
||||
typedef struct {
|
||||
// PRNG seed.
|
||||
unsigned int Seed;
|
||||
|
||||
// If non-zero, use CMP traces to guide mutations. Ignored if any of
|
||||
// |FromTORC4|, |FromTORC8|, or |FromTORCW| are null.
|
||||
int UseCmp;
|
||||
void (*FromTORC4)(size_t Idx, uint32_t *Arg1, uint32_t *Arg2);
|
||||
void (*FromTORC8)(size_t Idx, uint64_t *Arg1, uint64_t *Arg2);
|
||||
void (*FromTORCW)(size_t Idx, const uint8_t **Data1, size_t *Size1,
|
||||
const uint8_t **Data2, size_t *Size2);
|
||||
|
||||
// If non-zero, use hints from intercepting memmem, strstr, etc. Ignored if
|
||||
// |UseCmp| is zero or if |FromMMT| is null.
|
||||
int UseMemmem;
|
||||
void (*FromMMT)(size_t Idx, const uint8_t **Data, size_t *Size);
|
||||
|
||||
// If non-zero, generate only ASCII (isprint+isspace) inputs.
|
||||
int OnlyASCII;
|
||||
|
||||
// Optional user-provided custom mutator.
|
||||
size_t (*CustomMutator)(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
unsigned int Seed);
|
||||
|
||||
// Optional user-provided custom cross-over function.
|
||||
size_t (*CustomCrossOver)(const uint8_t *Data1, size_t Size1,
|
||||
const uint8_t *Data2, size_t Size2, uint8_t *Out,
|
||||
size_t MaxOutSize, unsigned int Seed);
|
||||
|
||||
// Optional MemorySanitizer callbacks.
|
||||
void (*MSanUnpoison)(const volatile void *, size_t size);
|
||||
void (*MSanUnpoisonParam)(size_t n);
|
||||
} LLVMMutagenConfiguration;
|
||||
|
||||
// Re-seeds the PRNG and sets mutator-related options.
|
||||
ATTRIBUTE_INTERFACE void
|
||||
LLVMMutagenConfigure(const LLVMMutagenConfiguration *config);
|
||||
|
||||
// Writes the mutation sequence to |Out|, and returns the number of
|
||||
// characters it wrote, or would have written given a large enough buffer,
|
||||
// excluding the null terminator. Thus, a return value of |Max| or greater
|
||||
// indicates the sequence was truncated (like snprintf). May truncate the
|
||||
// sequence unless |Verbose| is non-zero. Sets |OutNumItems| to the number of
|
||||
// items in the untruncated sequence.
|
||||
ATTRIBUTE_INTERFACE size_t LLVMMutagenGetMutationSequence(int Verbose,
|
||||
char *Out, size_t Max,
|
||||
size_t *OutNumItems);
|
||||
|
||||
// Writes the dictionary entry sequence to |Out|, and returns the number of
|
||||
// characters it wrote, or would have written given a large enough buffer,
|
||||
// excluding a null terminator. Thus, a return value of |Max| or greater
|
||||
// indicates the sequence was truncated (like snprintf). May truncate the
|
||||
// sequence unless |Verbose| is non-zero. Sets |OutNumItems| to the number of
|
||||
// items in the untruncated sequence.
|
||||
ATTRIBUTE_INTERFACE size_t LLVMMutagenGetDictionaryEntrySequence(
|
||||
int Verbose, char *Out, size_t Max, size_t *OutNumItems);
|
||||
|
||||
// Instructs the library to record the current mutation sequence as successful
|
||||
// at increasing coverage.
|
||||
ATTRIBUTE_INTERFACE void LLVMMutagenRecordSequence();
|
||||
|
||||
// Clears the mutation and dictionary entry sequences.
|
||||
ATTRIBUTE_INTERFACE void LLVMMutagenResetSequence();
|
||||
|
||||
// Adds data used by various mutators to produce new inputs.
|
||||
ATTRIBUTE_INTERFACE void LLVMMutagenSetCrossOverWith(const uint8_t *Data,
|
||||
size_t Size);
|
||||
ATTRIBUTE_INTERFACE void LLVMMutagenAddWordToDictionary(const uint8_t *Word,
|
||||
size_t Size);
|
||||
|
||||
// Mutates the contents of |Data| and returns the new size.
|
||||
ATTRIBUTE_INTERFACE size_t LLVMMutagenMutate(uint8_t *Data, size_t Size,
|
||||
size_t Max);
|
||||
|
||||
// Like |LLVMMutagenMutate|, but never selects the custom mutators and is
|
||||
// therefore suitable to be called from them.
|
||||
ATTRIBUTE_INTERFACE size_t LLVMMutagenDefaultMutate(uint8_t *Data, size_t Size,
|
||||
size_t Max);
|
||||
|
||||
// Creates a recommended dictionary and returns its number of entries. The
|
||||
// entries can be retrieved by subsequent calls to
|
||||
// |LLVMMutagenRecommendDictionaryEntry|.
|
||||
ATTRIBUTE_INTERFACE size_t LLVMMutagenRecommendDictionary();
|
||||
|
||||
// Returns the ASCII representation of the next recommended dictionary entry,
|
||||
// or null if no entries remain (or |LLVMMutagenRecommendDictionary| wasn't
|
||||
// called). If non-null, the return pointer is valid until the next call to this
|
||||
// method, and if provided, |OutUseCount| is set to the entry's use count.
|
||||
ATTRIBUTE_INTERFACE const char *
|
||||
LLVMMutagenRecommendDictionaryEntry(size_t *OutUseCount);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // LLVM_FUZZER_MUTAGEN_H
|
|
@ -1,4 +1,4 @@
|
|||
//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===//
|
||||
//===- MutagenCrossOver.cpp - Cross over two test inputs ------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
|
@ -8,12 +8,11 @@
|
|||
// Cross over test inputs.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerMutate.h"
|
||||
#include "FuzzerRandom.h"
|
||||
#include "MutagenDispatcher.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace fuzzer {
|
||||
namespace mutagen {
|
||||
|
||||
// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out.
|
||||
size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1,
|
||||
|
@ -40,12 +39,12 @@ size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1,
|
|||
(*InPos) += ExtraSize;
|
||||
}
|
||||
// Use the other input data on the next iteration.
|
||||
InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1;
|
||||
InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1;
|
||||
InSize = CurrentlyUsingFirstData ? Size2 : Size1;
|
||||
Data = CurrentlyUsingFirstData ? Data2 : Data1;
|
||||
Data = CurrentlyUsingFirstData ? Data2 : Data1;
|
||||
CurrentlyUsingFirstData = !CurrentlyUsingFirstData;
|
||||
}
|
||||
return OutPos;
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
} // namespace mutagen
|
|
@ -0,0 +1,85 @@
|
|||
//===- MutagenDictionary.h - Internal header for the mutagen ----*- C++ -* ===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// mutagen::Dictionary
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_MUTAGEN_DICTIONARY_H
|
||||
#define LLVM_FUZZER_MUTAGEN_DICTIONARY_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
namespace mutagen {
|
||||
namespace {
|
||||
|
||||
using fuzzer::Word;
|
||||
|
||||
} // namespace
|
||||
|
||||
class DictionaryEntry {
|
||||
public:
|
||||
DictionaryEntry() {}
|
||||
DictionaryEntry(Word W) : W(W) {}
|
||||
DictionaryEntry(Word W, size_t PositionHint)
|
||||
: W(W), PositionHint(PositionHint) {}
|
||||
const Word &GetW() const { return W; }
|
||||
|
||||
bool HasPositionHint() const {
|
||||
return PositionHint != std::numeric_limits<size_t>::max();
|
||||
}
|
||||
size_t GetPositionHint() const {
|
||||
assert(HasPositionHint());
|
||||
return PositionHint;
|
||||
}
|
||||
void IncUseCount() { UseCount++; }
|
||||
void IncSuccessCount() { SuccessCount++; }
|
||||
size_t GetUseCount() const { return UseCount; }
|
||||
size_t GetSuccessCount() const { return SuccessCount; }
|
||||
|
||||
private:
|
||||
Word W;
|
||||
size_t PositionHint = std::numeric_limits<size_t>::max();
|
||||
size_t UseCount = 0;
|
||||
size_t SuccessCount = 0;
|
||||
};
|
||||
|
||||
class Dictionary {
|
||||
public:
|
||||
static const size_t kMaxDictSize = 1 << 14;
|
||||
|
||||
bool ContainsWord(const Word &W) const {
|
||||
return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) {
|
||||
return DE.GetW() == W;
|
||||
});
|
||||
}
|
||||
const DictionaryEntry *begin() const { return &DE[0]; }
|
||||
const DictionaryEntry *end() const { return begin() + Size; }
|
||||
DictionaryEntry &operator[](size_t Idx) {
|
||||
assert(Idx < Size);
|
||||
return DE[Idx];
|
||||
}
|
||||
void push_back(DictionaryEntry DE) {
|
||||
if (Size < kMaxDictSize)
|
||||
this->DE[Size++] = DE;
|
||||
}
|
||||
void clear() { Size = 0; }
|
||||
bool empty() const { return Size == 0; }
|
||||
size_t size() const { return Size; }
|
||||
|
||||
private:
|
||||
DictionaryEntry DE[kMaxDictSize];
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
} // namespace mutagen
|
||||
|
||||
#endif // LLVM_FUZZER_MUTAGEN_DICTIONARY_H
|
|
@ -0,0 +1,658 @@
|
|||
//===- MutagenDispatcher.cpp - Mutate a test input ------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Mutate a test input.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MutagenDispatcher.h"
|
||||
#include "FuzzerBuiltins.h"
|
||||
#include "FuzzerPlatform.h"
|
||||
#include "MutagenUtil.h"
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
namespace mutagen {
|
||||
namespace {
|
||||
|
||||
using fuzzer::Bswap;
|
||||
|
||||
std::string ToASCII(const uint8_t *Data, size_t Size) {
|
||||
std::ostringstream OSS;
|
||||
for (size_t i = 0; i < Size; i++) {
|
||||
uint16_t Byte = Data[i];
|
||||
if (Byte == '\\')
|
||||
OSS << "\\\\";
|
||||
else if (Byte == '"')
|
||||
OSS << "\\\"";
|
||||
else if (Byte >= 32 && Byte < 127)
|
||||
OSS << static_cast<char>(Byte);
|
||||
else
|
||||
OSS << "\\x" << std::hex << std::setw(2) << std::setfill('0') << Byte
|
||||
<< std::dec;
|
||||
}
|
||||
return OSS.str();
|
||||
}
|
||||
|
||||
std::string ToASCII(const Word &W) { return ToASCII(W.data(), W.size()); }
|
||||
|
||||
} // namespace
|
||||
|
||||
void MutationDispatcher::SetConfig(const LLVMMutagenConfiguration *C) {
|
||||
memcpy(&Config, C, sizeof(Config));
|
||||
if (!Config.FromTORC4 || !Config.FromTORC8 || !Config.FromTORCW)
|
||||
Config.UseCmp = 0;
|
||||
if (!Config.FromMMT)
|
||||
Config.UseMemmem = 0;
|
||||
}
|
||||
|
||||
MutationDispatcher::MutationDispatcher(const LLVMMutagenConfiguration *config)
|
||||
: Rand(config->Seed) {
|
||||
SetConfig(config);
|
||||
DefaultMutators.insert(
|
||||
DefaultMutators.begin(),
|
||||
{
|
||||
{&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
|
||||
{&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
|
||||
{&MutationDispatcher::Mutate_InsertRepeatedBytes,
|
||||
"InsertRepeatedBytes"},
|
||||
{&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"},
|
||||
{&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"},
|
||||
{&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"},
|
||||
{&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"},
|
||||
{&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"},
|
||||
{&MutationDispatcher::Mutate_CopyPart, "CopyPart"},
|
||||
{&MutationDispatcher::Mutate_CrossOver, "CrossOver"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
|
||||
"ManualDict"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
|
||||
"PersAutoDict"},
|
||||
});
|
||||
if (Config.UseCmp)
|
||||
DefaultMutators.push_back(
|
||||
{&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
|
||||
|
||||
if (Config.CustomMutator)
|
||||
Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
|
||||
else
|
||||
Mutators = DefaultMutators;
|
||||
|
||||
if (Config.CustomCrossOver)
|
||||
Mutators.push_back(
|
||||
{&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
|
||||
}
|
||||
|
||||
static char RandCh(Random &Rand) {
|
||||
if (Rand.RandBool())
|
||||
return static_cast<char>(Rand(256));
|
||||
const char Special[] = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
|
||||
return Special[Rand(sizeof(Special) - 1)];
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Config.MSanUnpoison)
|
||||
Config.MSanUnpoison(Data, Size);
|
||||
if (Config.MSanUnpoisonParam)
|
||||
Config.MSanUnpoisonParam(4);
|
||||
return Config.CustomMutator(Data, Size, MaxSize, Rand.Rand<unsigned int>());
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size == 0)
|
||||
return 0;
|
||||
if (!CrossOverWith)
|
||||
return 0;
|
||||
const Unit &Other = *CrossOverWith;
|
||||
if (Other.empty())
|
||||
return 0;
|
||||
CustomCrossOverInPlaceHere.resize(MaxSize);
|
||||
auto &U = CustomCrossOverInPlaceHere;
|
||||
|
||||
if (Config.MSanUnpoison) {
|
||||
Config.MSanUnpoison(Data, Size);
|
||||
Config.MSanUnpoison(Other.data(), Other.size());
|
||||
Config.MSanUnpoison(U.data(), U.size());
|
||||
}
|
||||
if (Config.MSanUnpoisonParam)
|
||||
Config.MSanUnpoisonParam(7);
|
||||
size_t NewSize =
|
||||
Config.CustomCrossOver(Data, Size, Other.data(), Other.size(), U.data(),
|
||||
U.size(), Rand.Rand<unsigned int>());
|
||||
|
||||
if (!NewSize)
|
||||
return 0;
|
||||
assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit");
|
||||
memcpy(Data, U.data(), NewSize);
|
||||
return NewSize;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize || Size == 0)
|
||||
return 0;
|
||||
size_t ShuffleAmount =
|
||||
Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size.
|
||||
size_t ShuffleStart = Rand(Size - ShuffleAmount);
|
||||
assert(ShuffleStart + ShuffleAmount <= Size);
|
||||
std::shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, Rand);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size <= 1)
|
||||
return 0;
|
||||
size_t N = Rand(Size / 2) + 1;
|
||||
assert(N < Size);
|
||||
size_t Idx = Rand(Size - N + 1);
|
||||
// Erase Data[Idx:Idx+N].
|
||||
memmove(Data + Idx, Data + Idx + N, Size - Idx - N);
|
||||
// Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx);
|
||||
return Size - N;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size >= MaxSize)
|
||||
return 0;
|
||||
size_t Idx = Rand(Size + 1);
|
||||
// Insert new value at Data[Idx].
|
||||
memmove(Data + Idx + 1, Data + Idx, Size - Idx);
|
||||
Data[Idx] = RandCh(Rand);
|
||||
return Size + 1;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
const size_t kMinBytesToInsert = 3;
|
||||
if (Size + kMinBytesToInsert >= MaxSize)
|
||||
return 0;
|
||||
size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128);
|
||||
size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert;
|
||||
assert(Size + N <= MaxSize && N);
|
||||
size_t Idx = Rand(Size + 1);
|
||||
// Insert new values at Data[Idx].
|
||||
memmove(Data + Idx + N, Data + Idx, Size - Idx);
|
||||
// Give preference to 0x00 and 0xff.
|
||||
uint8_t Byte = static_cast<uint8_t>(
|
||||
Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255));
|
||||
for (size_t i = 0; i < N; i++)
|
||||
Data[Idx + i] = Byte;
|
||||
return Size + N;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize)
|
||||
return 0;
|
||||
size_t Idx = Rand(Size);
|
||||
Data[Idx] = RandCh(Rand);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize)
|
||||
return 0;
|
||||
size_t Idx = Rand(Size);
|
||||
Data[Idx] ^= 1 << Rand(8);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
DictionaryEntry &DE) {
|
||||
const Word &W = DE.GetW();
|
||||
bool UsePositionHint = DE.HasPositionHint() &&
|
||||
DE.GetPositionHint() + W.size() < Size &&
|
||||
Rand.RandBool();
|
||||
if (Rand.RandBool()) { // Insert W.
|
||||
if (Size + W.size() > MaxSize)
|
||||
return 0;
|
||||
size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1);
|
||||
memmove(Data + Idx + W.size(), Data + Idx, Size - Idx);
|
||||
memcpy(Data + Idx, W.data(), W.size());
|
||||
Size += W.size();
|
||||
} else { // Overwrite some bytes with W.
|
||||
if (W.size() > Size)
|
||||
return 0;
|
||||
size_t Idx =
|
||||
UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1 - W.size());
|
||||
memcpy(Data + Idx, W.data(), W.size());
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
// Somewhere in the past we have observed a comparison instructions
|
||||
// with arguments Arg1 Arg2. This function tries to guess a dictionary
|
||||
// entry that will satisfy that comparison.
|
||||
// It first tries to find one of the arguments (possibly swapped) in the
|
||||
// input and if it succeeds it creates a DE with a position hint.
|
||||
// Otherwise it creates a DE with one of the arguments w/o a position hint.
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
const void *Arg1, const void *Arg2, const void *Arg1Mutation,
|
||||
const void *Arg2Mutation, size_t ArgSize, const uint8_t *Data,
|
||||
size_t Size) {
|
||||
bool HandleFirst = Rand.RandBool();
|
||||
const void *ExistingBytes, *DesiredBytes;
|
||||
Word W;
|
||||
const uint8_t *End = Data + Size;
|
||||
for (int Arg = 0; Arg < 2; Arg++) {
|
||||
ExistingBytes = HandleFirst ? Arg1 : Arg2;
|
||||
DesiredBytes = HandleFirst ? Arg2Mutation : Arg1Mutation;
|
||||
HandleFirst = !HandleFirst;
|
||||
W.Set(reinterpret_cast<const uint8_t *>(DesiredBytes), ArgSize);
|
||||
const size_t kMaxNumPositions = 8;
|
||||
size_t Positions[kMaxNumPositions];
|
||||
size_t NumPositions = 0;
|
||||
for (const uint8_t *Cur = Data;
|
||||
Cur < End && NumPositions < kMaxNumPositions; Cur++) {
|
||||
Cur =
|
||||
(const uint8_t *)SearchMemory(Cur, End - Cur, ExistingBytes, ArgSize);
|
||||
if (!Cur)
|
||||
break;
|
||||
Positions[NumPositions++] = Cur - Data;
|
||||
}
|
||||
if (!NumPositions)
|
||||
continue;
|
||||
return DictionaryEntry(W, Positions[Rand(NumPositions)]);
|
||||
}
|
||||
DictionaryEntry DE(W);
|
||||
return DE;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
|
||||
T Arg1, T Arg2, const uint8_t *Data, size_t Size) {
|
||||
if (Rand.RandBool())
|
||||
Arg1 = Bswap(Arg1);
|
||||
if (Rand.RandBool())
|
||||
Arg2 = Bswap(Arg2);
|
||||
T Arg1Mutation = static_cast<T>(Arg1 + Rand(-1, 1));
|
||||
T Arg2Mutation = static_cast<T>(Arg2 + Rand(-1, 1));
|
||||
return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation,
|
||||
sizeof(Arg1), Data, Size);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromTORC(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
Word W;
|
||||
DictionaryEntry DE;
|
||||
switch (Rand(4)) {
|
||||
case 0: {
|
||||
uint64_t A, B;
|
||||
Config.FromTORC8(Rand.Rand<size_t>(), &A, &B);
|
||||
DE = MakeDictionaryEntryFromCMP(A, B, Data, Size);
|
||||
} break;
|
||||
case 1: {
|
||||
uint32_t A, B;
|
||||
Config.FromTORC4(Rand.Rand<size_t>(), &A, &B);
|
||||
if ((A >> 16) == 0 && (B >> 16) == 0 && Rand.RandBool())
|
||||
DE = MakeDictionaryEntryFromCMP((uint16_t)A, (uint16_t)B, Data, Size);
|
||||
else
|
||||
DE = MakeDictionaryEntryFromCMP(A, B, Data, Size);
|
||||
} break;
|
||||
case 2: {
|
||||
const uint8_t *DataA, *DataB;
|
||||
size_t SizeA, SizeB;
|
||||
Config.FromTORCW(Rand.Rand<size_t>(), &DataA, &SizeA, &DataB, &SizeB);
|
||||
DE = MakeDictionaryEntryFromCMP(DataA, DataB, DataA, DataB, SizeA, Data,
|
||||
Size);
|
||||
} break;
|
||||
case 3:
|
||||
if (Config.UseMemmem) {
|
||||
const uint8_t *DataW;
|
||||
size_t SizeW;
|
||||
Config.FromMMT(Rand.Rand<size_t>(), &DataW, &SizeW);
|
||||
DE = DictionaryEntry(Word(DataW, SizeW));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
if (!DE.GetW().size())
|
||||
return 0;
|
||||
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
|
||||
if (!Size)
|
||||
return 0;
|
||||
DictionaryEntry &DERef =
|
||||
CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ %
|
||||
kCmpDictionaryEntriesDequeSize];
|
||||
DERef = DE;
|
||||
CurrentDictionaryEntrySequence.push_back(&DERef);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary(
|
||||
uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data,
|
||||
size_t Size, size_t MaxSize) {
|
||||
if (Size > MaxSize)
|
||||
return 0;
|
||||
if (D.empty())
|
||||
return 0;
|
||||
DictionaryEntry &DE = D[Rand(D.size())];
|
||||
Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
|
||||
if (!Size)
|
||||
return 0;
|
||||
DE.IncUseCount();
|
||||
CurrentDictionaryEntrySequence.push_back(&DE);
|
||||
return Size;
|
||||
}
|
||||
|
||||
// Overwrites part of To[0,ToSize) with a part of From[0,FromSize).
|
||||
// Returns ToSize.
|
||||
size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize,
|
||||
uint8_t *To, size_t ToSize) {
|
||||
// Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize).
|
||||
size_t ToBeg = Rand(ToSize);
|
||||
size_t CopySize = Rand(ToSize - ToBeg) + 1;
|
||||
assert(ToBeg + CopySize <= ToSize);
|
||||
CopySize = std::min(CopySize, FromSize);
|
||||
size_t FromBeg = Rand(FromSize - CopySize + 1);
|
||||
assert(FromBeg + CopySize <= FromSize);
|
||||
memmove(To + ToBeg, From + FromBeg, CopySize);
|
||||
return ToSize;
|
||||
}
|
||||
|
||||
// Inserts part of From[0,ToSize) into To.
|
||||
// Returns new size of To on success or 0 on failure.
|
||||
size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize,
|
||||
uint8_t *To, size_t ToSize,
|
||||
size_t MaxToSize) {
|
||||
if (ToSize >= MaxToSize)
|
||||
return 0;
|
||||
size_t AvailableSpace = MaxToSize - ToSize;
|
||||
size_t MaxCopySize = std::min(AvailableSpace, FromSize);
|
||||
size_t CopySize = Rand(MaxCopySize) + 1;
|
||||
size_t FromBeg = Rand(FromSize - CopySize + 1);
|
||||
assert(FromBeg + CopySize <= FromSize);
|
||||
size_t ToInsertPos = Rand(ToSize + 1);
|
||||
assert(ToInsertPos + CopySize <= MaxToSize);
|
||||
size_t TailSize = ToSize - ToInsertPos;
|
||||
if (To == From) {
|
||||
MutateInPlaceHere.resize(MaxToSize);
|
||||
memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize);
|
||||
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
|
||||
memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize);
|
||||
} else {
|
||||
memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
|
||||
memmove(To + ToInsertPos, From + FromBeg, CopySize);
|
||||
}
|
||||
return ToSize + CopySize;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize || Size == 0)
|
||||
return 0;
|
||||
// If Size == MaxSize, `InsertPartOf(...)` will
|
||||
// fail so there's no point using it in this case.
|
||||
if (Size == MaxSize || Rand.RandBool())
|
||||
return CopyPartOf(Data, Size, Data, Size);
|
||||
else
|
||||
return InsertPartOf(Data, Size, Data, Size, MaxSize);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize)
|
||||
return 0;
|
||||
size_t B = Rand(Size);
|
||||
while (B < Size && !isdigit(Data[B]))
|
||||
B++;
|
||||
if (B == Size)
|
||||
return 0;
|
||||
size_t E = B;
|
||||
while (E < Size && isdigit(Data[E]))
|
||||
E++;
|
||||
assert(B < E);
|
||||
// now we have digits in [B, E).
|
||||
// strtol and friends don't accept non-zero-teminated data, parse it manually.
|
||||
uint64_t Val = Data[B] - '0';
|
||||
for (size_t i = B + 1; i < E; i++)
|
||||
Val = Val * 10 + Data[i] - '0';
|
||||
|
||||
// Mutate the integer value.
|
||||
switch (Rand(5)) {
|
||||
case 0:
|
||||
Val++;
|
||||
break;
|
||||
case 1:
|
||||
Val--;
|
||||
break;
|
||||
case 2:
|
||||
Val /= 2;
|
||||
break;
|
||||
case 3:
|
||||
Val *= 2;
|
||||
break;
|
||||
case 4:
|
||||
Val = Rand(Val * Val);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
// Just replace the bytes with the new ones, don't bother moving bytes.
|
||||
for (size_t i = B; i < E; i++) {
|
||||
size_t Idx = E + B - i - 1;
|
||||
assert(Idx >= B && Idx < E);
|
||||
Data[Idx] = (Val % 10) + '0';
|
||||
Val /= 10;
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) {
|
||||
if (Size < sizeof(T))
|
||||
return 0;
|
||||
size_t Off = Rand(Size - sizeof(T) + 1);
|
||||
assert(Off + sizeof(T) <= Size);
|
||||
T Val;
|
||||
if (Off < 64 && !Rand(4)) {
|
||||
Val = static_cast<T>(Size);
|
||||
if (Rand.RandBool())
|
||||
Val = Bswap(Val);
|
||||
} else {
|
||||
memcpy(&Val, Data + Off, sizeof(Val));
|
||||
T Add = static_cast<T>(Rand(21));
|
||||
Add -= 10;
|
||||
if (Rand.RandBool())
|
||||
Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes.
|
||||
else
|
||||
Val = Val + Add; // Add assuming current endiannes.
|
||||
if (Add == 0 || Rand.RandBool()) // Maybe negate.
|
||||
Val = -Val;
|
||||
}
|
||||
memcpy(Data + Off, &Val, sizeof(Val));
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data,
|
||||
size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize)
|
||||
return 0;
|
||||
switch (Rand(4)) {
|
||||
case 3:
|
||||
return ChangeBinaryInteger<uint64_t>(Data, Size, Rand);
|
||||
case 2:
|
||||
return ChangeBinaryInteger<uint32_t>(Data, Size, Rand);
|
||||
case 1:
|
||||
return ChangeBinaryInteger<uint16_t>(Data, Size, Rand);
|
||||
case 0:
|
||||
return ChangeBinaryInteger<uint8_t>(Data, Size, Rand);
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
if (Size > MaxSize)
|
||||
return 0;
|
||||
if (Size == 0)
|
||||
return 0;
|
||||
if (!CrossOverWith)
|
||||
return 0;
|
||||
const Unit &O = *CrossOverWith;
|
||||
if (O.empty())
|
||||
return 0;
|
||||
size_t NewSize = 0;
|
||||
switch (Rand(3)) {
|
||||
case 0:
|
||||
MutateInPlaceHere.resize(MaxSize);
|
||||
NewSize = CrossOver(Data, Size, O.data(), O.size(),
|
||||
MutateInPlaceHere.data(), MaxSize);
|
||||
memcpy(Data, MutateInPlaceHere.data(), NewSize);
|
||||
break;
|
||||
case 1:
|
||||
NewSize = InsertPartOf(O.data(), O.size(), Data, Size, MaxSize);
|
||||
if (!NewSize)
|
||||
NewSize = CopyPartOf(O.data(), O.size(), Data, Size);
|
||||
break;
|
||||
case 2:
|
||||
NewSize = CopyPartOf(O.data(), O.size(), Data, Size);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
assert(NewSize > 0 && "CrossOver returned empty unit");
|
||||
assert(NewSize <= MaxSize && "CrossOver returned overisized unit");
|
||||
return NewSize;
|
||||
}
|
||||
|
||||
void MutationDispatcher::StartMutationSequence() {
|
||||
CurrentMutatorSequence.clear();
|
||||
CurrentDictionaryEntrySequence.clear();
|
||||
}
|
||||
|
||||
// Copy successful dictionary entries to PersistentAutoDictionary.
|
||||
void MutationDispatcher::RecordSuccessfulMutationSequence() {
|
||||
for (auto *DE : CurrentDictionaryEntrySequence) {
|
||||
// PersistentAutoDictionary.AddWithSuccessCountOne(DE);
|
||||
DE->IncSuccessCount();
|
||||
assert(DE->GetW().size());
|
||||
// Linear search is fine here as this happens seldom.
|
||||
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
|
||||
PersistentAutoDictionary.push_back(*DE);
|
||||
}
|
||||
}
|
||||
|
||||
const Dictionary &MutationDispatcher::RecommendDictionary() {
|
||||
RecommendedDictionary.clear();
|
||||
for (auto &DE : PersistentAutoDictionary)
|
||||
if (!ManualDictionary.ContainsWord(DE.GetW()))
|
||||
RecommendedDictionary.push_back(DE);
|
||||
NextRecommendedDictionaryEntry = 0;
|
||||
return RecommendedDictionary;
|
||||
}
|
||||
|
||||
const char *MutationDispatcher::RecommendDictionaryEntry(size_t *UseCount) {
|
||||
if (NextRecommendedDictionaryEntry >= RecommendedDictionary.size())
|
||||
return nullptr;
|
||||
auto &DE = RecommendedDictionary[NextRecommendedDictionaryEntry++];
|
||||
assert(DE.GetW().size());
|
||||
DictionaryEntryWord = ToASCII(DE.GetW());
|
||||
if (UseCount)
|
||||
*UseCount = DE.GetUseCount();
|
||||
return DictionaryEntryWord.c_str();
|
||||
}
|
||||
|
||||
const Sequence<MutationDispatcher::Mutator> &
|
||||
MutationDispatcher::MutationSequence() {
|
||||
CurrentMutatorSequence.SetString([](Mutator M) { return M.Name; });
|
||||
return CurrentMutatorSequence;
|
||||
}
|
||||
|
||||
const Sequence<DictionaryEntry *> &
|
||||
MutationDispatcher::DictionaryEntrySequence() {
|
||||
CurrentDictionaryEntrySequence.SetString([](DictionaryEntry *DE) {
|
||||
return std::string("\"") + ToASCII(DE->GetW()) + std::string("\"");
|
||||
});
|
||||
return CurrentDictionaryEntrySequence;
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
|
||||
return MutateImpl(Data, Size, MaxSize, Mutators);
|
||||
}
|
||||
|
||||
size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize) {
|
||||
return MutateImpl(Data, Size, MaxSize, DefaultMutators);
|
||||
}
|
||||
|
||||
// Mutates Data in place, returns new size.
|
||||
size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
Vector<Mutator> &Mutators) {
|
||||
assert(MaxSize > 0);
|
||||
// Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize),
|
||||
// in which case they will return 0.
|
||||
// Try several times before returning un-mutated data.
|
||||
for (int Iter = 0; Iter < 100; Iter++) {
|
||||
auto M = Mutators[Rand(Mutators.size())];
|
||||
size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize);
|
||||
if (NewSize && NewSize <= MaxSize) {
|
||||
if (Config.OnlyASCII)
|
||||
ToASCII(Data, NewSize);
|
||||
CurrentMutatorSequence.push_back(M);
|
||||
return NewSize;
|
||||
}
|
||||
}
|
||||
*Data = ' ';
|
||||
return 1; // Fallback, should not happen frequently.
|
||||
}
|
||||
|
||||
// Mask represents the set of Data bytes that are worth mutating.
|
||||
size_t MutationDispatcher::MutateWithMask(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize,
|
||||
const Vector<uint8_t> &Mask) {
|
||||
size_t MaskedSize = std::min(Size, Mask.size());
|
||||
// * Copy the worthy bytes into a temporary array T
|
||||
// * Mutate T
|
||||
// * Copy T back.
|
||||
// This is totally unoptimized.
|
||||
auto &T = MutateWithMaskTemp;
|
||||
if (T.size() < Size)
|
||||
T.resize(Size);
|
||||
size_t OneBits = 0;
|
||||
for (size_t I = 0; I < MaskedSize; I++)
|
||||
if (Mask[I])
|
||||
T[OneBits++] = Data[I];
|
||||
|
||||
if (!OneBits)
|
||||
return 0;
|
||||
assert(!T.empty());
|
||||
size_t NewSize = Mutate(T.data(), OneBits, OneBits);
|
||||
assert(NewSize <= OneBits);
|
||||
(void)NewSize;
|
||||
// Even if NewSize < OneBits we still use all OneBits bytes.
|
||||
for (size_t I = 0, J = 0; I < MaskedSize; I++)
|
||||
if (Mask[I])
|
||||
Data[I] = T[J++];
|
||||
return Size;
|
||||
}
|
||||
|
||||
void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
|
||||
ManualDictionary.push_back({W, std::numeric_limits<size_t>::max()});
|
||||
}
|
||||
|
||||
} // namespace mutagen
|
|
@ -0,0 +1,190 @@
|
|||
//===- MutagenDispatcher.h - Internal header for the mutagen ----*- C++ -* ===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// mutagen::MutationDispatcher
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_MUTAGEN_DISPATCHER_H
|
||||
#define LLVM_FUZZER_MUTAGEN_DISPATCHER_H
|
||||
|
||||
#include "FuzzerRandom.h"
|
||||
#include "Mutagen.h"
|
||||
#include "MutagenDictionary.h"
|
||||
#include "MutagenSequence.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace mutagen {
|
||||
namespace {
|
||||
|
||||
using fuzzer::Random;
|
||||
using fuzzer::Unit;
|
||||
using fuzzer::Vector;
|
||||
using fuzzer::Word;
|
||||
|
||||
} // namespace
|
||||
|
||||
class MutationDispatcher final {
|
||||
public:
|
||||
struct Mutator {
|
||||
size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
|
||||
const char *Name;
|
||||
};
|
||||
|
||||
explicit MutationDispatcher(const LLVMMutagenConfiguration *Config);
|
||||
~MutationDispatcher() = default;
|
||||
|
||||
/// Indicate that we are about to start a new sequence of mutations.
|
||||
void StartMutationSequence();
|
||||
/// Returns the current sequence of mutations. May truncate the sequence
|
||||
/// unless Verbose is true. Sets |OutSize| to the length of the untrancated
|
||||
/// sequence, if provided.
|
||||
const Sequence<Mutator> &MutationSequence();
|
||||
/// Returns the current sequence of dictionary entries. May truncate the
|
||||
/// sequence unless Verbose is true. Sets |OutSize| to the length of the
|
||||
/// untrancated sequence, if provided.
|
||||
const Sequence<DictionaryEntry *> &DictionaryEntrySequence();
|
||||
/// Indicate that the current sequence of mutations was successful.
|
||||
void RecordSuccessfulMutationSequence();
|
||||
/// Mutates data by invoking user-provided mutator.
|
||||
size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by invoking user-provided crossover.
|
||||
size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by shuffling bytes.
|
||||
size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by erasing bytes.
|
||||
size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by inserting a byte.
|
||||
size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by inserting several repeated bytes.
|
||||
size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by changing one byte.
|
||||
size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by changing one bit.
|
||||
size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Mutates data by copying/inserting a part of data into a different place.
|
||||
size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the manual dictionary.
|
||||
size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the TORC.
|
||||
size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Mutates data by adding a word from the persistent automatic dictionary.
|
||||
size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
/// Tries to find an ASCII integer in Data, changes it to another ASCII int.
|
||||
size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
/// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways.
|
||||
size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// CrossOver Data with CrossOverWith.
|
||||
size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
Vector<Mutator> &Mutators);
|
||||
|
||||
size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
|
||||
size_t ToSize, size_t MaxToSize);
|
||||
size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
|
||||
size_t ToSize);
|
||||
size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
DictionaryEntry &DE);
|
||||
|
||||
template <class T>
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2,
|
||||
const uint8_t *Data, size_t Size);
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(const Word &Arg1, const Word &Arg2,
|
||||
const uint8_t *Data, size_t Size);
|
||||
DictionaryEntry MakeDictionaryEntryFromCMP(const void *Arg1, const void *Arg2,
|
||||
const void *Arg1Mutation,
|
||||
const void *Arg2Mutation,
|
||||
size_t ArgSize,
|
||||
const uint8_t *Data, size_t Size);
|
||||
|
||||
/// Applies one of the configured mutations.
|
||||
/// Returns the new size of data which could be up to MaxSize.
|
||||
size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Applies one of the configured mutations to the bytes of Data
|
||||
/// that have '1' in Mask.
|
||||
/// Mask.size() should be >= Size.
|
||||
size_t MutateWithMask(uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
const Vector<uint8_t> &Mask);
|
||||
|
||||
/// Applies one of the default mutations. Provided as a service
|
||||
/// to mutation authors.
|
||||
size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize);
|
||||
|
||||
/// Creates a cross-over of two pieces of Data, returns its size.
|
||||
size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2,
|
||||
size_t Size2, uint8_t *Out, size_t MaxOutSize);
|
||||
|
||||
void AddWordToManualDictionary(const Word &W);
|
||||
|
||||
// Creates a recommended dictionary and returns its number of entries. The
|
||||
// entries can be retrieved by subsequent calls to
|
||||
// |LLVMMutagenRecommendDictionaryEntry|.
|
||||
const Dictionary &RecommendDictionary();
|
||||
|
||||
// Returns the ASCII representation of the next recommended dictionary entry,
|
||||
// and sets |OutUseCount| to its use count. The return pointer is valid until
|
||||
// the next call to this method.
|
||||
const char *RecommendDictionaryEntry(size_t *OutUseCount);
|
||||
|
||||
void SetCrossOverWith(const Unit *U) { CrossOverWith = U; }
|
||||
|
||||
Random &GetRand() { return Rand; }
|
||||
|
||||
private:
|
||||
// Imports and validates the disptacher's configuration.
|
||||
void SetConfig(const LLVMMutagenConfiguration *Config);
|
||||
|
||||
Random Rand;
|
||||
LLVMMutagenConfiguration Config;
|
||||
|
||||
// Dictionary provided by the user via -dict=DICT_FILE.
|
||||
Dictionary ManualDictionary;
|
||||
// Persistent dictionary modified by the fuzzer, consists of
|
||||
// entries that led to successful discoveries in the past mutations.
|
||||
Dictionary PersistentAutoDictionary;
|
||||
// Recommended dictionary buolt by |RecommendDictionary|.
|
||||
Dictionary RecommendedDictionary;
|
||||
size_t NextRecommendedDictionaryEntry = 0;
|
||||
std::string DictionaryEntryWord;
|
||||
|
||||
Sequence<DictionaryEntry *> CurrentDictionaryEntrySequence;
|
||||
|
||||
static const size_t kCmpDictionaryEntriesDequeSize = 16;
|
||||
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
|
||||
size_t CmpDictionaryEntriesDequeIdx = 0;
|
||||
|
||||
const Unit *CrossOverWith = nullptr;
|
||||
Vector<uint8_t> MutateInPlaceHere;
|
||||
Vector<uint8_t> MutateWithMaskTemp;
|
||||
// CustomCrossOver needs its own buffer as a custom implementation may call
|
||||
// LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere.
|
||||
Vector<uint8_t> CustomCrossOverInPlaceHere;
|
||||
|
||||
Vector<Mutator> Mutators;
|
||||
Vector<Mutator> DefaultMutators;
|
||||
Sequence<Mutator> CurrentMutatorSequence;
|
||||
};
|
||||
|
||||
// Returns a pointer to the MutationDispatcher is use by MutagenInterface.
|
||||
// This should only be used for testing.
|
||||
MutationDispatcher *GetMutationDispatcherForTest();
|
||||
|
||||
} // namespace mutagen
|
||||
|
||||
#endif // LLVM_FUZZER_MUTAGEN_DISPATCHER_H
|
|
@ -0,0 +1,101 @@
|
|||
//===- MutagenSequence.h - Internal header for the mutagen ------*- C++ -* ===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// mutagen::Sequence
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_MUTAGEN_SEQUENCE_H
|
||||
#define LLVM_FUZZER_MUTAGEN_SEQUENCE_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace mutagen {
|
||||
namespace {
|
||||
|
||||
using fuzzer::Vector;
|
||||
|
||||
} // namespace
|
||||
|
||||
// The Sequence type bundles together a list of items, a string representation,
|
||||
// and a position in that string suitable for truncating it when overly long,
|
||||
// e.g. after the tenth item.
|
||||
template <typename T> class Sequence {
|
||||
public:
|
||||
constexpr static size_t kMaxBriefItems = 10;
|
||||
|
||||
void clear() {
|
||||
Items.clear();
|
||||
Size = 0;
|
||||
Str.clear();
|
||||
Brief = 0;
|
||||
}
|
||||
|
||||
bool empty() const { return Size == 0; }
|
||||
|
||||
size_t size() const { return Size; }
|
||||
|
||||
void push_back(T t) { Items.push_back(t); }
|
||||
|
||||
typename Vector<T>::const_iterator begin() const { return Items.begin(); }
|
||||
typename Vector<T>::iterator begin() { return Items.begin(); }
|
||||
|
||||
typename Vector<T>::const_iterator end() const { return Items.end(); }
|
||||
typename Vector<T>::iterator end() { return Items.end(); }
|
||||
|
||||
std::string GetString(bool Verbose = true) const {
|
||||
return Verbose ? Str : Str.substr(0, Brief);
|
||||
}
|
||||
|
||||
// Constructs the string representation of the sequence, using a callback that
|
||||
// converts items to strings.
|
||||
template <typename ItemCallback>
|
||||
// std::string ItemCallback(T Item);
|
||||
void SetString(ItemCallback ConvertToASCII) {
|
||||
// No change since last call.
|
||||
if (Size == Items.size())
|
||||
return;
|
||||
Size = Items.size();
|
||||
std::ostringstream OSS;
|
||||
size_t i = 0;
|
||||
for (; i < Size && i < kMaxBriefItems; i++)
|
||||
OSS << ConvertToASCII(Items[i]) << "-";
|
||||
Brief = static_cast<size_t>(OSS.tellp());
|
||||
for (; i < Size; i++)
|
||||
OSS << ConvertToASCII(Items[i]) << "-";
|
||||
Str = OSS.str();
|
||||
}
|
||||
|
||||
private:
|
||||
Vector<T> Items;
|
||||
size_t Size = 0;
|
||||
std::string Str;
|
||||
size_t Brief = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
typename Vector<T>::const_iterator begin(const Sequence<T> &S) {
|
||||
return S.begin();
|
||||
}
|
||||
|
||||
template <typename T> typename Vector<T>::iterator begin(Sequence<T> &S) {
|
||||
return S.begin();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename Vector<T>::const_iterator end(const Sequence<T> &S) {
|
||||
return S.end();
|
||||
}
|
||||
|
||||
template <typename T> typename Vector<T>::iterator end(Sequence<T> &S) {
|
||||
return S.end();
|
||||
}
|
||||
|
||||
} // namespace mutagen
|
||||
|
||||
#endif // LLVM_FUZZER_MUTAGEN_SEQUENCE_H
|
|
@ -0,0 +1,24 @@
|
|||
//===- MutagenUtil.h - Internal header for the mutagen Utils ----*- C++ -* ===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Util functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_MUTAGEN_UTIL_H
|
||||
#define LLVM_FUZZER_MUTAGEN_UTIL_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace mutagen {
|
||||
|
||||
const void *SearchMemory(const void *haystack, size_t haystacklen,
|
||||
const void *needle, size_t needlelen);
|
||||
|
||||
} // namespace mutagen
|
||||
|
||||
#endif // LLVM_FUZZER_MUTAGEN_UTIL_H
|
|
@ -0,0 +1,23 @@
|
|||
//===- MutagenUtilPosix.cpp - Misc utils for Posix. -----------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc utils implementation using Posix API.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerPlatform.h"
|
||||
#if (LIBFUZZER_POSIX || LIBFUZZER_FUCHSIA)
|
||||
#include <cstring>
|
||||
|
||||
namespace mutagen {
|
||||
|
||||
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||
size_t PattLen) {
|
||||
return memmem(Data, DataLen, Patt, PattLen);
|
||||
}
|
||||
|
||||
} // namespace mutagen
|
||||
|
||||
#endif // (LIBFUZZER_POSIX || LIBFUZZER_FUCHSIA)
|
|
@ -0,0 +1,41 @@
|
|||
//===- MutagenUtilWindows.cpp - Misc utils for Windows. -------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc utils implementation for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerPlatform.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
namespace mutagen {
|
||||
|
||||
const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||
size_t PattLen) {
|
||||
// TODO: make this implementation more efficient.
|
||||
const char *Cdata = (const char *)Data;
|
||||
const char *Cpatt = (const char *)Patt;
|
||||
|
||||
if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen)
|
||||
return NULL;
|
||||
|
||||
if (PattLen == 1)
|
||||
return memchr(Data, *Cpatt, DataLen);
|
||||
|
||||
const char *End = Cdata + DataLen - PattLen + 1;
|
||||
|
||||
for (const char *It = Cdata; It < End; ++It)
|
||||
if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0)
|
||||
return It;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace mutagen
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
LIBMUTAGEN_SRC_DIR=$(dirname $0)
|
||||
CXX="${CXX:-clang}"
|
||||
for f in $LIBMUTAGEN_SRC_DIR/*.cpp; do
|
||||
$CXX -O2 -fno-omit-frame-pointer -std=c++11 -I$LIBMUTAGEN_SRC_DIR/.. $f -c &
|
||||
done
|
||||
wait
|
||||
rm -f libMutagen.a
|
||||
ar ru libMutagen.a Mutagen*.o
|
||||
rm -f Mutagen*.o
|
||||
|
|
@ -17,6 +17,9 @@ set_target_properties(FuzzerUnitTests PROPERTIES FOLDER "Compiler-RT Tests")
|
|||
add_custom_target(FuzzedDataProviderUnitTests)
|
||||
set_target_properties(FuzzedDataProviderUnitTests PROPERTIES FOLDER "Compiler-RT Tests")
|
||||
|
||||
add_custom_target(MutagenUnitTests)
|
||||
set_target_properties(MutagenUnitTests PROPERTIES FOLDER "Compiler-RT Tests")
|
||||
|
||||
set(LIBFUZZER_UNITTEST_LINK_FLAGS ${COMPILER_RT_UNITTEST_LINK_FLAGS})
|
||||
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS --driver-mode=g++)
|
||||
|
||||
|
@ -46,19 +49,31 @@ if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST FUZZER_SUPPORTED_ARCH)
|
|||
set(arch ${COMPILER_RT_DEFAULT_TARGET_ARCH})
|
||||
|
||||
set(LIBFUZZER_TEST_RUNTIME RTFuzzerTest.${arch})
|
||||
set(LIBMUTAGEN_TEST_RUNTIME RTMutagenTest.${arch})
|
||||
if(APPLE)
|
||||
set(LIBFUZZER_TEST_RUNTIME_OBJECTS
|
||||
$<TARGET_OBJECTS:RTfuzzer.osx>)
|
||||
set(LIBMUTAGEN_TEST_RUNTIME_OBJECTS
|
||||
$<TARGET_OBJECTS:RTmutagen.osx>)
|
||||
else()
|
||||
set(LIBFUZZER_TEST_RUNTIME_OBJECTS
|
||||
$<TARGET_OBJECTS:RTfuzzer.${arch}>)
|
||||
set(LIBMUTAGEN_TEST_RUNTIME_OBJECTS
|
||||
$<TARGET_OBJECTS:RTmutagen.${arch}>)
|
||||
endif()
|
||||
add_library(${LIBFUZZER_TEST_RUNTIME} STATIC
|
||||
${LIBFUZZER_TEST_RUNTIME_OBJECTS})
|
||||
${LIBFUZZER_TEST_RUNTIME_OBJECTS}
|
||||
${LIBMUTAGEN_TEST_RUNTIME_OBJECTS})
|
||||
set_target_properties(${LIBFUZZER_TEST_RUNTIME} PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
FOLDER "Compiler-RT Runtime tests")
|
||||
|
||||
add_library(${LIBMUTAGEN_TEST_RUNTIME} STATIC
|
||||
${LIBMUTAGEN_TEST_RUNTIME_OBJECTS})
|
||||
set_target_properties(${LIBMUTAGEN_TEST_RUNTIME} PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
FOLDER "Compiler-RT Runtime tests")
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND
|
||||
COMPILER_RT_LIBCXX_PATH AND
|
||||
COMPILER_RT_LIBCXXABI_PATH)
|
||||
|
@ -66,6 +81,8 @@ if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST FUZZER_SUPPORTED_ARCH)
|
|||
set(LIBFUZZER_TEST_RUNTIME_DEPS libcxx_fuzzer_${arch}-build ${libfuzzer_headers})
|
||||
set(LIBFUZZER_TEST_RUNTIME_CFLAGS -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1)
|
||||
set(LIBFUZZER_TEST_RUNTIME_LINK_FLAGS ${LIBCXX_${arch}_PREFIX}/lib/libc++.a)
|
||||
file(GLOB libmutagen_headers ../mutagen/*.h)
|
||||
set(LIBMUTAGEN_TEST_RUNTIME_DEPS libcxx_fuzzer_${arch}-build ${libmutagen_headers})
|
||||
endif()
|
||||
|
||||
set(FuzzerTestObjects)
|
||||
|
@ -73,7 +90,7 @@ if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST FUZZER_SUPPORTED_ARCH)
|
|||
FuzzerUnitTests "Fuzzer-${arch}-Test" ${arch}
|
||||
SOURCES FuzzerUnittest.cpp ${COMPILER_RT_GTEST_SOURCE}
|
||||
RUNTIME ${LIBFUZZER_TEST_RUNTIME}
|
||||
DEPS gtest ${LIBFUZZER_TEST_RUNTIME_DEPS}
|
||||
DEPS gtest ${LIBFUZZER_TEST_RUNTIME_DEPS}
|
||||
CFLAGS ${LIBFUZZER_UNITTEST_CFLAGS} ${LIBFUZZER_TEST_RUNTIME_CFLAGS}
|
||||
LINK_FLAGS ${LIBFUZZER_UNITTEST_LINK_FLAGS} ${LIBFUZZER_TEST_RUNTIME_LINK_FLAGS})
|
||||
set_target_properties(FuzzerUnitTests PROPERTIES
|
||||
|
@ -88,4 +105,15 @@ if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST FUZZER_SUPPORTED_ARCH)
|
|||
LINK_FLAGS ${LIBFUZZER_UNITTEST_LINK_FLAGS} ${LIBFUZZER_TEST_RUNTIME_LINK_FLAGS})
|
||||
set_target_properties(FuzzedDataProviderUnitTests PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(MutagenTestObjects)
|
||||
generate_compiler_rt_tests(MutagenTestObjects
|
||||
MutagenUnitTests "Mutagen-${arch}-Test" ${arch}
|
||||
SOURCES MutagenUnittest.cpp ${COMPILER_RT_GTEST_SOURCE}
|
||||
RUNTIME ${LIBFUZZER_TEST_RUNTIME}
|
||||
DEPS gtest ${LIBFUZZER_TEST_RUNTIME_DEPS} ${LIBMUTAGEN_TEST_RUNTIME_DEPS}
|
||||
CFLAGS ${LIBFUZZER_UNITTEST_CFLAGS} ${LIBFUZZER_TEST_RUNTIME_CFLAGS}
|
||||
LINK_FLAGS ${LIBFUZZER_UNITTEST_LINK_FLAGS} ${LIBFUZZER_TEST_RUNTIME_LINK_FLAGS})
|
||||
set_target_properties(MutagenUnitTests PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#define GTEST_NO_LLVM_SUPPORT 1
|
||||
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
#include "FuzzerInternal.h"
|
||||
#include "FuzzerMerge.h"
|
||||
#include "FuzzerMutate.h"
|
||||
|
@ -44,65 +43,6 @@ TEST(Fuzzer, Basename) {
|
|||
#endif
|
||||
}
|
||||
|
||||
TEST(Fuzzer, CrossOver) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
|
||||
Unit A({0, 1, 2}), B({5, 6, 7});
|
||||
Unit C;
|
||||
Unit Expected[] = {
|
||||
{ 0 },
|
||||
{ 0, 1 },
|
||||
{ 0, 5 },
|
||||
{ 0, 1, 2 },
|
||||
{ 0, 1, 5 },
|
||||
{ 0, 5, 1 },
|
||||
{ 0, 5, 6 },
|
||||
{ 0, 1, 2, 5 },
|
||||
{ 0, 1, 5, 2 },
|
||||
{ 0, 1, 5, 6 },
|
||||
{ 0, 5, 1, 2 },
|
||||
{ 0, 5, 1, 6 },
|
||||
{ 0, 5, 6, 1 },
|
||||
{ 0, 5, 6, 7 },
|
||||
{ 0, 1, 2, 5, 6 },
|
||||
{ 0, 1, 5, 2, 6 },
|
||||
{ 0, 1, 5, 6, 2 },
|
||||
{ 0, 1, 5, 6, 7 },
|
||||
{ 0, 5, 1, 2, 6 },
|
||||
{ 0, 5, 1, 6, 2 },
|
||||
{ 0, 5, 1, 6, 7 },
|
||||
{ 0, 5, 6, 1, 2 },
|
||||
{ 0, 5, 6, 1, 7 },
|
||||
{ 0, 5, 6, 7, 1 },
|
||||
{ 0, 1, 2, 5, 6, 7 },
|
||||
{ 0, 1, 5, 2, 6, 7 },
|
||||
{ 0, 1, 5, 6, 2, 7 },
|
||||
{ 0, 1, 5, 6, 7, 2 },
|
||||
{ 0, 5, 1, 2, 6, 7 },
|
||||
{ 0, 5, 1, 6, 2, 7 },
|
||||
{ 0, 5, 1, 6, 7, 2 },
|
||||
{ 0, 5, 6, 1, 2, 7 },
|
||||
{ 0, 5, 6, 1, 7, 2 },
|
||||
{ 0, 5, 6, 7, 1, 2 }
|
||||
};
|
||||
for (size_t Len = 1; Len < 8; Len++) {
|
||||
Set<Unit> FoundUnits, ExpectedUnitsWitThisLength;
|
||||
for (int Iter = 0; Iter < 3000; Iter++) {
|
||||
C.resize(Len);
|
||||
size_t NewSize = MD->CrossOver(A.data(), A.size(), B.data(), B.size(),
|
||||
C.data(), C.size());
|
||||
C.resize(NewSize);
|
||||
FoundUnits.insert(C);
|
||||
}
|
||||
for (const Unit &U : Expected)
|
||||
if (U.size() <= Len)
|
||||
ExpectedUnitsWitThisLength.insert(U);
|
||||
EXPECT_EQ(ExpectedUnitsWitThisLength, FoundUnits);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Fuzzer, Hash) {
|
||||
uint8_t A[] = {'a', 'b', 'c'};
|
||||
fuzzer::Unit U(A, A + sizeof(A));
|
||||
|
@ -111,423 +51,6 @@ TEST(Fuzzer, Hash) {
|
|||
EXPECT_EQ("81fe8bfe87576c3ecb22426f8e57847382917acf", fuzzer::Hash(U));
|
||||
}
|
||||
|
||||
typedef size_t (MutationDispatcher::*Mutator)(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
void TestEraseBytes(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
uint8_t REM0[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM1[8] = {0x00, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM2[8] = {0x00, 0x11, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM3[8] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM4[8] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x66, 0x77};
|
||||
uint8_t REM5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x66, 0x77};
|
||||
uint8_t REM6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x77};
|
||||
uint8_t REM7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
|
||||
uint8_t REM8[6] = {0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM9[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
|
||||
uint8_t REM10[6] = {0x00, 0x11, 0x22, 0x55, 0x66, 0x77};
|
||||
|
||||
uint8_t REM11[5] = {0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM12[5] = {0x00, 0x11, 0x22, 0x33, 0x44};
|
||||
uint8_t REM13[5] = {0x00, 0x44, 0x55, 0x66, 0x77};
|
||||
|
||||
|
||||
Random Rand(0);
|
||||
std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
|
||||
int FoundMask = 0;
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (*MD.*M)(T, sizeof(T), sizeof(T));
|
||||
if (NewSize == 7 && !memcmp(REM0, T, 7)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(REM1, T, 7)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(REM2, T, 7)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(REM3, T, 7)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 7 && !memcmp(REM4, T, 7)) FoundMask |= 1 << 4;
|
||||
if (NewSize == 7 && !memcmp(REM5, T, 7)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 7 && !memcmp(REM6, T, 7)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 7 && !memcmp(REM7, T, 7)) FoundMask |= 1 << 7;
|
||||
|
||||
if (NewSize == 6 && !memcmp(REM8, T, 6)) FoundMask |= 1 << 8;
|
||||
if (NewSize == 6 && !memcmp(REM9, T, 6)) FoundMask |= 1 << 9;
|
||||
if (NewSize == 6 && !memcmp(REM10, T, 6)) FoundMask |= 1 << 10;
|
||||
|
||||
if (NewSize == 5 && !memcmp(REM11, T, 5)) FoundMask |= 1 << 11;
|
||||
if (NewSize == 5 && !memcmp(REM12, T, 5)) FoundMask |= 1 << 12;
|
||||
if (NewSize == 5 && !memcmp(REM13, T, 5)) FoundMask |= 1 << 13;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, (1 << 14) - 1);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, EraseBytes1) {
|
||||
TestEraseBytes(&MutationDispatcher::Mutate_EraseBytes, 200);
|
||||
}
|
||||
TEST(FuzzerMutate, EraseBytes2) {
|
||||
TestEraseBytes(&MutationDispatcher::Mutate, 2000);
|
||||
}
|
||||
|
||||
void TestInsertByte(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
|
||||
int FoundMask = 0;
|
||||
uint8_t INS0[8] = {0xF1, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t INS1[8] = {0x00, 0xF2, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t INS2[8] = {0x00, 0x11, 0xF3, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t INS3[8] = {0x00, 0x11, 0x22, 0xF4, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t INS4[8] = {0x00, 0x11, 0x22, 0x33, 0xF5, 0x44, 0x55, 0x66};
|
||||
uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF6, 0x55, 0x66};
|
||||
uint8_t INS6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF7, 0x66};
|
||||
uint8_t INS7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF8};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
size_t NewSize = (*MD.*M)(T, 7, 8);
|
||||
if (NewSize == 8 && !memcmp(INS0, T, 8)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 8 && !memcmp(INS1, T, 8)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 8 && !memcmp(INS2, T, 8)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 8 && !memcmp(INS3, T, 8)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 8 && !memcmp(INS4, T, 8)) FoundMask |= 1 << 4;
|
||||
if (NewSize == 8 && !memcmp(INS5, T, 8)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(INS6, T, 8)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(INS7, T, 8)) FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, InsertByte1) {
|
||||
TestInsertByte(&MutationDispatcher::Mutate_InsertByte, 1 << 15);
|
||||
}
|
||||
TEST(FuzzerMutate, InsertByte2) {
|
||||
TestInsertByte(&MutationDispatcher::Mutate, 1 << 17);
|
||||
}
|
||||
|
||||
void TestInsertRepeatedBytes(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
|
||||
int FoundMask = 0;
|
||||
uint8_t INS0[7] = {0x00, 0x11, 0x22, 0x33, 'a', 'a', 'a'};
|
||||
uint8_t INS1[7] = {0x00, 0x11, 0x22, 'a', 'a', 'a', 0x33};
|
||||
uint8_t INS2[7] = {0x00, 0x11, 'a', 'a', 'a', 0x22, 0x33};
|
||||
uint8_t INS3[7] = {0x00, 'a', 'a', 'a', 0x11, 0x22, 0x33};
|
||||
uint8_t INS4[7] = {'a', 'a', 'a', 0x00, 0x11, 0x22, 0x33};
|
||||
|
||||
uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 'b', 'b', 'b', 'b'};
|
||||
uint8_t INS6[8] = {0x00, 0x11, 0x22, 'b', 'b', 'b', 'b', 0x33};
|
||||
uint8_t INS7[8] = {0x00, 0x11, 'b', 'b', 'b', 'b', 0x22, 0x33};
|
||||
uint8_t INS8[8] = {0x00, 'b', 'b', 'b', 'b', 0x11, 0x22, 0x33};
|
||||
uint8_t INS9[8] = {'b', 'b', 'b', 'b', 0x00, 0x11, 0x22, 0x33};
|
||||
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33};
|
||||
size_t NewSize = (*MD.*M)(T, 4, 8);
|
||||
if (NewSize == 7 && !memcmp(INS0, T, 7)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(INS1, T, 7)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(INS2, T, 7)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(INS3, T, 7)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 7 && !memcmp(INS4, T, 7)) FoundMask |= 1 << 4;
|
||||
|
||||
if (NewSize == 8 && !memcmp(INS5, T, 8)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(INS6, T, 8)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(INS7, T, 8)) FoundMask |= 1 << 7;
|
||||
if (NewSize == 8 && !memcmp(INS8, T, 8)) FoundMask |= 1 << 8;
|
||||
if (NewSize == 8 && !memcmp(INS9, T, 8)) FoundMask |= 1 << 9;
|
||||
|
||||
}
|
||||
EXPECT_EQ(FoundMask, (1 << 10) - 1);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, InsertRepeatedBytes1) {
|
||||
TestInsertRepeatedBytes(&MutationDispatcher::Mutate_InsertRepeatedBytes, 10000);
|
||||
}
|
||||
TEST(FuzzerMutate, InsertRepeatedBytes2) {
|
||||
TestInsertRepeatedBytes(&MutationDispatcher::Mutate, 300000);
|
||||
}
|
||||
|
||||
void TestChangeByte(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[8] = {0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH1[8] = {0x00, 0xF1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH2[8] = {0x00, 0x11, 0xF2, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH3[8] = {0x00, 0x11, 0x22, 0xF3, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0xF4, 0x55, 0x66, 0x77};
|
||||
uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF5, 0x66, 0x77};
|
||||
uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF5, 0x77};
|
||||
uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (*MD.*M)(T, 8, 9);
|
||||
if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4;
|
||||
if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ChangeByte1) {
|
||||
TestChangeByte(&MutationDispatcher::Mutate_ChangeByte, 1 << 15);
|
||||
}
|
||||
TEST(FuzzerMutate, ChangeByte2) {
|
||||
TestChangeByte(&MutationDispatcher::Mutate, 1 << 17);
|
||||
}
|
||||
|
||||
void TestChangeBit(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[8] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH1[8] = {0x00, 0x13, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH2[8] = {0x00, 0x11, 0x02, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH3[8] = {0x00, 0x11, 0x22, 0x37, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x54, 0x55, 0x66, 0x77};
|
||||
uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x54, 0x66, 0x77};
|
||||
uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x76, 0x77};
|
||||
uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (*MD.*M)(T, 8, 9);
|
||||
if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4;
|
||||
if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ChangeBit1) {
|
||||
TestChangeBit(&MutationDispatcher::Mutate_ChangeBit, 1 << 16);
|
||||
}
|
||||
TEST(FuzzerMutate, ChangeBit2) {
|
||||
TestChangeBit(&MutationDispatcher::Mutate, 1 << 18);
|
||||
}
|
||||
|
||||
void TestShuffleBytes(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[7] = {0x00, 0x22, 0x11, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t CH1[7] = {0x11, 0x00, 0x33, 0x22, 0x44, 0x55, 0x66};
|
||||
uint8_t CH2[7] = {0x00, 0x33, 0x11, 0x22, 0x44, 0x55, 0x66};
|
||||
uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x33};
|
||||
uint8_t CH4[7] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x44, 0x66};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
size_t NewSize = (*MD.*M)(T, 7, 7);
|
||||
if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 7 && !memcmp(CH4, T, 7)) FoundMask |= 1 << 4;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 31);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ShuffleBytes1) {
|
||||
TestShuffleBytes(&MutationDispatcher::Mutate_ShuffleBytes, 1 << 17);
|
||||
}
|
||||
TEST(FuzzerMutate, ShuffleBytes2) {
|
||||
TestShuffleBytes(&MutationDispatcher::Mutate, 1 << 20);
|
||||
}
|
||||
|
||||
void TestCopyPart(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11};
|
||||
uint8_t CH1[7] = {0x55, 0x66, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t CH2[7] = {0x00, 0x55, 0x66, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x66};
|
||||
uint8_t CH4[7] = {0x00, 0x11, 0x11, 0x22, 0x33, 0x55, 0x66};
|
||||
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
size_t NewSize = (*MD.*M)(T, 7, 7);
|
||||
if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 7 && !memcmp(CH4, T, 7)) FoundMask |= 1 << 4;
|
||||
}
|
||||
|
||||
uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11, 0x22};
|
||||
uint8_t CH6[8] = {0x22, 0x33, 0x44, 0x00, 0x11, 0x22, 0x33, 0x44};
|
||||
uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x33, 0x44};
|
||||
uint8_t CH8[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x22, 0x33, 0x44};
|
||||
uint8_t CH9[8] = {0x00, 0x11, 0x22, 0x22, 0x33, 0x44, 0x33, 0x44};
|
||||
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (*MD.*M)(T, 5, 8);
|
||||
if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
|
||||
if (NewSize == 8 && !memcmp(CH8, T, 8)) FoundMask |= 1 << 8;
|
||||
if (NewSize == 8 && !memcmp(CH9, T, 8)) FoundMask |= 1 << 9;
|
||||
}
|
||||
|
||||
EXPECT_EQ(FoundMask, 1023);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, CopyPart1) {
|
||||
TestCopyPart(&MutationDispatcher::Mutate_CopyPart, 1 << 10);
|
||||
}
|
||||
TEST(FuzzerMutate, CopyPart2) {
|
||||
TestCopyPart(&MutationDispatcher::Mutate, 1 << 13);
|
||||
}
|
||||
TEST(FuzzerMutate, CopyPartNoInsertAtMaxSize) {
|
||||
// This (non exhaustively) tests if `Mutate_CopyPart` tries to perform an
|
||||
// insert on an input of size `MaxSize`. Performing an insert in this case
|
||||
// will lead to the mutation failing.
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
|
||||
uint8_t Data[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11, 0x22};
|
||||
size_t MaxSize = sizeof(Data);
|
||||
for (int count = 0; count < (1 << 18); ++count) {
|
||||
size_t NewSize = MD->Mutate_CopyPart(Data, MaxSize, MaxSize);
|
||||
ASSERT_EQ(NewSize, MaxSize);
|
||||
}
|
||||
}
|
||||
|
||||
void TestAddWordFromDictionary(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
|
||||
uint8_t Word1[4] = {0xAA, 0xBB, 0xCC, 0xDD};
|
||||
uint8_t Word2[3] = {0xFF, 0xEE, 0xEF};
|
||||
MD->AddWordToManualDictionary(Word(Word1, sizeof(Word1)));
|
||||
MD->AddWordToManualDictionary(Word(Word2, sizeof(Word2)));
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[7] = {0x00, 0x11, 0x22, 0xAA, 0xBB, 0xCC, 0xDD};
|
||||
uint8_t CH1[7] = {0x00, 0x11, 0xAA, 0xBB, 0xCC, 0xDD, 0x22};
|
||||
uint8_t CH2[7] = {0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22};
|
||||
uint8_t CH3[7] = {0xAA, 0xBB, 0xCC, 0xDD, 0x00, 0x11, 0x22};
|
||||
uint8_t CH4[6] = {0x00, 0x11, 0x22, 0xFF, 0xEE, 0xEF};
|
||||
uint8_t CH5[6] = {0x00, 0x11, 0xFF, 0xEE, 0xEF, 0x22};
|
||||
uint8_t CH6[6] = {0x00, 0xFF, 0xEE, 0xEF, 0x11, 0x22};
|
||||
uint8_t CH7[6] = {0xFF, 0xEE, 0xEF, 0x00, 0x11, 0x22};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[7] = {0x00, 0x11, 0x22};
|
||||
size_t NewSize = (*MD.*M)(T, 3, 7);
|
||||
if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3;
|
||||
if (NewSize == 6 && !memcmp(CH4, T, 6)) FoundMask |= 1 << 4;
|
||||
if (NewSize == 6 && !memcmp(CH5, T, 6)) FoundMask |= 1 << 5;
|
||||
if (NewSize == 6 && !memcmp(CH6, T, 6)) FoundMask |= 1 << 6;
|
||||
if (NewSize == 6 && !memcmp(CH7, T, 6)) FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, AddWordFromDictionary1) {
|
||||
TestAddWordFromDictionary(
|
||||
&MutationDispatcher::Mutate_AddWordFromManualDictionary, 1 << 15);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, AddWordFromDictionary2) {
|
||||
TestAddWordFromDictionary(&MutationDispatcher::Mutate, 1 << 15);
|
||||
}
|
||||
|
||||
void TestChangeASCIIInteger(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
|
||||
|
||||
uint8_t CH0[8] = {'1', '2', '3', '4', '5', '6', '7', '7'};
|
||||
uint8_t CH1[8] = {'1', '2', '3', '4', '5', '6', '7', '9'};
|
||||
uint8_t CH2[8] = {'2', '4', '6', '9', '1', '3', '5', '6'};
|
||||
uint8_t CH3[8] = {'0', '6', '1', '7', '2', '8', '3', '9'};
|
||||
int FoundMask = 0;
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
|
||||
size_t NewSize = (*MD.*M)(T, 8, 8);
|
||||
/**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
|
||||
else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
|
||||
else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
|
||||
else if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
|
||||
else if (NewSize == 8) FoundMask |= 1 << 4;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 31);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ChangeASCIIInteger1) {
|
||||
TestChangeASCIIInteger(&MutationDispatcher::Mutate_ChangeASCIIInteger,
|
||||
1 << 15);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ChangeASCIIInteger2) {
|
||||
TestChangeASCIIInteger(&MutationDispatcher::Mutate, 1 << 15);
|
||||
}
|
||||
|
||||
void TestChangeBinaryInteger(Mutator M, int NumIter) {
|
||||
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
|
||||
fuzzer::EF = t.get();
|
||||
Random Rand(0);
|
||||
std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
|
||||
|
||||
uint8_t CH0[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x79};
|
||||
uint8_t CH1[8] = {0x00, 0x11, 0x22, 0x31, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH2[8] = {0xff, 0x10, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH3[8] = {0x00, 0x11, 0x2a, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x4f, 0x66, 0x77};
|
||||
uint8_t CH5[8] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88};
|
||||
uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x00, 0x00, 0x00, 0x08, 0x77}; // Size
|
||||
uint8_t CH7[8] = {0x00, 0x08, 0x00, 0x33, 0x44, 0x55, 0x66, 0x77}; // Sw(Size)
|
||||
|
||||
int FoundMask = 0;
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (*MD.*M)(T, 8, 8);
|
||||
/**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
|
||||
else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
|
||||
else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
|
||||
else if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
|
||||
else if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4;
|
||||
else if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
|
||||
else if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
|
||||
else if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ChangeBinaryInteger1) {
|
||||
TestChangeBinaryInteger(&MutationDispatcher::Mutate_ChangeBinaryInteger,
|
||||
1 << 12);
|
||||
}
|
||||
|
||||
TEST(FuzzerMutate, ChangeBinaryInteger2) {
|
||||
TestChangeBinaryInteger(&MutationDispatcher::Mutate, 1 << 15);
|
||||
}
|
||||
|
||||
|
||||
TEST(FuzzerDictionary, ParseOneDictionaryEntry) {
|
||||
Unit U;
|
||||
EXPECT_FALSE(ParseOneDictionaryEntry("", &U));
|
||||
|
|
|
@ -0,0 +1,961 @@
|
|||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "mutagen/Mutagen.h"
|
||||
#include "mutagen/MutagenDispatcher.h"
|
||||
#include "mutagen/MutagenSequence.h"
|
||||
#include "mutagen/MutagenUtil.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <chrono>
|
||||
|
||||
namespace mutagen {
|
||||
namespace {
|
||||
|
||||
using fuzzer::Set;
|
||||
|
||||
std::unique_ptr<MutationDispatcher> CreateMutationDispatcher() {
|
||||
LLVMMutagenConfiguration Config;
|
||||
memset(&Config, 0, sizeof(Config));
|
||||
return std::make_unique<MutationDispatcher>(&Config);
|
||||
}
|
||||
|
||||
typedef size_t (MutationDispatcher::*Mutator)(uint8_t *Data, size_t Size,
|
||||
size_t MaxSize);
|
||||
|
||||
TEST(MutationDispatcher, CrossOver) {
|
||||
auto MD = CreateMutationDispatcher();
|
||||
Unit A({0, 1, 2}), B({5, 6, 7});
|
||||
Unit C;
|
||||
Unit Expected[] = {{0},
|
||||
{0, 1},
|
||||
{0, 5},
|
||||
{0, 1, 2},
|
||||
{0, 1, 5},
|
||||
{0, 5, 1},
|
||||
{0, 5, 6},
|
||||
{0, 1, 2, 5},
|
||||
{0, 1, 5, 2},
|
||||
{0, 1, 5, 6},
|
||||
{0, 5, 1, 2},
|
||||
{0, 5, 1, 6},
|
||||
{0, 5, 6, 1},
|
||||
{0, 5, 6, 7},
|
||||
{0, 1, 2, 5, 6},
|
||||
{0, 1, 5, 2, 6},
|
||||
{0, 1, 5, 6, 2},
|
||||
{0, 1, 5, 6, 7},
|
||||
{0, 5, 1, 2, 6},
|
||||
{0, 5, 1, 6, 2},
|
||||
{0, 5, 1, 6, 7},
|
||||
{0, 5, 6, 1, 2},
|
||||
{0, 5, 6, 1, 7},
|
||||
{0, 5, 6, 7, 1},
|
||||
{0, 1, 2, 5, 6, 7},
|
||||
{0, 1, 5, 2, 6, 7},
|
||||
{0, 1, 5, 6, 2, 7},
|
||||
{0, 1, 5, 6, 7, 2},
|
||||
{0, 5, 1, 2, 6, 7},
|
||||
{0, 5, 1, 6, 2, 7},
|
||||
{0, 5, 1, 6, 7, 2},
|
||||
{0, 5, 6, 1, 2, 7},
|
||||
{0, 5, 6, 1, 7, 2},
|
||||
{0, 5, 6, 7, 1, 2}};
|
||||
for (size_t Len = 1; Len < 8; Len++) {
|
||||
Set<Unit> FoundUnits, ExpectedUnitsWitThisLength;
|
||||
for (int Iter = 0; Iter < 3000; Iter++) {
|
||||
C.resize(Len);
|
||||
size_t NewSize = MD->CrossOver(A.data(), A.size(), B.data(), B.size(),
|
||||
C.data(), C.size());
|
||||
C.resize(NewSize);
|
||||
FoundUnits.insert(C);
|
||||
}
|
||||
for (const Unit &U : Expected)
|
||||
if (U.size() <= Len)
|
||||
ExpectedUnitsWitThisLength.insert(U);
|
||||
EXPECT_EQ(ExpectedUnitsWitThisLength, FoundUnits);
|
||||
}
|
||||
}
|
||||
|
||||
void TestEraseBytes(Mutator M, int NumIter) {
|
||||
uint8_t REM0[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM1[8] = {0x00, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM2[8] = {0x00, 0x11, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM3[8] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM4[8] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x66, 0x77};
|
||||
uint8_t REM5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x66, 0x77};
|
||||
uint8_t REM6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x77};
|
||||
uint8_t REM7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
|
||||
uint8_t REM8[6] = {0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM9[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
|
||||
uint8_t REM10[6] = {0x00, 0x11, 0x22, 0x55, 0x66, 0x77};
|
||||
|
||||
uint8_t REM11[5] = {0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t REM12[5] = {0x00, 0x11, 0x22, 0x33, 0x44};
|
||||
uint8_t REM13[5] = {0x00, 0x44, 0x55, 0x66, 0x77};
|
||||
|
||||
auto MD = CreateMutationDispatcher();
|
||||
int FoundMask = 0;
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (*MD.*M)(T, sizeof(T), sizeof(T));
|
||||
if (NewSize == 7 && !memcmp(REM0, T, 7))
|
||||
FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(REM1, T, 7))
|
||||
FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(REM2, T, 7))
|
||||
FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(REM3, T, 7))
|
||||
FoundMask |= 1 << 3;
|
||||
if (NewSize == 7 && !memcmp(REM4, T, 7))
|
||||
FoundMask |= 1 << 4;
|
||||
if (NewSize == 7 && !memcmp(REM5, T, 7))
|
||||
FoundMask |= 1 << 5;
|
||||
if (NewSize == 7 && !memcmp(REM6, T, 7))
|
||||
FoundMask |= 1 << 6;
|
||||
if (NewSize == 7 && !memcmp(REM7, T, 7))
|
||||
FoundMask |= 1 << 7;
|
||||
|
||||
if (NewSize == 6 && !memcmp(REM8, T, 6))
|
||||
FoundMask |= 1 << 8;
|
||||
if (NewSize == 6 && !memcmp(REM9, T, 6))
|
||||
FoundMask |= 1 << 9;
|
||||
if (NewSize == 6 && !memcmp(REM10, T, 6))
|
||||
FoundMask |= 1 << 10;
|
||||
|
||||
if (NewSize == 5 && !memcmp(REM11, T, 5))
|
||||
FoundMask |= 1 << 11;
|
||||
if (NewSize == 5 && !memcmp(REM12, T, 5))
|
||||
FoundMask |= 1 << 12;
|
||||
if (NewSize == 5 && !memcmp(REM13, T, 5))
|
||||
FoundMask |= 1 << 13;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, (1 << 14) - 1);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, EraseBytes1) {
|
||||
TestEraseBytes(&MutationDispatcher::Mutate_EraseBytes, 200);
|
||||
}
|
||||
TEST(MutationDispatcher, EraseBytes2) {
|
||||
TestEraseBytes(&MutationDispatcher::Mutate, 2000);
|
||||
}
|
||||
|
||||
void TestInsertByte(Mutator M, int NumIter) {
|
||||
auto MD = CreateMutationDispatcher();
|
||||
int FoundMask = 0;
|
||||
uint8_t INS0[8] = {0xF1, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t INS1[8] = {0x00, 0xF2, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t INS2[8] = {0x00, 0x11, 0xF3, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t INS3[8] = {0x00, 0x11, 0x22, 0xF4, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t INS4[8] = {0x00, 0x11, 0x22, 0x33, 0xF5, 0x44, 0x55, 0x66};
|
||||
uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF6, 0x55, 0x66};
|
||||
uint8_t INS6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF7, 0x66};
|
||||
uint8_t INS7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF8};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
size_t NewSize = (*MD.*M)(T, 7, 8);
|
||||
if (NewSize == 8 && !memcmp(INS0, T, 8))
|
||||
FoundMask |= 1 << 0;
|
||||
if (NewSize == 8 && !memcmp(INS1, T, 8))
|
||||
FoundMask |= 1 << 1;
|
||||
if (NewSize == 8 && !memcmp(INS2, T, 8))
|
||||
FoundMask |= 1 << 2;
|
||||
if (NewSize == 8 && !memcmp(INS3, T, 8))
|
||||
FoundMask |= 1 << 3;
|
||||
if (NewSize == 8 && !memcmp(INS4, T, 8))
|
||||
FoundMask |= 1 << 4;
|
||||
if (NewSize == 8 && !memcmp(INS5, T, 8))
|
||||
FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(INS6, T, 8))
|
||||
FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(INS7, T, 8))
|
||||
FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, InsertByte1) {
|
||||
TestInsertByte(&MutationDispatcher::Mutate_InsertByte, 1 << 15);
|
||||
}
|
||||
TEST(MutationDispatcher, InsertByte2) {
|
||||
TestInsertByte(&MutationDispatcher::Mutate, 1 << 17);
|
||||
}
|
||||
|
||||
void TestInsertRepeatedBytes(Mutator M, int NumIter) {
|
||||
auto MD = CreateMutationDispatcher();
|
||||
int FoundMask = 0;
|
||||
uint8_t INS0[7] = {0x00, 0x11, 0x22, 0x33, 'a', 'a', 'a'};
|
||||
uint8_t INS1[7] = {0x00, 0x11, 0x22, 'a', 'a', 'a', 0x33};
|
||||
uint8_t INS2[7] = {0x00, 0x11, 'a', 'a', 'a', 0x22, 0x33};
|
||||
uint8_t INS3[7] = {0x00, 'a', 'a', 'a', 0x11, 0x22, 0x33};
|
||||
uint8_t INS4[7] = {'a', 'a', 'a', 0x00, 0x11, 0x22, 0x33};
|
||||
|
||||
uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 'b', 'b', 'b', 'b'};
|
||||
uint8_t INS6[8] = {0x00, 0x11, 0x22, 'b', 'b', 'b', 'b', 0x33};
|
||||
uint8_t INS7[8] = {0x00, 0x11, 'b', 'b', 'b', 'b', 0x22, 0x33};
|
||||
uint8_t INS8[8] = {0x00, 'b', 'b', 'b', 'b', 0x11, 0x22, 0x33};
|
||||
uint8_t INS9[8] = {'b', 'b', 'b', 'b', 0x00, 0x11, 0x22, 0x33};
|
||||
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33};
|
||||
size_t NewSize = (*MD.*M)(T, 4, 8);
|
||||
if (NewSize == 7 && !memcmp(INS0, T, 7))
|
||||
FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(INS1, T, 7))
|
||||
FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(INS2, T, 7))
|
||||
FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(INS3, T, 7))
|
||||
FoundMask |= 1 << 3;
|
||||
if (NewSize == 7 && !memcmp(INS4, T, 7))
|
||||
FoundMask |= 1 << 4;
|
||||
|
||||
if (NewSize == 8 && !memcmp(INS5, T, 8))
|
||||
FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(INS6, T, 8))
|
||||
FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(INS7, T, 8))
|
||||
FoundMask |= 1 << 7;
|
||||
if (NewSize == 8 && !memcmp(INS8, T, 8))
|
||||
FoundMask |= 1 << 8;
|
||||
if (NewSize == 8 && !memcmp(INS9, T, 8))
|
||||
FoundMask |= 1 << 9;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, (1 << 10) - 1);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, InsertRepeatedBytes1) {
|
||||
TestInsertRepeatedBytes(&MutationDispatcher::Mutate_InsertRepeatedBytes,
|
||||
10000);
|
||||
}
|
||||
TEST(MutationDispatcher, InsertRepeatedBytes2) {
|
||||
TestInsertRepeatedBytes(&MutationDispatcher::Mutate, 300000);
|
||||
}
|
||||
|
||||
void TestChangeByte(Mutator M, int NumIter) {
|
||||
auto MD = CreateMutationDispatcher();
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[8] = {0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH1[8] = {0x00, 0xF1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH2[8] = {0x00, 0x11, 0xF2, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH3[8] = {0x00, 0x11, 0x22, 0xF3, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0xF4, 0x55, 0x66, 0x77};
|
||||
uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF5, 0x66, 0x77};
|
||||
uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF5, 0x77};
|
||||
uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (*MD.*M)(T, 8, 9);
|
||||
if (NewSize == 8 && !memcmp(CH0, T, 8))
|
||||
FoundMask |= 1 << 0;
|
||||
if (NewSize == 8 && !memcmp(CH1, T, 8))
|
||||
FoundMask |= 1 << 1;
|
||||
if (NewSize == 8 && !memcmp(CH2, T, 8))
|
||||
FoundMask |= 1 << 2;
|
||||
if (NewSize == 8 && !memcmp(CH3, T, 8))
|
||||
FoundMask |= 1 << 3;
|
||||
if (NewSize == 8 && !memcmp(CH4, T, 8))
|
||||
FoundMask |= 1 << 4;
|
||||
if (NewSize == 8 && !memcmp(CH5, T, 8))
|
||||
FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(CH6, T, 8))
|
||||
FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(CH7, T, 8))
|
||||
FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, ChangeByte1) {
|
||||
TestChangeByte(&MutationDispatcher::Mutate_ChangeByte, 1 << 15);
|
||||
}
|
||||
TEST(MutationDispatcher, ChangeByte2) {
|
||||
TestChangeByte(&MutationDispatcher::Mutate, 1 << 17);
|
||||
}
|
||||
|
||||
void TestChangeBit(Mutator M, int NumIter) {
|
||||
auto MD = CreateMutationDispatcher();
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[8] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH1[8] = {0x00, 0x13, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH2[8] = {0x00, 0x11, 0x02, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH3[8] = {0x00, 0x11, 0x22, 0x37, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x54, 0x55, 0x66, 0x77};
|
||||
uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x54, 0x66, 0x77};
|
||||
uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x76, 0x77};
|
||||
uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (*MD.*M)(T, 8, 9);
|
||||
if (NewSize == 8 && !memcmp(CH0, T, 8))
|
||||
FoundMask |= 1 << 0;
|
||||
if (NewSize == 8 && !memcmp(CH1, T, 8))
|
||||
FoundMask |= 1 << 1;
|
||||
if (NewSize == 8 && !memcmp(CH2, T, 8))
|
||||
FoundMask |= 1 << 2;
|
||||
if (NewSize == 8 && !memcmp(CH3, T, 8))
|
||||
FoundMask |= 1 << 3;
|
||||
if (NewSize == 8 && !memcmp(CH4, T, 8))
|
||||
FoundMask |= 1 << 4;
|
||||
if (NewSize == 8 && !memcmp(CH5, T, 8))
|
||||
FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(CH6, T, 8))
|
||||
FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(CH7, T, 8))
|
||||
FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, ChangeBit1) {
|
||||
TestChangeBit(&MutationDispatcher::Mutate_ChangeBit, 1 << 16);
|
||||
}
|
||||
TEST(MutationDispatcher, ChangeBit2) {
|
||||
TestChangeBit(&MutationDispatcher::Mutate, 1 << 18);
|
||||
}
|
||||
|
||||
void TestShuffleBytes(Mutator M, int NumIter) {
|
||||
auto MD = CreateMutationDispatcher();
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[7] = {0x00, 0x22, 0x11, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t CH1[7] = {0x11, 0x00, 0x33, 0x22, 0x44, 0x55, 0x66};
|
||||
uint8_t CH2[7] = {0x00, 0x33, 0x11, 0x22, 0x44, 0x55, 0x66};
|
||||
uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x33};
|
||||
uint8_t CH4[7] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x44, 0x66};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
size_t NewSize = (*MD.*M)(T, 7, 7);
|
||||
if (NewSize == 7 && !memcmp(CH0, T, 7))
|
||||
FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(CH1, T, 7))
|
||||
FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(CH2, T, 7))
|
||||
FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(CH3, T, 7))
|
||||
FoundMask |= 1 << 3;
|
||||
if (NewSize == 7 && !memcmp(CH4, T, 7))
|
||||
FoundMask |= 1 << 4;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 31);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, ShuffleBytes1) {
|
||||
TestShuffleBytes(&MutationDispatcher::Mutate_ShuffleBytes, 1 << 17);
|
||||
}
|
||||
TEST(MutationDispatcher, ShuffleBytes2) {
|
||||
TestShuffleBytes(&MutationDispatcher::Mutate, 1 << 20);
|
||||
}
|
||||
|
||||
void TestCopyPart(Mutator M, int NumIter) {
|
||||
auto MD = CreateMutationDispatcher();
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11};
|
||||
uint8_t CH1[7] = {0x55, 0x66, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t CH2[7] = {0x00, 0x55, 0x66, 0x33, 0x44, 0x55, 0x66};
|
||||
uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x66};
|
||||
uint8_t CH4[7] = {0x00, 0x11, 0x11, 0x22, 0x33, 0x55, 0x66};
|
||||
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
size_t NewSize = (*MD.*M)(T, 7, 7);
|
||||
if (NewSize == 7 && !memcmp(CH0, T, 7))
|
||||
FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(CH1, T, 7))
|
||||
FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(CH2, T, 7))
|
||||
FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(CH3, T, 7))
|
||||
FoundMask |= 1 << 3;
|
||||
if (NewSize == 7 && !memcmp(CH4, T, 7))
|
||||
FoundMask |= 1 << 4;
|
||||
}
|
||||
|
||||
uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11, 0x22};
|
||||
uint8_t CH6[8] = {0x22, 0x33, 0x44, 0x00, 0x11, 0x22, 0x33, 0x44};
|
||||
uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x33, 0x44};
|
||||
uint8_t CH8[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x22, 0x33, 0x44};
|
||||
uint8_t CH9[8] = {0x00, 0x11, 0x22, 0x22, 0x33, 0x44, 0x33, 0x44};
|
||||
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (*MD.*M)(T, 5, 8);
|
||||
if (NewSize == 8 && !memcmp(CH5, T, 8))
|
||||
FoundMask |= 1 << 5;
|
||||
if (NewSize == 8 && !memcmp(CH6, T, 8))
|
||||
FoundMask |= 1 << 6;
|
||||
if (NewSize == 8 && !memcmp(CH7, T, 8))
|
||||
FoundMask |= 1 << 7;
|
||||
if (NewSize == 8 && !memcmp(CH8, T, 8))
|
||||
FoundMask |= 1 << 8;
|
||||
if (NewSize == 8 && !memcmp(CH9, T, 8))
|
||||
FoundMask |= 1 << 9;
|
||||
}
|
||||
|
||||
EXPECT_EQ(FoundMask, 1023);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, CopyPart1) {
|
||||
TestCopyPart(&MutationDispatcher::Mutate_CopyPart, 1 << 10);
|
||||
}
|
||||
TEST(MutationDispatcher, CopyPart2) {
|
||||
TestCopyPart(&MutationDispatcher::Mutate, 1 << 13);
|
||||
}
|
||||
TEST(MutationDispatcher, CopyPartNoInsertAtMaxSize) {
|
||||
// This (non exhaustively) tests if `Mutate_CopyPart` tries to perform an
|
||||
// insert on an input of size `MaxSize`. Performing an insert in this case
|
||||
// will lead to the mutation failing.
|
||||
auto MD = CreateMutationDispatcher();
|
||||
uint8_t Data[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11, 0x22};
|
||||
size_t MaxSize = sizeof(Data);
|
||||
for (int count = 0; count < (1 << 18); ++count) {
|
||||
size_t NewSize = MD->Mutate_CopyPart(Data, MaxSize, MaxSize);
|
||||
ASSERT_EQ(NewSize, MaxSize);
|
||||
}
|
||||
}
|
||||
|
||||
void TestAddWordFromDictionary(Mutator M, int NumIter) {
|
||||
auto MD = CreateMutationDispatcher();
|
||||
uint8_t Word1[4] = {0xAA, 0xBB, 0xCC, 0xDD};
|
||||
uint8_t Word2[3] = {0xFF, 0xEE, 0xEF};
|
||||
MD->AddWordToManualDictionary(Word(Word1, sizeof(Word1)));
|
||||
MD->AddWordToManualDictionary(Word(Word2, sizeof(Word2)));
|
||||
int FoundMask = 0;
|
||||
uint8_t CH0[7] = {0x00, 0x11, 0x22, 0xAA, 0xBB, 0xCC, 0xDD};
|
||||
uint8_t CH1[7] = {0x00, 0x11, 0xAA, 0xBB, 0xCC, 0xDD, 0x22};
|
||||
uint8_t CH2[7] = {0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22};
|
||||
uint8_t CH3[7] = {0xAA, 0xBB, 0xCC, 0xDD, 0x00, 0x11, 0x22};
|
||||
uint8_t CH4[6] = {0x00, 0x11, 0x22, 0xFF, 0xEE, 0xEF};
|
||||
uint8_t CH5[6] = {0x00, 0x11, 0xFF, 0xEE, 0xEF, 0x22};
|
||||
uint8_t CH6[6] = {0x00, 0xFF, 0xEE, 0xEF, 0x11, 0x22};
|
||||
uint8_t CH7[6] = {0xFF, 0xEE, 0xEF, 0x00, 0x11, 0x22};
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[7] = {0x00, 0x11, 0x22};
|
||||
size_t NewSize = (*MD.*M)(T, 3, 7);
|
||||
if (NewSize == 7 && !memcmp(CH0, T, 7))
|
||||
FoundMask |= 1 << 0;
|
||||
if (NewSize == 7 && !memcmp(CH1, T, 7))
|
||||
FoundMask |= 1 << 1;
|
||||
if (NewSize == 7 && !memcmp(CH2, T, 7))
|
||||
FoundMask |= 1 << 2;
|
||||
if (NewSize == 7 && !memcmp(CH3, T, 7))
|
||||
FoundMask |= 1 << 3;
|
||||
if (NewSize == 6 && !memcmp(CH4, T, 6))
|
||||
FoundMask |= 1 << 4;
|
||||
if (NewSize == 6 && !memcmp(CH5, T, 6))
|
||||
FoundMask |= 1 << 5;
|
||||
if (NewSize == 6 && !memcmp(CH6, T, 6))
|
||||
FoundMask |= 1 << 6;
|
||||
if (NewSize == 6 && !memcmp(CH7, T, 6))
|
||||
FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, AddWordFromDictionary1) {
|
||||
TestAddWordFromDictionary(
|
||||
&MutationDispatcher::Mutate_AddWordFromManualDictionary, 1 << 15);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, AddWordFromDictionary2) {
|
||||
TestAddWordFromDictionary(&MutationDispatcher::Mutate, 1 << 15);
|
||||
}
|
||||
|
||||
void TestChangeASCIIInteger(Mutator M, int NumIter) {
|
||||
auto MD = CreateMutationDispatcher();
|
||||
|
||||
uint8_t CH0[8] = {'1', '2', '3', '4', '5', '6', '7', '7'};
|
||||
uint8_t CH1[8] = {'1', '2', '3', '4', '5', '6', '7', '9'};
|
||||
uint8_t CH2[8] = {'2', '4', '6', '9', '1', '3', '5', '6'};
|
||||
uint8_t CH3[8] = {'0', '6', '1', '7', '2', '8', '3', '9'};
|
||||
int FoundMask = 0;
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
|
||||
size_t NewSize = (*MD.*M)(T, 8, 8);
|
||||
/**/ if (NewSize == 8 && !memcmp(CH0, T, 8))
|
||||
FoundMask |= 1 << 0;
|
||||
else if (NewSize == 8 && !memcmp(CH1, T, 8))
|
||||
FoundMask |= 1 << 1;
|
||||
else if (NewSize == 8 && !memcmp(CH2, T, 8))
|
||||
FoundMask |= 1 << 2;
|
||||
else if (NewSize == 8 && !memcmp(CH3, T, 8))
|
||||
FoundMask |= 1 << 3;
|
||||
else if (NewSize == 8)
|
||||
FoundMask |= 1 << 4;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 31);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, ChangeASCIIInteger1) {
|
||||
TestChangeASCIIInteger(&MutationDispatcher::Mutate_ChangeASCIIInteger,
|
||||
1 << 15);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, ChangeASCIIInteger2) {
|
||||
TestChangeASCIIInteger(&MutationDispatcher::Mutate, 1 << 15);
|
||||
}
|
||||
|
||||
void TestChangeBinaryInteger(Mutator M, int NumIter) {
|
||||
auto MD = CreateMutationDispatcher();
|
||||
|
||||
uint8_t CH0[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x79};
|
||||
uint8_t CH1[8] = {0x00, 0x11, 0x22, 0x31, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH2[8] = {0xff, 0x10, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH3[8] = {0x00, 0x11, 0x2a, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x4f, 0x66, 0x77};
|
||||
uint8_t CH5[8] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88};
|
||||
uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x00, 0x00, 0x00, 0x08, 0x77}; // Size
|
||||
uint8_t CH7[8] = {0x00, 0x08, 0x00, 0x33, 0x44, 0x55, 0x66, 0x77}; // Sw(Size)
|
||||
|
||||
int FoundMask = 0;
|
||||
for (int i = 0; i < NumIter; i++) {
|
||||
uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
|
||||
size_t NewSize = (*MD.*M)(T, 8, 8);
|
||||
/**/ if (NewSize == 8 && !memcmp(CH0, T, 8))
|
||||
FoundMask |= 1 << 0;
|
||||
else if (NewSize == 8 && !memcmp(CH1, T, 8))
|
||||
FoundMask |= 1 << 1;
|
||||
else if (NewSize == 8 && !memcmp(CH2, T, 8))
|
||||
FoundMask |= 1 << 2;
|
||||
else if (NewSize == 8 && !memcmp(CH3, T, 8))
|
||||
FoundMask |= 1 << 3;
|
||||
else if (NewSize == 8 && !memcmp(CH4, T, 8))
|
||||
FoundMask |= 1 << 4;
|
||||
else if (NewSize == 8 && !memcmp(CH5, T, 8))
|
||||
FoundMask |= 1 << 5;
|
||||
else if (NewSize == 8 && !memcmp(CH6, T, 8))
|
||||
FoundMask |= 1 << 6;
|
||||
else if (NewSize == 8 && !memcmp(CH7, T, 8))
|
||||
FoundMask |= 1 << 7;
|
||||
}
|
||||
EXPECT_EQ(FoundMask, 255);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, ChangeBinaryInteger1) {
|
||||
TestChangeBinaryInteger(&MutationDispatcher::Mutate_ChangeBinaryInteger,
|
||||
1 << 12);
|
||||
}
|
||||
|
||||
TEST(MutationDispatcher, ChangeBinaryInteger2) {
|
||||
TestChangeBinaryInteger(&MutationDispatcher::Mutate, 1 << 15);
|
||||
}
|
||||
|
||||
// Test fixture for MutagenInterface unit tests.
|
||||
static const char *kWord1 = "word1";
|
||||
static const char *kWord2 = "word2";
|
||||
|
||||
class MutagenInterface : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
Current = this;
|
||||
|
||||
Config.Seed = 1;
|
||||
|
||||
Config.UseCmp = 1;
|
||||
Config.FromTORC4 = [](size_t Idx, uint32_t *Arg1, uint32_t *Arg2) {
|
||||
++(Current->FromTORC4Calls);
|
||||
*Arg1 = 0x0401;
|
||||
*Arg2 = 0x0402;
|
||||
};
|
||||
Config.FromTORC8 = [](size_t Idx, uint64_t *Arg1, uint64_t *Arg2) {
|
||||
++(Current->FromTORC8Calls);
|
||||
*Arg1 = 0x0801;
|
||||
*Arg2 = 0x0802;
|
||||
};
|
||||
Config.FromTORCW = [](size_t Idx, const uint8_t **Data1, size_t *Size1,
|
||||
const uint8_t **Data2, size_t *Size2) {
|
||||
++(Current->FromTORCWCalls);
|
||||
*Data1 = reinterpret_cast<const uint8_t *>(kWord1);
|
||||
*Size1 = strlen(kWord1);
|
||||
*Data2 = reinterpret_cast<const uint8_t *>(kWord2);
|
||||
*Size2 = strlen(kWord2);
|
||||
};
|
||||
|
||||
Config.UseMemmem = 0;
|
||||
Config.FromMMT = [](size_t Idx, const uint8_t **Data, size_t *Size) {
|
||||
++(Current->FromMMTCalls);
|
||||
*Data = reinterpret_cast<const uint8_t *>(kWord1);
|
||||
*Size = strlen(kWord1);
|
||||
};
|
||||
|
||||
Config.OnlyASCII = 0;
|
||||
|
||||
Config.CustomMutator = [](uint8_t *Data, size_t Size, size_t MaxSize,
|
||||
unsigned int Seed) {
|
||||
++(Current->CustomMutatorCalls);
|
||||
return LLVMMutagenDefaultMutate(Data, Size, MaxSize);
|
||||
};
|
||||
|
||||
Config.CustomCrossOver =
|
||||
[](const uint8_t *Data1, size_t Size1, const uint8_t *Data2,
|
||||
size_t Size2, uint8_t *Out, size_t MaxOutSize, unsigned int Seed) {
|
||||
++(Current->CustomCrossOverCalls);
|
||||
auto *MD = GetMutationDispatcherForTest();
|
||||
return MD->CrossOver(Data1, Size1, Data2, Size2, Out, MaxOutSize);
|
||||
};
|
||||
|
||||
U = Unit({1, 2, 3, 4});
|
||||
U.reserve(8);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
Current = nullptr;
|
||||
memset(&Config, 0, sizeof(Config));
|
||||
LLVMMutagenConfigure(&Config);
|
||||
}
|
||||
|
||||
LLVMMutagenConfiguration Config;
|
||||
Unit U;
|
||||
|
||||
size_t FromTORC4Calls = 0;
|
||||
size_t FromTORC8Calls = 0;
|
||||
size_t FromTORCWCalls = 0;
|
||||
size_t FromMMTCalls = 0;
|
||||
size_t CustomMutatorCalls = 0;
|
||||
size_t CustomCrossOverCalls = 0;
|
||||
|
||||
private:
|
||||
static MutagenInterface *Current;
|
||||
};
|
||||
|
||||
MutagenInterface *MutagenInterface::Current = nullptr;
|
||||
|
||||
// Unit tests for MutagenInterface.
|
||||
|
||||
TEST_F(MutagenInterface, Configure) {
|
||||
Config.OnlyASCII = 1;
|
||||
LLVMMutagenConfigure(&Config);
|
||||
auto *MD = GetMutationDispatcherForTest();
|
||||
ASSERT_NE(MD, nullptr);
|
||||
|
||||
Random Rand1(Config.Seed);
|
||||
Random &Rand2 = MD->GetRand();
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
EXPECT_EQ(Rand1(), Rand2());
|
||||
|
||||
Config.Seed = static_cast<unsigned>(
|
||||
std::chrono::system_clock::now().time_since_epoch().count());
|
||||
Config.OnlyASCII = 0;
|
||||
LLVMMutagenConfigure(&Config);
|
||||
MD = GetMutationDispatcherForTest();
|
||||
ASSERT_NE(MD, nullptr);
|
||||
|
||||
Random Rand3(Config.Seed);
|
||||
Random &Rand4 = MD->GetRand();
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
EXPECT_EQ(Rand3(), Rand4());
|
||||
}
|
||||
|
||||
TEST_F(MutagenInterface, UseTORCs) {
|
||||
// If !UseCmp, none of the TORC/MMT callbacks are called, regardless of
|
||||
// UseMemmem.
|
||||
Config.UseCmp = 0;
|
||||
Config.UseMemmem = 1;
|
||||
LLVMMutagenConfigure(&Config);
|
||||
for (size_t i = 0; i < 200; ++i)
|
||||
LLVMMutagenMutate(U.data(), U.size(), U.capacity());
|
||||
EXPECT_EQ(FromTORC4Calls, 0U);
|
||||
EXPECT_EQ(FromTORC8Calls, 0U);
|
||||
EXPECT_EQ(FromTORCWCalls, 0U);
|
||||
EXPECT_EQ(FromMMTCalls, 0U);
|
||||
|
||||
// If UseCmp, but !UseMemmem, only the TORC callbacks are invoked.
|
||||
Config.UseCmp = 1;
|
||||
Config.UseMemmem = 0;
|
||||
LLVMMutagenConfigure(&Config);
|
||||
for (size_t i = 0; i < 200; ++i)
|
||||
LLVMMutagenMutate(U.data(), U.size(), U.capacity());
|
||||
EXPECT_NE(FromTORC4Calls, 0U);
|
||||
EXPECT_NE(FromTORC8Calls, 0U);
|
||||
EXPECT_NE(FromTORCWCalls, 0U);
|
||||
EXPECT_EQ(FromMMTCalls, 0U);
|
||||
|
||||
// If UseCmp and UseMemmem, all the TORC/MMT callbacks are invoked.
|
||||
Config.UseCmp = 1;
|
||||
Config.UseMemmem = 1;
|
||||
LLVMMutagenConfigure(&Config);
|
||||
for (size_t i = 0; i < 200; ++i)
|
||||
LLVMMutagenMutate(U.data(), U.size(), U.capacity());
|
||||
EXPECT_NE(FromTORC4Calls, 0U);
|
||||
EXPECT_NE(FromTORC8Calls, 0U);
|
||||
EXPECT_NE(FromTORCWCalls, 0U);
|
||||
EXPECT_NE(FromMMTCalls, 0U);
|
||||
}
|
||||
|
||||
TEST_F(MutagenInterface, CustomCallbacks) {
|
||||
// DefaultMutate never selects custom callbacks.
|
||||
LLVMMutagenConfigure(&Config);
|
||||
for (size_t i = 0; i < 200; ++i)
|
||||
LLVMMutagenDefaultMutate(U.data(), U.size(), U.capacity());
|
||||
|
||||
// Valid.
|
||||
auto *MD = GetMutationDispatcherForTest();
|
||||
EXPECT_EQ(CustomMutatorCalls, 0U);
|
||||
MD->Mutate_Custom(U.data(), U.size(), U.capacity());
|
||||
EXPECT_EQ(CustomMutatorCalls, 1U);
|
||||
|
||||
// Null cross-over input disables CustomCrossOver.
|
||||
LLVMMutagenSetCrossOverWith(nullptr, 0);
|
||||
MD->Mutate_CustomCrossOver(U.data(), U.size(), U.capacity());
|
||||
EXPECT_EQ(CustomCrossOverCalls, 0U);
|
||||
|
||||
// Zero-length cross-over input disables CustomCrossOver.
|
||||
Unit CrossOverWith = {4, 3, 2, 1};
|
||||
LLVMMutagenSetCrossOverWith(CrossOverWith.data(), 0);
|
||||
MD->Mutate_CustomCrossOver(U.data(), U.size(), U.capacity());
|
||||
EXPECT_EQ(CustomCrossOverCalls, 0U);
|
||||
|
||||
// Valid.
|
||||
LLVMMutagenSetCrossOverWith(CrossOverWith.data(), CrossOverWith.size());
|
||||
MD->Mutate_CustomCrossOver(U.data(), U.size(), U.capacity());
|
||||
EXPECT_EQ(CustomCrossOverCalls, 1U);
|
||||
|
||||
// Can mutate without custom callbacks.
|
||||
Config.CustomMutator = nullptr;
|
||||
Config.CustomCrossOver = nullptr;
|
||||
LLVMMutagenConfigure(&Config);
|
||||
for (size_t i = 0; i < 200; ++i)
|
||||
LLVMMutagenMutate(U.data(), U.size(), U.capacity());
|
||||
}
|
||||
|
||||
TEST_F(MutagenInterface, MutationSequence) {
|
||||
LLVMMutagenConfigure(&Config);
|
||||
char Buf[1024];
|
||||
size_t NumItems;
|
||||
|
||||
Set<std::string> Names = {
|
||||
"ShuffleBytes", "EraseBytes", "InsertBytes", "InsertRepeatedBytes",
|
||||
"ChangeByte", "ChangeBit", "CopyPart", "ChangeASCIIInt",
|
||||
"ChangeBinInt",
|
||||
};
|
||||
std::string Name;
|
||||
std::istringstream ISS;
|
||||
|
||||
// Empty sequences
|
||||
auto Size = LLVMMutagenGetMutationSequence(true, Buf, sizeof(Buf), &NumItems);
|
||||
EXPECT_STREQ(Buf, "");
|
||||
EXPECT_EQ(Size, 0U);
|
||||
EXPECT_EQ(NumItems, 0U);
|
||||
|
||||
while (true) {
|
||||
// Can get size without output parameters.
|
||||
Size = LLVMMutagenGetMutationSequence(true, nullptr, 0, &NumItems);
|
||||
if (NumItems > Sequence<Mutator>::kMaxBriefItems)
|
||||
break;
|
||||
// !Verbose has no effect for <= 10 items.
|
||||
EXPECT_EQ(LLVMMutagenGetMutationSequence(false, nullptr, 0, nullptr), Size);
|
||||
EXPECT_GT(LLVMMutagenDefaultMutate(U.data(), U.size(), U.capacity()), 0U);
|
||||
}
|
||||
|
||||
// All items are valid.
|
||||
LLVMMutagenGetMutationSequence(true, Buf, sizeof(Buf), nullptr);
|
||||
ISS.str(Buf);
|
||||
size_t N = 0;
|
||||
while (std::getline(ISS, Name, '-')) {
|
||||
EXPECT_GT(Names.count(Name), 0U);
|
||||
++N;
|
||||
}
|
||||
EXPECT_EQ(N, NumItems);
|
||||
|
||||
// !Verbose truncates, but items are still valid.
|
||||
EXPECT_LT(LLVMMutagenGetMutationSequence(false, Buf, sizeof(Buf), nullptr),
|
||||
Size);
|
||||
ISS.str(Buf);
|
||||
N = 0;
|
||||
while (std::getline(ISS, Name, '-')) {
|
||||
EXPECT_GT(Names.count(Name), 0U);
|
||||
++N;
|
||||
}
|
||||
EXPECT_LT(N, NumItems);
|
||||
|
||||
// Truncated sequence is a prefix of its untruncated equivalent.
|
||||
std::string Truncated(Buf);
|
||||
LLVMMutagenGetMutationSequence(true, Buf, sizeof(Buf), &NumItems);
|
||||
Buf[Truncated.size()] = '\0';
|
||||
EXPECT_STREQ(Truncated.c_str(), Buf);
|
||||
|
||||
// Stops at the end of |Buf|, and null terminates.
|
||||
EXPECT_EQ(LLVMMutagenGetMutationSequence(true, Buf, Size - 1, nullptr), Size);
|
||||
EXPECT_EQ(strlen(Buf), Size - 2);
|
||||
|
||||
// Clear the sequence.
|
||||
LLVMMutagenResetSequence();
|
||||
EXPECT_EQ(LLVMMutagenGetMutationSequence(true, nullptr, 0, nullptr), 0U);
|
||||
}
|
||||
|
||||
static uint8_t FromASCIINybble(char C) {
|
||||
if ('0' <= C && C <= '9')
|
||||
return static_cast<uint8_t>(C - '0');
|
||||
if ('A' <= C && C <= 'F')
|
||||
return static_cast<uint8_t>(C - 'A' + 10);
|
||||
assert('a' <= C && C <= 'f');
|
||||
return static_cast<uint8_t>(C - 'a' + 10);
|
||||
}
|
||||
|
||||
static Word FromASCII(const char *DE) {
|
||||
Unit Tmp;
|
||||
bool Escape = false;
|
||||
size_t Hex = 0;
|
||||
uint8_t Nybble = 0;
|
||||
for (char C = *DE++; C; C = *DE++) {
|
||||
if (Hex == 2) {
|
||||
Nybble = FromASCIINybble(C);
|
||||
--Hex;
|
||||
} else if (Hex == 1) {
|
||||
Tmp.push_back(static_cast<uint8_t>(Nybble << 4) | FromASCIINybble(C));
|
||||
--Hex;
|
||||
} else if (Escape) {
|
||||
switch (C) {
|
||||
case '\\':
|
||||
case '"':
|
||||
Tmp.push_back(static_cast<uint8_t>(C));
|
||||
break;
|
||||
case 'x':
|
||||
Hex = 2;
|
||||
break;
|
||||
default:
|
||||
assert(false && "FromASCII failure.");
|
||||
}
|
||||
Escape = false;
|
||||
} else if (C == '\\') {
|
||||
Escape = true;
|
||||
} else {
|
||||
Tmp.push_back(static_cast<uint8_t>(C));
|
||||
}
|
||||
}
|
||||
return Word(Tmp.data(), Tmp.size());
|
||||
}
|
||||
|
||||
TEST_F(MutagenInterface, Dictionaries) {
|
||||
LLVMMutagenConfigure(&Config);
|
||||
size_t NumItems;
|
||||
char Buf[1024];
|
||||
std::istringstream ISS;
|
||||
std::string Str;
|
||||
|
||||
// Empty sequences
|
||||
auto Size =
|
||||
LLVMMutagenGetDictionaryEntrySequence(true, Buf, sizeof(Buf), &NumItems);
|
||||
EXPECT_STREQ(Buf, "");
|
||||
EXPECT_EQ(Size, 0U);
|
||||
EXPECT_EQ(NumItems, 0U);
|
||||
|
||||
auto *MD = GetMutationDispatcherForTest();
|
||||
while (true) {
|
||||
// Can get size without output parameters.
|
||||
Size = LLVMMutagenGetDictionaryEntrySequence(true, nullptr, 0, &NumItems);
|
||||
if (NumItems > Sequence<DictionaryEntry *>::kMaxBriefItems)
|
||||
break;
|
||||
// !Verbose has no effect for <= 10 items.
|
||||
EXPECT_EQ(LLVMMutagenGetDictionaryEntrySequence(false, nullptr, 0, nullptr),
|
||||
Size);
|
||||
MD->Mutate_AddWordFromTORC(U.data(), U.size(), U.capacity());
|
||||
}
|
||||
|
||||
// All items are valid.
|
||||
LLVMMutagenGetDictionaryEntrySequence(true, Buf, sizeof(Buf), nullptr);
|
||||
ISS.str(Buf);
|
||||
size_t N = 0;
|
||||
while (std::getline(ISS, Str, '-')) {
|
||||
ASSERT_FALSE(Str.empty());
|
||||
EXPECT_EQ(Str[0], '"');
|
||||
EXPECT_EQ(Str[Str.size() - 1], '"');
|
||||
++N;
|
||||
}
|
||||
EXPECT_EQ(N, NumItems);
|
||||
|
||||
// !Verbose truncates, but items are still valid.
|
||||
EXPECT_LT(
|
||||
LLVMMutagenGetDictionaryEntrySequence(false, Buf, sizeof(Buf), nullptr),
|
||||
Size);
|
||||
ISS.str(Buf);
|
||||
N = 0;
|
||||
while (std::getline(ISS, Str, '-')) {
|
||||
ASSERT_FALSE(Str.empty());
|
||||
EXPECT_EQ(Str[0], '"');
|
||||
EXPECT_EQ(Str[Str.size() - 1], '"');
|
||||
++N;
|
||||
}
|
||||
EXPECT_LT(N, NumItems);
|
||||
|
||||
// Truncated sequence is a prefix of its untruncated equivalent.
|
||||
std::string Truncated(Buf);
|
||||
LLVMMutagenGetDictionaryEntrySequence(true, Buf, sizeof(Buf), &NumItems);
|
||||
Buf[Truncated.size()] = '\0';
|
||||
EXPECT_STREQ(Truncated.c_str(), Buf);
|
||||
|
||||
// Stops at the end of |Buf|, and null terminates.
|
||||
EXPECT_EQ(LLVMMutagenGetDictionaryEntrySequence(true, Buf, Size - 1, nullptr),
|
||||
Size);
|
||||
EXPECT_EQ(strlen(Buf), Size - 2);
|
||||
|
||||
// Clear the sequence.
|
||||
LLVMMutagenResetSequence();
|
||||
EXPECT_EQ(LLVMMutagenGetDictionaryEntrySequence(true, nullptr, 0, nullptr),
|
||||
0U);
|
||||
|
||||
// Retuns null if no recommendations.
|
||||
size_t UseCount = 0;
|
||||
EXPECT_EQ(LLVMMutagenRecommendDictionaryEntry(&UseCount), nullptr);
|
||||
EXPECT_EQ(LLVMMutagenRecommendDictionary(), 0U);
|
||||
EXPECT_EQ(LLVMMutagenRecommendDictionaryEntry(&UseCount), nullptr);
|
||||
|
||||
// Record sequences.
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
MD->Mutate_AddWordFromTORC(U.data(), U.size(), U.capacity());
|
||||
}
|
||||
LLVMMutagenRecordSequence();
|
||||
}
|
||||
|
||||
size_t NumDEs = LLVMMutagenRecommendDictionary();
|
||||
EXPECT_NE(NumDEs, 0U);
|
||||
for (size_t i = 0; i < NumDEs; ++i) {
|
||||
auto *DE = LLVMMutagenRecommendDictionaryEntry(&UseCount);
|
||||
EXPECT_NE(DE, nullptr);
|
||||
EXPECT_EQ(UseCount, 0U);
|
||||
}
|
||||
|
||||
// Increment the use counts of entries.
|
||||
for (size_t i = 0; i < 100; ++i)
|
||||
MD->Mutate_AddWordFromPersistentAutoDictionary(U.data(), U.size(),
|
||||
U.capacity());
|
||||
NumDEs = LLVMMutagenRecommendDictionary();
|
||||
EXPECT_NE(NumDEs, 0U);
|
||||
for (size_t i = 0; i < NumDEs; ++i) {
|
||||
auto *DE = LLVMMutagenRecommendDictionaryEntry(&UseCount);
|
||||
EXPECT_NE(DE, nullptr);
|
||||
EXPECT_NE(UseCount, 0U);
|
||||
}
|
||||
|
||||
// Add the first few words manually to exclude them from recommendations.
|
||||
Vector<Word> ManualAdditions;
|
||||
NumDEs = LLVMMutagenRecommendDictionary();
|
||||
ASSERT_GT(NumDEs, 3U);
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
auto *DE = LLVMMutagenRecommendDictionaryEntry(nullptr);
|
||||
auto W = FromASCII(DE);
|
||||
LLVMMutagenAddWordToDictionary(W.data(), W.size());
|
||||
ManualAdditions.push_back(W);
|
||||
}
|
||||
N = NumDEs;
|
||||
|
||||
// Get the recommended dictionary without the manual additions.
|
||||
NumDEs = LLVMMutagenRecommendDictionary();
|
||||
EXPECT_EQ(NumDEs, N - 3);
|
||||
for (size_t i = 0; i < NumDEs; ++i) {
|
||||
auto *DE = LLVMMutagenRecommendDictionaryEntry(nullptr);
|
||||
ASSERT_NE(DE, nullptr);
|
||||
Word W1(reinterpret_cast<const uint8_t *>(DE), strlen(DE));
|
||||
for (const auto &W2 : ManualAdditions)
|
||||
EXPECT_FALSE(W1 == W2);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mutagen
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
|
@ -20,6 +20,7 @@ endif()
|
|||
if(COMPILER_RT_INCLUDE_TESTS)
|
||||
list(APPEND LIBFUZZER_TEST_DEPS FuzzerUnitTests)
|
||||
list(APPEND LIBFUZZER_TEST_DEPS FuzzedDataProviderUnitTests)
|
||||
list(APPEND LIBFUZZER_TEST_DEPS MutagenUnitTests)
|
||||
endif()
|
||||
|
||||
add_custom_target(check-fuzzer)
|
||||
|
|
Loading…
Reference in New Issue