Index: lib/fuzzer/CMakeLists.txt =================================================================== --- lib/fuzzer/CMakeLists.txt +++ lib/fuzzer/CMakeLists.txt @@ -12,6 +12,7 @@ FuzzerLoop.cpp FuzzerMerge.cpp FuzzerMutate.cpp + FuzzerMutationStats.cpp FuzzerSHA1.cpp FuzzerShmemFuchsia.cpp FuzzerShmemPosix.cpp Index: lib/fuzzer/FuzzerDefs.h =================================================================== --- lib/fuzzer/FuzzerDefs.h +++ lib/fuzzer/FuzzerDefs.h @@ -17,7 +17,9 @@ #include #include #include +#include #include +#include #include #include Index: lib/fuzzer/FuzzerDriver.cpp =================================================================== --- lib/fuzzer/FuzzerDriver.cpp +++ lib/fuzzer/FuzzerDriver.cpp @@ -613,6 +613,7 @@ Options.PrintNewCovPcs = Flags.print_pcs; Options.PrintNewCovFuncs = Flags.print_funcs; Options.PrintFinalStats = Flags.print_final_stats; + Options.PrintMutationStats = Flags.print_mutation_stats; Options.PrintCorpusStats = Flags.print_corpus_stats; Options.PrintCoverage = Flags.print_coverage; Options.DumpCoverage = Flags.dump_coverage; Index: lib/fuzzer/FuzzerFlags.def =================================================================== --- lib/fuzzer/FuzzerFlags.def +++ lib/fuzzer/FuzzerFlags.def @@ -146,10 +146,11 @@ "after this one. Useful for fuzzers that need to do their own " "argument parsing.") FUZZER_FLAG_STRING(focus_function, "Experimental. " - "Fuzzing will focus on inputs that trigger calls to this function") + "Fuzzing will focus on inputs that trigger calls to this function") FUZZER_DEPRECATED_FLAG(run_equivalence_server) FUZZER_DEPRECATED_FLAG(use_equivalence_server) FUZZER_FLAG_INT(analyze_dict, 0, "Experimental") FUZZER_DEPRECATED_FLAG(use_clang_coverage) FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace") +FUZZER_FLAG_INT(print_mutation_stats, 0, "Experimental") Index: lib/fuzzer/FuzzerLoop.cpp =================================================================== --- lib/fuzzer/FuzzerLoop.cpp +++ lib/fuzzer/FuzzerLoop.cpp @@ -13,6 +13,7 @@ #include "FuzzerIO.h" #include "FuzzerInternal.h" #include "FuzzerMutate.h" +#include "FuzzerMutationStats.h" #include "FuzzerRandom.h" #include "FuzzerShmem.h" #include "FuzzerTracePC.h" @@ -363,6 +364,9 @@ Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded); Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds); Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb()); + + if (Options.PrintMutationStats) + MStats->PrintMutationCounts(); } void Fuzzer::SetMaxInputLen(size_t MaxInputLen) { @@ -660,6 +664,7 @@ /*DuringInitialCorpusExecution*/ false); if (NewCov) { ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size}); + MD.CountCurrentMutatorSequence(); break; // We will mutate this input more in the next rounds. } if (Options.ReduceDepth && !FoundUniqFeatures) Index: lib/fuzzer/FuzzerMutate.h =================================================================== --- lib/fuzzer/FuzzerMutate.h +++ lib/fuzzer/FuzzerMutate.h @@ -86,13 +86,16 @@ Random &GetRand() { return Rand; } -private: + void CountCurrentMutatorSequence(); struct Mutator { size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max); - const char *Name; + int Identifier; }; + +private: + 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, Index: lib/fuzzer/FuzzerMutate.cpp =================================================================== --- lib/fuzzer/FuzzerMutate.cpp +++ lib/fuzzer/FuzzerMutate.cpp @@ -10,6 +10,7 @@ //===----------------------------------------------------------------------===// #include "FuzzerMutate.h" +#include "FuzzerMutationStats.h" #include "FuzzerCorpus.h" #include "FuzzerDefs.h" #include "FuzzerExtFunctions.h" @@ -20,6 +21,24 @@ const size_t Dictionary::kMaxDictSize; +const std::map kMutationNames = { + {0, "AddWordFromManualDictionaryCount"}, + {1, "AddWordFromPersistentAutoDictionaryCount"}, + {2, "AddWordFromTORCCount"}, + {3, "ChangeASCIIIntegerCount"}, + {4, "ChangeBinaryIntegerCount"}, + {5, "ChangeBitCount"}, + {6, "CopyPartCount"}, + {7, "ChangeByteCount"}, + {8, "CrossOverCount"}, + {9, "CustomCrossoverCount"}, + {10, "CustomMutationCount"}, + {11, "EraseBytesCount"}, + {12, "InsertByteCount"}, + {13, "InsertRepeatedBytesCount"}, + {14, "ShuffleBytesCount"} +}; + static void PrintASCII(const Word &W, const char *PrintAfter) { PrintASCII(W.data(), W.size(), PrintAfter); } @@ -30,34 +49,34 @@ DefaultMutators.insert( DefaultMutators.begin(), { - {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"}, - {&MutationDispatcher::Mutate_InsertByte, "InsertByte"}, + {&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"}, + 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"}, + MANUALDICT}, {&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary, - "PersAutoDict"}, + PERSAUTODICT}, }); if(Options.UseCmp) DefaultMutators.push_back( - {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"}); + {&MutationDispatcher::Mutate_AddWordFromTORC, CMP}); if (EF->LLVMFuzzerCustomMutator) - Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"}); + Mutators.push_back({&MutationDispatcher::Mutate_Custom, CUSTOMMUTATION}); else Mutators = DefaultMutators; if (EF->LLVMFuzzerCustomCrossOver) Mutators.push_back( - {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"}); + {&MutationDispatcher::Mutate_CustomCrossOver, CUSTOMCROSSOVER}); } static char RandCh(Random &Rand) { @@ -68,6 +87,7 @@ size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize) { + MStats->IncrementCount(CUSTOMMUTATION, 0); return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand()); } @@ -87,6 +107,7 @@ return 0; assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit"); memcpy(Data, U.data(), NewSize); + MStats->IncrementCount(CUSTOMCROSSOVER, 0); return NewSize; } @@ -98,6 +119,7 @@ size_t ShuffleStart = Rand(Size - ShuffleAmount); assert(ShuffleStart + ShuffleAmount <= Size); std::shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, Rand); + MStats->IncrementCount(SHUFFLEBYTES, 0); return Size; } @@ -110,6 +132,7 @@ // 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); + MStats->IncrementCount(ERASEBYTES, 0); return Size - N; } @@ -120,6 +143,7 @@ // Insert new value at Data[Idx]. memmove(Data + Idx + 1, Data + Idx, Size - Idx); Data[Idx] = RandCh(Rand); + MStats->IncrementCount(INSERTBYTE, 0); return Size + 1; } @@ -138,6 +162,7 @@ uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255); for (size_t i = 0; i < N; i++) Data[Idx + i] = Byte; + MStats->IncrementCount(INSERTREPEATEDBYTES, 0); return Size + N; } @@ -146,6 +171,7 @@ if (Size > MaxSize) return 0; size_t Idx = Rand(Size); Data[Idx] = RandCh(Rand); + MStats->IncrementCount(CHANGEBYTE, 0); return Size; } @@ -154,12 +180,14 @@ if (Size > MaxSize) return 0; size_t Idx = Rand(Size); Data[Idx] ^= 1 << Rand(8); + MStats->IncrementCount(CHANGEBIT, 0); return Size; } size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size, size_t MaxSize) { + MStats->IncrementCount(MANUALDICT, 0); return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize); } @@ -275,11 +303,13 @@ kCmpDictionaryEntriesDequeSize]; DERef = DE; CurrentDictionaryEntrySequence.push_back(&DERef); + MStats->IncrementCount(CMP, 0); return Size; } size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary( uint8_t *Data, size_t Size, size_t MaxSize) { + MStats->IncrementCount(PERSAUTODICT, 0); return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize); } @@ -292,6 +322,7 @@ if (!Size) return 0; DE.IncUseCount(); CurrentDictionaryEntrySequence.push_back(&DE); + return Size; } @@ -338,6 +369,7 @@ size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize) { + MStats->IncrementCount(COPYPART, 0); if (Size > MaxSize || Size == 0) return 0; // If Size == MaxSize, `InsertPartOf(...)` will // fail so there's no point using it in this case. @@ -378,6 +410,7 @@ Data[Idx] = (Val % 10) + '0'; Val /= 10; } + MStats->IncrementCount(CHANGEASCIIINT, 0); return Size; } @@ -409,6 +442,7 @@ size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize) { + MStats->IncrementCount(CHANGEBININT, 0); if (Size > MaxSize) return 0; switch (Rand(4)) { case 3: return ChangeBinaryInteger(Data, Size, Rand); @@ -447,6 +481,7 @@ assert(NewSize > 0 && "CrossOver returned empty unit"); assert(NewSize <= MaxSize && "CrossOver returned overisized unit"); memcpy(Data, U.data(), NewSize); + MStats->IncrementCount(CROSSOVER, 0); return NewSize; } @@ -485,8 +520,10 @@ void MutationDispatcher::PrintMutationSequence() { Printf("MS: %zd ", CurrentMutatorSequence.size()); - for (auto M : CurrentMutatorSequence) - Printf("%s-", M.Name); + for (auto M : CurrentMutatorSequence) { + auto currentMutation = kMutationNames.find(M.Identifier); + Printf("%s-", currentMutation->second.c_str()); + } if (!CurrentDictionaryEntrySequence.empty()) { Printf(" DE: "); for (auto DE : CurrentDictionaryEntrySequence) { @@ -532,4 +569,10 @@ {W, std::numeric_limits::max()}); } +void MutationDispatcher::CountCurrentMutatorSequence() { + for (auto M : CurrentMutatorSequence) + // Increment using map look up and enum for index + MStats->IncrementCount(M.Identifier, 1); +} + } // namespace fuzzer Index: lib/fuzzer/FuzzerMutationStats.h =================================================================== --- /dev/null +++ lib/fuzzer/FuzzerMutationStats.h @@ -0,0 +1,49 @@ +//===- FuzzerMutationStats.h - Header for mutation tracking ----------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// fuzzer::MutationStats +//===----------------------------------------------------------------------===// + + +namespace fuzzer { + +enum MutatorType { + MANUALDICT, + PERSAUTODICT, + CMP, + CHANGEASCIIINT, + CHANGEBININT, + CHANGEBIT, + CHANGEBYTE, + COPYPART, + CROSSOVER, + CUSTOMCROSSOVER, + CUSTOMMUTATION, + ERASEBYTES, + INSERTBYTE, + INSERTREPEATEDBYTES, + SHUFFLEBYTES, + MAXNUMBEROFMUTATIONTYPES +}; + +class MutationStats { +public: + ~MutationStats() {} + void PrintMutationCounts(); + void IncrementCount(int mType, int type); + +private: + // A total count of each mutation used in the fuzzing process. + std::array total; + // The number of each mutation that resulted in new coverage. + std::array useful; +}; + +extern MutationStats *MStats; + +} Index: lib/fuzzer/FuzzerMutationStats.cpp =================================================================== --- /dev/null +++ lib/fuzzer/FuzzerMutationStats.cpp @@ -0,0 +1,42 @@ +//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Mutate a test input. +//===----------------------------------------------------------------------===// + +#include "FuzzerMutate.h" +#include "FuzzerMutationStats.h" +#include "FuzzerIO.h" + +#define NAMEOFSTAT(var) #var + +namespace fuzzer { + +MutationStats* MStats = new MutationStats(); + +void MutationStats::PrintMutationCounts() { + // Outputs the number of each mutation used + Printf("\n\nMutations\n\n"); +} + +// Updates the count of mutations (total/useful) depending on type. +// Types: (0) Total, (1) Useful +void MutationStats::IncrementCount(int mType, int type) { + switch (type) { + case 0: + total[mType]++; + break; + case 1: + useful[mType]++; + break; + default: + break; + } +} + +} // namespace fuzzer Index: lib/fuzzer/FuzzerOptions.h =================================================================== --- lib/fuzzer/FuzzerOptions.h +++ lib/fuzzer/FuzzerOptions.h @@ -52,6 +52,7 @@ bool PrintNewCovPcs = false; int PrintNewCovFuncs = 0; bool PrintFinalStats = false; + bool PrintMutationStats = false; bool PrintCorpusStats = false; bool PrintCoverage = false; bool DumpCoverage = false;