aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Fuzzer/FuzzerLoop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Fuzzer/FuzzerLoop.cpp')
-rw-r--r--lib/Fuzzer/FuzzerLoop.cpp233
1 files changed, 233 insertions, 0 deletions
diff --git a/lib/Fuzzer/FuzzerLoop.cpp b/lib/Fuzzer/FuzzerLoop.cpp
new file mode 100644
index 0000000..70b63eb
--- /dev/null
+++ b/lib/Fuzzer/FuzzerLoop.cpp
@@ -0,0 +1,233 @@
+//===- 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>
+#include <iostream>
+
+namespace fuzzer {
+
+// static
+Unit Fuzzer::CurrentUnit;
+system_clock::time_point Fuzzer::UnitStartTime;
+
+void Fuzzer::SetDeathCallback() {
+ __sanitizer_set_death_callback(DeathCallback);
+}
+
+void Fuzzer::DeathCallback() {
+ std::cerr << "DEATH: " << std::endl;
+ Print(CurrentUnit, "\n");
+ PrintASCII(CurrentUnit, "\n");
+ WriteToCrash(CurrentUnit, "crash-");
+}
+
+void Fuzzer::AlarmCallback() {
+ size_t Seconds =
+ duration_cast<seconds>(system_clock::now() - UnitStartTime).count();
+ std::cerr << "ALARM: working on the last Unit for " << Seconds << " seconds"
+ << std::endl;
+ if (Seconds >= 3) {
+ Print(CurrentUnit, "\n");
+ PrintASCII(CurrentUnit, "\n");
+ WriteToCrash(CurrentUnit, "timeout-");
+ }
+ exit(1);
+}
+
+void Fuzzer::ShuffleAndMinimize() {
+ bool PreferSmall =
+ (Options.PreferSmallDuringInitialShuffle == 1 ||
+ (Options.PreferSmallDuringInitialShuffle == -1 && rand() % 2));
+ if (Options.Verbosity)
+ std::cerr << "Shuffle: Size: " << Corpus.size()
+ << " prefer small: " << PreferSmall
+ << "\n";
+ 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(); });
+ size_t MaxCov = 0;
+ 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)
+ std::cerr << "NEW0: " << NewCoverage
+ << " L " << U.size()
+ << "\n";
+ }
+ }
+ }
+ Corpus = NewCorpus;
+ if (Options.Verbosity)
+ std::cerr << "Shuffle done: " << Corpus.size() << " IC: " << MaxCov << "\n";
+}
+
+size_t Fuzzer::RunOne(const Unit &U) {
+ UnitStartTime = system_clock::now();
+ TotalNumberOfRuns++;
+ if (Options.UseFullCoverageSet)
+ return RunOneMaximizeFullCoverageSet(U);
+ if (Options.UseCoveragePairs)
+ return RunOneMaximizeCoveragePairs(U);
+ return RunOneMaximizeTotalCoverage(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;
+}
+
+// Experimental. Does not yet scale.
+// Fuly reset the current coverage state, run a single unit,
+// collect all coverage pairs and return non-zero if a new pair is observed.
+size_t Fuzzer::RunOneMaximizeCoveragePairs(const Unit &U) {
+ __sanitizer_reset_coverage();
+ Callback(U.data(), U.size());
+ uintptr_t *PCs;
+ uintptr_t NumPCs = __sanitizer_get_coverage_guards(&PCs);
+ bool HasNewPairs = false;
+ for (uintptr_t i = 0; i < NumPCs; i++) {
+ if (!PCs[i]) continue;
+ for (uintptr_t j = 0; j < NumPCs; j++) {
+ if (!PCs[j]) continue;
+ uint64_t Pair = (i << 32) | j;
+ HasNewPairs |= CoveragePairs.insert(Pair).second;
+ }
+ }
+ if (HasNewPairs)
+ return CoveragePairs.size();
+ return 0;
+}
+
+// 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();
+ Callback(U.data(), U.size());
+ 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 OldCoverage = __sanitizer_get_total_unique_coverage();
+ Callback(U.data(), U.size());
+ size_t NewCoverage = __sanitizer_get_total_unique_coverage();
+ if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && Options.Verbosity) {
+ size_t Seconds = secondsSinceProcessStartUp();
+ std::cerr
+ << "#" << TotalNumberOfRuns
+ << "\tcov: " << NewCoverage
+ << "\texec/s: " << (Seconds ? TotalNumberOfRuns / Seconds : 0) << "\n";
+ }
+ if (NewCoverage > OldCoverage)
+ 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)
+ std::cerr << "Written to " << Path << std::endl;
+}
+
+void Fuzzer::WriteToCrash(const Unit &U, const char *Prefix) {
+ std::string Path = Prefix + Hash(U);
+ WriteToFile(U, Path);
+ std::cerr << "CRASHED; file written to " << Path << std::endl;
+}
+
+void Fuzzer::SaveCorpus() {
+ if (Options.OutputCorpus.empty()) return;
+ for (const auto &U : Corpus)
+ WriteToFile(U, DirPlusFile(Options.OutputCorpus, Hash(U)));
+ if (Options.Verbosity)
+ std::cerr << "Written corpus of " << Corpus.size() << " files to "
+ << Options.OutputCorpus << "\n";
+}
+
+size_t Fuzzer::MutateAndTestOne(Unit *U) {
+ size_t NewUnits = 0;
+ for (int i = 0; i < Options.MutateDepth; i++) {
+ if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
+ return NewUnits;
+ Mutate(U, Options.MaxLen);
+ size_t NewCoverage = RunOne(*U);
+ if (NewCoverage) {
+ Corpus.push_back(*U);
+ NewUnits++;
+ if (Options.Verbosity) {
+ std::cerr << "#" << TotalNumberOfRuns
+ << "\tNEW: " << NewCoverage
+ << " L: " << U->size()
+ << " S: " << Corpus.size()
+ << " I: " << i
+ << "\t";
+ if (U->size() < 30) {
+ PrintASCII(*U);
+ std::cerr << "\t";
+ Print(*U);
+ }
+ std::cerr << "\n";
+ }
+ WriteToOutputCorpus(*U);
+ if (Options.ExitOnFirst)
+ exit(0);
+ }
+ }
+ return NewUnits;
+}
+
+size_t Fuzzer::Loop(size_t NumIterations) {
+ size_t NewUnits = 0;
+ for (size_t i = 1; i <= NumIterations; i++) {
+ for (size_t J1 = 0; J1 < Corpus.size(); J1++) {
+ if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
+ return NewUnits;
+ // First, simply mutate the unit w/o doing crosses.
+ CurrentUnit = Corpus[J1];
+ NewUnits += MutateAndTestOne(&CurrentUnit);
+ // Now, cross with others.
+ if (Options.DoCrossOver) {
+ for (size_t J2 = 0; J2 < Corpus.size(); J2++) {
+ CurrentUnit.clear();
+ CrossOver(Corpus[J1], Corpus[J2], &CurrentUnit, Options.MaxLen);
+ NewUnits += MutateAndTestOne(&CurrentUnit);
+ }
+ }
+ }
+ }
+ return NewUnits;
+}
+
+} // namespace fuzzer