diff options
Diffstat (limited to 'lib/Fuzzer/FuzzerLoop.cpp')
-rw-r--r-- | lib/Fuzzer/FuzzerLoop.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/lib/Fuzzer/FuzzerLoop.cpp b/lib/Fuzzer/FuzzerLoop.cpp new file mode 100644 index 0000000000000..4c8b247a72ed6 --- /dev/null +++ b/lib/Fuzzer/FuzzerLoop.cpp @@ -0,0 +1,339 @@ +//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Fuzzer's main loop. +//===----------------------------------------------------------------------===// + +#include "FuzzerInternal.h" +#include <sanitizer/coverage_interface.h> +#include <algorithm> + +namespace fuzzer { + +// Only one Fuzzer per process. +static Fuzzer *F; + +Fuzzer::Fuzzer(UserSuppliedFuzzer &USF, FuzzingOptions Options) + : USF(USF), Options(Options) { + SetDeathCallback(); + InitializeTraceState(); + assert(!F); + F = this; +} + +void Fuzzer::SetDeathCallback() { + __sanitizer_set_death_callback(StaticDeathCallback); +} + +void Fuzzer::PrintUnitInASCIIOrTokens(const Unit &U, const char *PrintAfter) { + if (Options.Tokens.empty()) { + PrintASCII(U, PrintAfter); + } else { + auto T = SubstituteTokens(U); + T.push_back(0); + Printf("%s%s", T.data(), PrintAfter); + } +} + +void Fuzzer::StaticDeathCallback() { + assert(F); + F->DeathCallback(); +} + +void Fuzzer::DeathCallback() { + Printf("DEATH:\n"); + Print(CurrentUnit, "\n"); + PrintUnitInASCIIOrTokens(CurrentUnit, "\n"); + WriteToCrash(CurrentUnit, "crash-"); +} + +void Fuzzer::StaticAlarmCallback() { + assert(F); + F->AlarmCallback(); +} + +void Fuzzer::AlarmCallback() { + assert(Options.UnitTimeoutSec > 0); + size_t Seconds = + duration_cast<seconds>(system_clock::now() - UnitStartTime).count(); + if (Seconds == 0) return; + if (Options.Verbosity >= 2) + Printf("AlarmCallback %zd\n", Seconds); + if (Seconds >= (size_t)Options.UnitTimeoutSec) { + Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds); + Printf(" and the timeout value is %d (use -timeout=N to change)\n", + Options.UnitTimeoutSec); + Print(CurrentUnit, "\n"); + PrintUnitInASCIIOrTokens(CurrentUnit, "\n"); + WriteToCrash(CurrentUnit, "timeout-"); + exit(1); + } +} + +void Fuzzer::PrintStats(const char *Where, size_t Cov, const char *End) { + if (!Options.Verbosity) return; + size_t Seconds = secondsSinceProcessStartUp(); + size_t ExecPerSec = (Seconds ? TotalNumberOfRuns / Seconds : 0); + Printf("#%zd\t%s cov %zd bits %zd units %zd exec/s %zd %s", TotalNumberOfRuns, + Where, Cov, TotalBits(), Corpus.size(), ExecPerSec, End); +} + +void Fuzzer::RereadOutputCorpus() { + if (Options.OutputCorpus.empty()) return; + std::vector<Unit> AdditionalCorpus; + ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus, + &EpochOfLastReadOfOutputCorpus); + if (Corpus.empty()) { + Corpus = AdditionalCorpus; + return; + } + if (!Options.Reload) return; + if (Options.Verbosity >= 2) + Printf("Reload: read %zd new units.\n", AdditionalCorpus.size()); + for (auto &X : AdditionalCorpus) { + if (X.size() > (size_t)Options.MaxLen) + X.resize(Options.MaxLen); + if (UnitHashesAddedToCorpus.insert(Hash(X)).second) { + CurrentUnit.clear(); + CurrentUnit.insert(CurrentUnit.begin(), X.begin(), X.end()); + size_t NewCoverage = RunOne(CurrentUnit); + if (NewCoverage) { + Corpus.push_back(X); + if (Options.Verbosity >= 1) + PrintStats("RELOAD", NewCoverage); + } + } + } +} + +void Fuzzer::ShuffleAndMinimize() { + size_t MaxCov = 0; + bool PreferSmall = + (Options.PreferSmallDuringInitialShuffle == 1 || + (Options.PreferSmallDuringInitialShuffle == -1 && rand() % 2)); + if (Options.Verbosity) + Printf("PreferSmall: %d\n", PreferSmall); + PrintStats("READ ", 0); + std::vector<Unit> NewCorpus; + std::random_shuffle(Corpus.begin(), Corpus.end()); + if (PreferSmall) + std::stable_sort( + Corpus.begin(), Corpus.end(), + [](const Unit &A, const Unit &B) { return A.size() < B.size(); }); + Unit &U = CurrentUnit; + for (const auto &C : Corpus) { + for (size_t First = 0; First < 1; First++) { + U.clear(); + size_t Last = std::min(First + Options.MaxLen, C.size()); + U.insert(U.begin(), C.begin() + First, C.begin() + Last); + size_t NewCoverage = RunOne(U); + if (NewCoverage) { + MaxCov = NewCoverage; + NewCorpus.push_back(U); + if (Options.Verbosity >= 2) + Printf("NEW0: %zd L %zd\n", NewCoverage, U.size()); + } + } + } + Corpus = NewCorpus; + for (auto &X : Corpus) + UnitHashesAddedToCorpus.insert(Hash(X)); + PrintStats("INITED", MaxCov); +} + +size_t Fuzzer::RunOne(const Unit &U) { + UnitStartTime = system_clock::now(); + TotalNumberOfRuns++; + size_t Res = 0; + if (Options.UseFullCoverageSet) + Res = RunOneMaximizeFullCoverageSet(U); + else + Res = RunOneMaximizeTotalCoverage(U); + auto UnitStopTime = system_clock::now(); + auto TimeOfUnit = + duration_cast<seconds>(UnitStopTime - UnitStartTime).count(); + if (TimeOfUnit > TimeOfLongestUnitInSeconds) { + TimeOfLongestUnitInSeconds = TimeOfUnit; + Printf("Longest unit: %zd s:\n", TimeOfLongestUnitInSeconds); + Print(U, "\n"); + } + return Res; +} + +void Fuzzer::RunOneAndUpdateCorpus(const Unit &U) { + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) + return; + ReportNewCoverage(RunOne(U), U); +} + +static uintptr_t HashOfArrayOfPCs(uintptr_t *PCs, uintptr_t NumPCs) { + uintptr_t Res = 0; + for (uintptr_t i = 0; i < NumPCs; i++) { + Res = (Res + PCs[i]) * 7; + } + return Res; +} + +Unit Fuzzer::SubstituteTokens(const Unit &U) const { + Unit Res; + for (auto Idx : U) { + if (Idx < Options.Tokens.size()) { + std::string Token = Options.Tokens[Idx]; + Res.insert(Res.end(), Token.begin(), Token.end()); + } else { + Res.push_back(' '); + } + } + // FIXME: Apply DFSan labels. + return Res; +} + +void Fuzzer::ExecuteCallback(const Unit &U) { + if (Options.Tokens.empty()) { + USF.TargetFunction(U.data(), U.size()); + } else { + auto T = SubstituteTokens(U); + USF.TargetFunction(T.data(), T.size()); + } +} + +// Experimental. +// Fuly reset the current coverage state, run a single unit, +// compute a hash function from the full coverage set, +// return non-zero if the hash value is new. +// This produces tons of new units and as is it's only suitable for small tests, +// e.g. test/FullCoverageSetTest.cpp. FIXME: make it scale. +size_t Fuzzer::RunOneMaximizeFullCoverageSet(const Unit &U) { + __sanitizer_reset_coverage(); + ExecuteCallback(U); + uintptr_t *PCs; + uintptr_t NumPCs =__sanitizer_get_coverage_guards(&PCs); + if (FullCoverageSets.insert(HashOfArrayOfPCs(PCs, NumPCs)).second) + return FullCoverageSets.size(); + return 0; +} + +size_t Fuzzer::RunOneMaximizeTotalCoverage(const Unit &U) { + size_t NumCounters = __sanitizer_get_number_of_counters(); + if (Options.UseCounters) { + CounterBitmap.resize(NumCounters); + __sanitizer_update_counter_bitset_and_clear_counters(0); + } + size_t OldCoverage = __sanitizer_get_total_unique_coverage(); + ExecuteCallback(U); + size_t NewCoverage = __sanitizer_get_total_unique_coverage(); + size_t NumNewBits = 0; + if (Options.UseCounters) + NumNewBits = __sanitizer_update_counter_bitset_and_clear_counters( + CounterBitmap.data()); + + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && Options.Verbosity) + PrintStats("pulse ", NewCoverage); + + if (NewCoverage > OldCoverage || NumNewBits) + return NewCoverage; + return 0; +} + +void Fuzzer::WriteToOutputCorpus(const Unit &U) { + if (Options.OutputCorpus.empty()) return; + std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U)); + WriteToFile(U, Path); + if (Options.Verbosity >= 2) + Printf("Written to %s\n", Path.c_str()); +} + +void Fuzzer::WriteToCrash(const Unit &U, const char *Prefix) { + std::string Path = Prefix + Hash(U); + WriteToFile(U, Path); + Printf("CRASHED; file written to %s\nBase64: ", Path.c_str()); + PrintFileAsBase64(Path); +} + +void Fuzzer::SaveCorpus() { + if (Options.OutputCorpus.empty()) return; + for (const auto &U : Corpus) + WriteToFile(U, DirPlusFile(Options.OutputCorpus, Hash(U))); + if (Options.Verbosity) + Printf("Written corpus of %zd files to %s\n", Corpus.size(), + Options.OutputCorpus.c_str()); +} + +void Fuzzer::ReportNewCoverage(size_t NewCoverage, const Unit &U) { + if (!NewCoverage) return; + Corpus.push_back(U); + UnitHashesAddedToCorpus.insert(Hash(U)); + PrintStats("NEW ", NewCoverage, ""); + if (Options.Verbosity) { + Printf(" L: %zd", U.size()); + if (U.size() < 30) { + Printf(" "); + PrintUnitInASCIIOrTokens(U, "\t"); + Print(U); + } + Printf("\n"); + } + WriteToOutputCorpus(U); + if (Options.ExitOnFirst) + exit(0); +} + +void Fuzzer::MutateAndTestOne(Unit *U) { + for (int i = 0; i < Options.MutateDepth; i++) { + StartTraceRecording(); + size_t Size = U->size(); + U->resize(Options.MaxLen); + size_t NewSize = USF.Mutate(U->data(), Size, U->size()); + assert(NewSize > 0 && NewSize <= (size_t)Options.MaxLen); + U->resize(NewSize); + RunOneAndUpdateCorpus(*U); + size_t NumTraceBasedMutations = StopTraceRecording(); + for (size_t j = 0; j < NumTraceBasedMutations; j++) { + ApplyTraceBasedMutation(j, U); + RunOneAndUpdateCorpus(*U); + } + } +} + +void Fuzzer::Loop(size_t NumIterations) { + for (size_t i = 1; i <= NumIterations; i++) { + for (size_t J1 = 0; J1 < Corpus.size(); J1++) { + SyncCorpus(); + RereadOutputCorpus(); + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) + return; + // First, simply mutate the unit w/o doing crosses. + CurrentUnit = Corpus[J1]; + MutateAndTestOne(&CurrentUnit); + // Now, cross with others. + if (Options.DoCrossOver && !Corpus[J1].empty()) { + for (size_t J2 = 0; J2 < Corpus.size(); J2++) { + CurrentUnit.resize(Options.MaxLen); + size_t NewSize = USF.CrossOver( + Corpus[J1].data(), Corpus[J1].size(), Corpus[J2].data(), + Corpus[J2].size(), CurrentUnit.data(), CurrentUnit.size()); + assert(NewSize > 0 && NewSize <= (size_t)Options.MaxLen); + CurrentUnit.resize(NewSize); + MutateAndTestOne(&CurrentUnit); + } + } + } + } +} + +void Fuzzer::SyncCorpus() { + if (Options.SyncCommand.empty() || Options.OutputCorpus.empty()) return; + auto Now = system_clock::now(); + if (duration_cast<seconds>(Now - LastExternalSync).count() < + Options.SyncTimeout) + return; + LastExternalSync = Now; + ExecuteCommand(Options.SyncCommand + " " + Options.OutputCorpus); +} + +} // namespace fuzzer |