diff --git a/compiler-rt/lib/fuzzer/CMakeLists.txt b/compiler-rt/lib/fuzzer/CMakeLists.txt index e27cf8d5e6fe89..3201ed279a621d 100644 --- a/compiler-rt/lib/fuzzer/CMakeLists.txt +++ b/compiler-rt/lib/fuzzer/CMakeLists.txt @@ -1,4 +1,5 @@ set(LIBFUZZER_SOURCES + FuzzerCrossOver.cpp FuzzerDataFlowTrace.cpp FuzzerDriver.cpp FuzzerExtFunctionsDlsym.cpp @@ -28,6 +29,7 @@ set(LIBFUZZER_HEADERS FuzzerCorpus.h FuzzerDataFlowTrace.h FuzzerDefs.h + FuzzerDictionary.h FuzzerExtFunctions.def FuzzerExtFunctions.h FuzzerFlags.def @@ -82,32 +84,6 @@ 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 "$" --no-whole-archive ${dir}/lib/libc++.a -r -o ${name}.o - COMMAND ${CMAKE_OBJCOPY} --localize-hidden ${name}.o - COMMAND ${CMAKE_COMMAND} -E remove "$" - COMMAND ${CMAKE_AR} qcs "$" ${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 @@ -159,6 +135,23 @@ 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 "$" --no-whole-archive ${dir}/lib/libc++.a -r -o ${name}.o + COMMAND ${CMAKE_OBJCOPY} --localize-hidden ${name}.o + COMMAND ${CMAKE_COMMAND} -E remove "$" + COMMAND ${CMAKE_AR} qcs "$" ${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}) diff --git a/compiler-rt/lib/fuzzer/mutagen/MutagenCrossOver.cpp b/compiler-rt/lib/fuzzer/FuzzerCrossOver.cpp similarity index 86% rename from compiler-rt/lib/fuzzer/mutagen/MutagenCrossOver.cpp rename to compiler-rt/lib/fuzzer/FuzzerCrossOver.cpp index 0fcffaf68ddf43..83d9f8d47cb18f 100644 --- a/compiler-rt/lib/fuzzer/mutagen/MutagenCrossOver.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerCrossOver.cpp @@ -1,4 +1,4 @@ -//===- MutagenCrossOver.cpp - Cross over two test inputs ------------------===// +//===- FuzzerCrossOver.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,11 +8,12 @@ // Cross over test inputs. //===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#include "FuzzerMutate.h" #include "FuzzerRandom.h" -#include "MutagenDispatcher.h" #include -namespace mutagen { +namespace fuzzer { // Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out. size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1, @@ -39,12 +40,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 mutagen +} // namespace fuzzer diff --git a/compiler-rt/lib/fuzzer/FuzzerDefs.h b/compiler-rt/lib/fuzzer/FuzzerDefs.h index 36820b61c2aa5a..1a2752af2f4d56 100644 --- a/compiler-rt/lib/fuzzer/FuzzerDefs.h +++ b/compiler-rt/lib/fuzzer/FuzzerDefs.h @@ -15,18 +15,21 @@ #include #include #include -#include #include #include #include #include + namespace fuzzer { template T Min(T a, T b) { return a < b ? a : b; } template 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; @@ -57,37 +60,6 @@ using Set = std::set, fuzzer_allocator>; typedef Vector Unit; typedef Vector UnitVector; - -// A simple POD sized array of bytes. -template 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::max(), - "FixedWord::kMaxSizeT cannot fit in a uint8_t."); - assert(S <= kMaxSize); - memcpy(Data, B, S); - Size = static_cast(S); - } - - bool operator==(const FixedWord &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); diff --git a/compiler-rt/lib/fuzzer/FuzzerDictionary.h b/compiler-rt/lib/fuzzer/FuzzerDictionary.h new file mode 100644 index 00000000000000..db55907d936314 --- /dev/null +++ b/compiler-rt/lib/fuzzer/FuzzerDictionary.h @@ -0,0 +1,120 @@ +//===- 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 +#include + +namespace fuzzer { +// A simple POD sized array of bytes. +template 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::max(), + "FixedWord::kMaxSizeT cannot fit in a uint8_t."); + assert(S <= kMaxSize); + memcpy(Data, B, S); + Size = static_cast(S); + } + + bool operator==(const FixedWord &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::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::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 *Units); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DICTIONARY_H diff --git a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp index 38efc2e18863cf..ceaa9070512f0a 100644 --- a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp @@ -19,16 +19,15 @@ #include "FuzzerPlatform.h" #include "FuzzerRandom.h" #include "FuzzerTracePC.h" -#include "mutagen/MutagenDispatcher.h" #include #include #include #include #include -#include #include #include #include +#include // This function should be present in the libFuzzer so that the client // binary can test for its existence. @@ -804,9 +803,8 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { ReadCorpora(*Inputs, {})); } - LLVMMutagenConfiguration Config; - ConfigureMutagen(Seed, Options, &Config); - auto *MD = new MutationDispatcher(&Config); + Random Rand(Seed); + auto *MD = new MutationDispatcher(Rand, Options); auto *Corpus = new InputCorpus(Options.OutputCorpus, Entropic); auto *F = new Fuzzer(Callback, *Corpus, *MD, Options); diff --git a/compiler-rt/lib/fuzzer/FuzzerInternal.h b/compiler-rt/lib/fuzzer/FuzzerInternal.h index a629c3d02f7740..37c8a01dc3c647 100644 --- a/compiler-rt/lib/fuzzer/FuzzerInternal.h +++ b/compiler-rt/lib/fuzzer/FuzzerInternal.h @@ -18,7 +18,6 @@ #include "FuzzerOptions.h" #include "FuzzerSHA1.h" #include "FuzzerValueBitMap.h" -#include "mutagen/MutagenDispatcher.h" #include #include #include @@ -27,12 +26,8 @@ #include namespace fuzzer { -namespace { using namespace std::chrono; -using mutagen::MutationDispatcher; - -} // namespace class Fuzzer { public: diff --git a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp index d50277e9f2a0de..86a78ab751741c 100644 --- a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp @@ -177,7 +177,7 @@ void Fuzzer::DumpCurrentUnit(const char *Prefix) { if (!CurrentUnitData) return; // Happens when running individual inputs. ScopedDisableMsanInterceptorChecks S; - PrintMutationSequence(MD); + MD.PrintMutationSequence(); Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str()); size_t UnitSize = CurrentUnitSize; if (UnitSize <= kMaxUnitSizeToPrint) { @@ -539,9 +539,8 @@ 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, - MS.GetString()); + MD.MutationSequence()); return true; } if (II && FoundUniqFeaturesOfII && @@ -653,7 +652,7 @@ void Fuzzer::PrintStatusForNewUnit(const Unit &U, const char *Text) { PrintStats(Text, ""); if (Options.Verbosity) { Printf(" L: %zd/%zd ", U.size(), Corpus.MaxInputSize()); - PrintMutationSequence(MD, Options.Verbosity >= 2); + MD.PrintMutationSequence(Options.Verbosity >= 2); Printf("\n"); } } @@ -899,7 +898,7 @@ void Fuzzer::Loop(Vector &CorporaFiles) { } PrintStats("DONE ", "\n"); - PrintRecommendedDictionary(MD); + MD.PrintRecommendedDictionary(); } void Fuzzer::MinimizeCrashLoop(const Unit &U) { diff --git a/compiler-rt/lib/fuzzer/FuzzerMutate.cpp b/compiler-rt/lib/fuzzer/FuzzerMutate.cpp index e3461467d9ff6a..4650f1beceacde 100644 --- a/compiler-rt/lib/fuzzer/FuzzerMutate.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerMutate.cpp @@ -1,76 +1,497 @@ -//===- FuzzerMutate.cpp - Mutation utilities -----------------------------===// +//===- FuzzerMutate.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 utilities. +// Mutate a test input. //===----------------------------------------------------------------------===// -#include "FuzzerMutate.h" +#include "FuzzerDefs.h" #include "FuzzerExtFunctions.h" #include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerOptions.h" #include "FuzzerTracePC.h" -#include "FuzzerUtil.h" namespace fuzzer { -namespace { -void FromTORC4(size_t Idx, uint32_t *A, uint32_t *B) { - const auto &X = TPC.TORC4.Get(Idx); - *A = X.A; - *B = X.B; +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); +} + +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"}); +} + +static char RandCh(Random &Rand) { + if (Rand.RandBool()) + return static_cast(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 (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()); +} + +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()); + + 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; } -void FromTORC8(size_t Idx, uint64_t *A, uint64_t *B) { - const auto &X = TPC.TORC8.Get(Idx); - *A = X.A; - *B = X.B; +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; } -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_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( + Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255)); + for (size_t i = 0; i < N; i++) + Data[Idx + i] = Byte; + return Size + N; } -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_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; } -void PrintASCII(const Word &W, const char *PrintAfter) { - fuzzer::PrintASCII(W.data(), W.size(), PrintAfter); +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; } -} // namespace +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(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 +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(Arg1 + Rand(-1, 1)); + T Arg2Mutation = static_cast(Arg2 + Rand(-1, 1)); + return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation, + sizeof(Arg1), Data, Size); +} -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; +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); } -void PrintRecommendedDictionary(MutationDispatcher &MD) { - auto RecommendedDictionary = MD.RecommendDictionary(); - if (RecommendedDictionary.empty()) - return; +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()); + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + } break; + case 1: { + auto X = TPC.TORC4.Get(Rand.Rand()); + 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()); + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + } break; + case 3: if (Options.UseMemmem) { + auto X = TPC.MMT.Get(Rand.Rand()); + 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 +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(Size); + if (Rand.RandBool()) + Val = Bswap(Val); + } else { + memcpy(&Val, Data + Off, sizeof(Val)); + T Add = static_cast(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(Data, Size, Rand); + case 2: return ChangeBinaryInteger(Data, Size, Rand); + case 1: return ChangeBinaryInteger(Data, Size, Rand); + case 0: return ChangeBinaryInteger(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 V; + for (auto &DE : PersistentAutoDictionary) + if (!ManualDictionary.ContainsWord(DE.GetW())) + V.push_back(DE); + if (V.empty()) return; Printf("###### Recommended dictionary. ######\n"); - for (auto &DE : RecommendedDictionary) { + for (auto &DE: V) { assert(DE.GetW().size()); Printf("\""); PrintASCII(DE.GetW(), "\""); @@ -79,12 +500,97 @@ void PrintRecommendedDictionary(MutationDispatcher &MD) { Printf("###### End of recommended dictionary. ######\n"); } -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()); +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 &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 &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::max()}); } } // namespace fuzzer diff --git a/compiler-rt/lib/fuzzer/FuzzerMutate.h b/compiler-rt/lib/fuzzer/FuzzerMutate.h index 85e284ef571c17..fd37191156d3fc 100644 --- a/compiler-rt/lib/fuzzer/FuzzerMutate.h +++ b/compiler-rt/lib/fuzzer/FuzzerMutate.h @@ -11,23 +11,145 @@ #ifndef LLVM_FUZZER_MUTATE_H #define LLVM_FUZZER_MUTATE_H +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" #include "FuzzerOptions.h" -#include "mutagen/Mutagen.h" -#include "mutagen/MutagenDispatcher.h" +#include "FuzzerRandom.h" namespace fuzzer { -namespace { -using mutagen::MutationDispatcher; +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); -} // namespace + /// Mutates data by adding a word from the manual dictionary. + size_t Mutate_AddWordFromManualDictionary(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 TORC. + size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize); -void PrintRecommendedDictionary(MutationDispatcher &MD); + /// 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 PrintMutationSequence(MutationDispatcher &MD, bool Verbose = true); + /// 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 &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 &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 + 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 CurrentDictionaryEntrySequence; + + static const size_t kCmpDictionaryEntriesDequeSize = 16; + DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize]; + size_t CmpDictionaryEntriesDequeIdx = 0; + + const Unit *CrossOverWith = nullptr; + Vector MutateInPlaceHere; + Vector MutateWithMaskTemp; + // CustomCrossOver needs its own buffer as a custom implementation may call + // LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere. + Vector CustomCrossOverInPlaceHere; + + Vector Mutators; + Vector DefaultMutators; + Vector CurrentMutatorSequence; +}; } // namespace fuzzer diff --git a/compiler-rt/lib/fuzzer/FuzzerRandom.h b/compiler-rt/lib/fuzzer/FuzzerRandom.h index 8256853a65bb52..ad6c07eb5ef563 100644 --- a/compiler-rt/lib/fuzzer/FuzzerRandom.h +++ b/compiler-rt/lib/fuzzer/FuzzerRandom.h @@ -11,7 +11,6 @@ #ifndef LLVM_FUZZER_RANDOM_H #define LLVM_FUZZER_RANDOM_H -#include #include namespace fuzzer { diff --git a/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp b/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp index b613aef7b59f5c..d808b9b00fa354 100644 --- a/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp @@ -16,6 +16,7 @@ #include "FuzzerBuiltinsMsvc.h" #include "FuzzerCorpus.h" #include "FuzzerDefs.h" +#include "FuzzerDictionary.h" #include "FuzzerExtFunctions.h" #include "FuzzerIO.h" #include "FuzzerPlatform.h" diff --git a/compiler-rt/lib/fuzzer/FuzzerTracePC.h b/compiler-rt/lib/fuzzer/FuzzerTracePC.h index 921a13f082ae3e..a93732972f7d70 100644 --- a/compiler-rt/lib/fuzzer/FuzzerTracePC.h +++ b/compiler-rt/lib/fuzzer/FuzzerTracePC.h @@ -12,7 +12,7 @@ #define LLVM_FUZZER_TRACE_PC #include "FuzzerDefs.h" -#include "FuzzerUtil.h" +#include "FuzzerDictionary.h" #include "FuzzerValueBitMap.h" #include @@ -40,7 +40,7 @@ struct TableOfRecentCompares { Table[Idx].B = Arg2; } - const Pair &Get(size_t I) { return Table[I % kSize]; } + Pair Get(size_t I) { return Table[I % kSize]; } Pair Table[kSize]; }; diff --git a/compiler-rt/lib/fuzzer/FuzzerUtil.h b/compiler-rt/lib/fuzzer/FuzzerUtil.h index 285f56be8a7672..a188a7be32a531 100644 --- a/compiler-rt/lib/fuzzer/FuzzerUtil.h +++ b/compiler-rt/lib/fuzzer/FuzzerUtil.h @@ -47,15 +47,6 @@ 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 *Units); - // Platform specific functions. void SetSignalHandler(const FuzzingOptions& Options); @@ -72,6 +63,9 @@ 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 &Args, const char *X1, const char *X2); diff --git a/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp b/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp index e83baa62886c0d..5034b4a28d3f51 100644 --- a/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerUtilFuchsia.cpp @@ -528,6 +528,11 @@ 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 diff --git a/compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp b/compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp index 5f0aa0190dd669..0446d732a9ec85 100644 --- a/compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerUtilPosix.cpp @@ -170,6 +170,11 @@ 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; } diff --git a/compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp b/compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp index 5deb5998fccb4d..1a54bb569eca41 100644 --- a/compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp @@ -182,6 +182,27 @@ 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 command_vector; command_vector.push_back("dumpbin /summary > nul"); diff --git a/compiler-rt/lib/fuzzer/build.sh b/compiler-rt/lib/fuzzer/build.sh index db52ffb7980a86..504e54e3a819ee 100755 --- a/compiler-rt/lib/fuzzer/build.sh +++ b/compiler-rt/lib/fuzzer/build.sh @@ -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 $LIBMUTAGEN_SRC_DIR/*.cpp; do - $CXX -O2 -fno-omit-frame-pointer -std=c++17 $f -c -I$LIBFUZZER_SRC_DIR & +for f in $LIBFUZZER_SRC_DIR/*.cpp; do + $CXX -g -O2 -fno-omit-frame-pointer -std=c++11 $f -c & done wait rm -f libFuzzer.a -ar ru libFuzzer.a Fuzzer*.o Mutagen*.o -rm -f Fuzzer*.o Mutagen*.o +ar ru libFuzzer.a Fuzzer*.o +rm -f Fuzzer*.o + diff --git a/compiler-rt/lib/fuzzer/mutagen/CMakeLists.txt b/compiler-rt/lib/fuzzer/mutagen/CMakeLists.txt deleted file mode 100644 index 1a8175ce6e6ecf..00000000000000 --- a/compiler-rt/lib/fuzzer/mutagen/CMakeLists.txt +++ /dev/null @@ -1,59 +0,0 @@ -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() diff --git a/compiler-rt/lib/fuzzer/mutagen/Mutagen.cpp b/compiler-rt/lib/fuzzer/mutagen/Mutagen.cpp deleted file mode 100644 index 8d5858191afeb8..00000000000000 --- a/compiler-rt/lib/fuzzer/mutagen/Mutagen.cpp +++ /dev/null @@ -1,100 +0,0 @@ -//===- 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 -#include -#include - -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" diff --git a/compiler-rt/lib/fuzzer/mutagen/Mutagen.h b/compiler-rt/lib/fuzzer/mutagen/Mutagen.h deleted file mode 100644 index 757ee3e07d2cd7..00000000000000 --- a/compiler-rt/lib/fuzzer/mutagen/Mutagen.h +++ /dev/null @@ -1,119 +0,0 @@ -//===- 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 -#include - -#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 diff --git a/compiler-rt/lib/fuzzer/mutagen/MutagenDictionary.h b/compiler-rt/lib/fuzzer/mutagen/MutagenDictionary.h deleted file mode 100644 index a665cabc316e76..00000000000000 --- a/compiler-rt/lib/fuzzer/mutagen/MutagenDictionary.h +++ /dev/null @@ -1,85 +0,0 @@ -//===- 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 -#include -#include -#include -#include - -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::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::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 diff --git a/compiler-rt/lib/fuzzer/mutagen/MutagenDispatcher.cpp b/compiler-rt/lib/fuzzer/mutagen/MutagenDispatcher.cpp deleted file mode 100644 index 32b5694cc6862e..00000000000000 --- a/compiler-rt/lib/fuzzer/mutagen/MutagenDispatcher.cpp +++ /dev/null @@ -1,659 +0,0 @@ -//===- 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 "FuzzerBuiltinsMsvc.h" -#include "FuzzerPlatform.h" -#include "MutagenUtil.h" -#include -#include - -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(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(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()); -} - -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()); - - 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( - 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(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 -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(Arg1 + Rand(-1, 1)); - T Arg2Mutation = static_cast(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(), &A, &B); - DE = MakeDictionaryEntryFromCMP(A, B, Data, Size); - } break; - case 1: { - uint32_t A, B; - Config.FromTORC4(Rand.Rand(), &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(), &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(), &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 -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(Size); - if (Rand.RandBool()) - Val = Bswap(Val); - } else { - memcpy(&Val, Data + Off, sizeof(Val)); - T Add = static_cast(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(Data, Size, Rand); - case 2: - return ChangeBinaryInteger(Data, Size, Rand); - case 1: - return ChangeBinaryInteger(Data, Size, Rand); - case 0: - return ChangeBinaryInteger(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::MutationSequence() { - CurrentMutatorSequence.SetString([](Mutator M) { return M.Name; }); - return CurrentMutatorSequence; -} - -const Sequence & -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 &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 &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::max()}); -} - -} // namespace mutagen diff --git a/compiler-rt/lib/fuzzer/mutagen/MutagenDispatcher.h b/compiler-rt/lib/fuzzer/mutagen/MutagenDispatcher.h deleted file mode 100644 index c5c43d5c346fe7..00000000000000 --- a/compiler-rt/lib/fuzzer/mutagen/MutagenDispatcher.h +++ /dev/null @@ -1,190 +0,0 @@ -//===- 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 -#include -#include - -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 &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 &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 &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 - 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 &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 CurrentDictionaryEntrySequence; - - static const size_t kCmpDictionaryEntriesDequeSize = 16; - DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize]; - size_t CmpDictionaryEntriesDequeIdx = 0; - - const Unit *CrossOverWith = nullptr; - Vector MutateInPlaceHere; - Vector MutateWithMaskTemp; - // CustomCrossOver needs its own buffer as a custom implementation may call - // LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere. - Vector CustomCrossOverInPlaceHere; - - Vector Mutators; - Vector DefaultMutators; - Sequence 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 diff --git a/compiler-rt/lib/fuzzer/mutagen/MutagenSequence.h b/compiler-rt/lib/fuzzer/mutagen/MutagenSequence.h deleted file mode 100644 index fd0ab2cb5f0f9d..00000000000000 --- a/compiler-rt/lib/fuzzer/mutagen/MutagenSequence.h +++ /dev/null @@ -1,101 +0,0 @@ -//===- 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 -#include - -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 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::const_iterator begin() const { return Items.begin(); } - typename Vector::iterator begin() { return Items.begin(); } - - typename Vector::const_iterator end() const { return Items.end(); } - typename Vector::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 - // 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(OSS.tellp()); - for (; i < Size; i++) - OSS << ConvertToASCII(Items[i]) << "-"; - Str = OSS.str(); - } - -private: - Vector Items; - size_t Size = 0; - std::string Str; - size_t Brief = 0; -}; - -template -typename Vector::const_iterator begin(const Sequence &S) { - return S.begin(); -} - -template typename Vector::iterator begin(Sequence &S) { - return S.begin(); -} - -template -typename Vector::const_iterator end(const Sequence &S) { - return S.end(); -} - -template typename Vector::iterator end(Sequence &S) { - return S.end(); -} - -} // namespace mutagen - -#endif // LLVM_FUZZER_MUTAGEN_SEQUENCE_H diff --git a/compiler-rt/lib/fuzzer/mutagen/MutagenUtil.h b/compiler-rt/lib/fuzzer/mutagen/MutagenUtil.h deleted file mode 100644 index cf3b78b9655af9..00000000000000 --- a/compiler-rt/lib/fuzzer/mutagen/MutagenUtil.h +++ /dev/null @@ -1,24 +0,0 @@ -//===- 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 -#include - -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 diff --git a/compiler-rt/lib/fuzzer/mutagen/MutagenUtilPosix.cpp b/compiler-rt/lib/fuzzer/mutagen/MutagenUtilPosix.cpp deleted file mode 100644 index c157c6190c5d75..00000000000000 --- a/compiler-rt/lib/fuzzer/mutagen/MutagenUtilPosix.cpp +++ /dev/null @@ -1,23 +0,0 @@ -//===- 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 - -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) diff --git a/compiler-rt/lib/fuzzer/mutagen/MutagenUtilWindows.cpp b/compiler-rt/lib/fuzzer/mutagen/MutagenUtilWindows.cpp deleted file mode 100644 index 93b86556b13935..00000000000000 --- a/compiler-rt/lib/fuzzer/mutagen/MutagenUtilWindows.cpp +++ /dev/null @@ -1,41 +0,0 @@ -//===- 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 -#include -#include - -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 diff --git a/compiler-rt/lib/fuzzer/mutagen/build.sh b/compiler-rt/lib/fuzzer/mutagen/build.sh deleted file mode 100755 index bac2a1e09f44fe..00000000000000 --- a/compiler-rt/lib/fuzzer/mutagen/build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/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 - diff --git a/compiler-rt/lib/fuzzer/tests/CMakeLists.txt b/compiler-rt/lib/fuzzer/tests/CMakeLists.txt index 3cc7e97e65a143..5b3e906419546b 100644 --- a/compiler-rt/lib/fuzzer/tests/CMakeLists.txt +++ b/compiler-rt/lib/fuzzer/tests/CMakeLists.txt @@ -17,9 +17,6 @@ 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++) @@ -49,31 +46,19 @@ 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 $) - set(LIBMUTAGEN_TEST_RUNTIME_OBJECTS - $) else() set(LIBFUZZER_TEST_RUNTIME_OBJECTS $) - set(LIBMUTAGEN_TEST_RUNTIME_OBJECTS - $) endif() add_library(${LIBFUZZER_TEST_RUNTIME} STATIC - ${LIBFUZZER_TEST_RUNTIME_OBJECTS} - ${LIBMUTAGEN_TEST_RUNTIME_OBJECTS}) + ${LIBFUZZER_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) @@ -81,8 +66,6 @@ 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) @@ -90,7 +73,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 @@ -105,15 +88,4 @@ 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() diff --git a/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp b/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp index 277fa5e6183f8b..974a01ff4ab6ef 100644 --- a/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp +++ b/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp @@ -10,6 +10,7 @@ #define GTEST_NO_LLVM_SUPPORT 1 #include "FuzzerCorpus.h" +#include "FuzzerDictionary.h" #include "FuzzerInternal.h" #include "FuzzerMerge.h" #include "FuzzerMutate.h" @@ -43,6 +44,65 @@ TEST(Fuzzer, Basename) { #endif } +TEST(Fuzzer, CrossOver) { + std::unique_ptr t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + std::unique_ptr 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 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)); @@ -51,6 +111,423 @@ 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 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 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 t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + std::unique_ptr 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 t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + std::unique_ptr 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 t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + std::unique_ptr 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 t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + std::unique_ptr 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 t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + std::unique_ptr 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 t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + std::unique_ptr 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 t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + std::unique_ptr 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 t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + std::unique_ptr 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 t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + std::unique_ptr 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 t(new ExternalFunctions()); + fuzzer::EF = t.get(); + Random Rand(0); + std::unique_ptr 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)); diff --git a/compiler-rt/lib/fuzzer/tests/MutagenUnittest.cpp b/compiler-rt/lib/fuzzer/tests/MutagenUnittest.cpp deleted file mode 100644 index 0e50168fdb4cf6..00000000000000 --- a/compiler-rt/lib/fuzzer/tests/MutagenUnittest.cpp +++ /dev/null @@ -1,961 +0,0 @@ -// 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 - -namespace mutagen { -namespace { - -using fuzzer::Set; - -std::unique_ptr CreateMutationDispatcher() { - LLVMMutagenConfiguration Config; - memset(&Config, 0, sizeof(Config)); - return std::make_unique(&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 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(kWord1); - *Size1 = strlen(kWord1); - *Data2 = reinterpret_cast(kWord2); - *Size2 = strlen(kWord2); - }; - - Config.UseMemmem = 0; - Config.FromMMT = [](size_t Idx, const uint8_t **Data, size_t *Size) { - ++(Current->FromMMTCalls); - *Data = reinterpret_cast(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( - 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 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::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(C - '0'); - if ('A' <= C && C <= 'F') - return static_cast(C - 'A' + 10); - assert('a' <= C && C <= 'f'); - return static_cast(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(Nybble << 4) | FromASCIINybble(C)); - --Hex; - } else if (Escape) { - switch (C) { - case '\\': - case '"': - Tmp.push_back(static_cast(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(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::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 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(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(); -} diff --git a/compiler-rt/test/fuzzer/CMakeLists.txt b/compiler-rt/test/fuzzer/CMakeLists.txt index acfcd437f0287b..c12a04b6f27023 100644 --- a/compiler-rt/test/fuzzer/CMakeLists.txt +++ b/compiler-rt/test/fuzzer/CMakeLists.txt @@ -20,7 +20,6 @@ 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)