summaryrefslogtreecommitdiffstats
path: root/llvm/lib/Fuzzer/FuzzerLoop.cpp
diff options
context:
space:
mode:
authorKostya Serebryany <kcc@google.com>2015-01-27 22:08:41 +0000
committerKostya Serebryany <kcc@google.com>2015-01-27 22:08:41 +0000
commitd53b43fe117c619aad57b5bf80000d1617eb142d (patch)
tree71b45ceb5839c384fcd2501ad10c70f084e2f86f /llvm/lib/Fuzzer/FuzzerLoop.cpp
parent7a503694febda8648e39417ba9039e3adeed5e36 (diff)
downloadbcm5719-llvm-d53b43fe117c619aad57b5bf80000d1617eb142d.tar.gz
bcm5719-llvm-d53b43fe117c619aad57b5bf80000d1617eb142d.zip
Add a Fuzzer library
Summary: A simple genetic in-process coverage-guided fuzz testing library. I've used this fuzzer to test clang-format (it found 12+ bugs, thanks djasper@ for the fixes!) and it may also help us test other parts of LLVM. So why not keep it in the LLVM repository? I plan to add the cmake build rules later (in a separate patch, if that's ok) and also add a clang-format-fuzzer target. See README.txt for details. Test Plan: Tests will follow separately. Reviewers: djasper, chandlerc, rnk Reviewed By: rnk Subscribers: majnemer, ygribov, dblaikie, llvm-commits Differential Revision: http://reviews.llvm.org/D7184 llvm-svn: 227252
Diffstat (limited to 'llvm/lib/Fuzzer/FuzzerLoop.cpp')
-rw-r--r--llvm/lib/Fuzzer/FuzzerLoop.cpp161
1 files changed, 161 insertions, 0 deletions
diff --git a/llvm/lib/Fuzzer/FuzzerLoop.cpp b/llvm/lib/Fuzzer/FuzzerLoop.cpp
new file mode 100644
index 00000000000..9ad2bd729aa
--- /dev/null
+++ b/llvm/lib/Fuzzer/FuzzerLoop.cpp
@@ -0,0 +1,161 @@
+//===- 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/asan_interface.h>
+#include <algorithm>
+#include <string>
+#include <iostream>
+#include <stdlib.h>
+
+// This function should be defined by the user.
+extern "C" void TestOneInput(const uint8_t *Data, size_t Size);
+
+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 > 60) {
+ Print(CurrentUnit, "\n");
+ PrintASCII(CurrentUnit, "\n");
+ WriteToCrash(CurrentUnit, "timeout-");
+ }
+ abort();
+}
+
+void Fuzzer::ShuffleAndMinimize() {
+ if (Options.Verbosity)
+ std::cerr << "Shuffle: " << Corpus.size() << "\n";
+ std::vector<Unit> NewCorpus;
+ random_shuffle(Corpus.begin(), Corpus.end());
+ 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 << "\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++;
+ size_t OldCoverage = __sanitizer_get_total_unique_coverage();
+ TestOneInput(U.data(), U.size());
+ size_t NewCoverage = __sanitizer_get_total_unique_coverage();
+ if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && Options.Verbosity) {
+ size_t Seconds =
+ duration_cast<seconds>(system_clock::now() - ProcessStartTime).count();
+ 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 = 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;
+}
+
+size_t Fuzzer::MutateAndTestOne(Unit *U) {
+ size_t NewUnits = 0;
+ for (size_t i = 0; i < Options.MutateDepth; i++) {
+ Mutate(U, Options.MaxLen);
+ if (U->empty()) continue;
+ size_t NewCoverage = RunOne(*U);
+ if (NewCoverage) {
+ Corpus.push_back(*U);
+ NewUnits++;
+ if (Options.Verbosity) {
+ std::cerr << "#" << TotalNumberOfRuns
+ << "\tNEW: " << NewCoverage
+ << " L: " << U->size()
+ << "\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++) {
+ if (Options.DoCrossOver) {
+ for (size_t J1 = 0; J1 < Corpus.size(); J1++) {
+ for (size_t J2 = 0; J2 < Corpus.size(); J2++) {
+ CurrentUnit.clear();
+ CrossOver(Corpus[J1], Corpus[J2], &CurrentUnit, Options.MaxLen);
+ NewUnits += MutateAndTestOne(&CurrentUnit);
+ }
+ }
+ } else { // No CrossOver
+ for (size_t J = 0; J < Corpus.size(); J++) {
+ CurrentUnit = Corpus[J];
+ NewUnits += MutateAndTestOne(&CurrentUnit);
+ }
+ }
+ }
+ return NewUnits;
+}
+
+} // namespace fuzzer
OpenPOWER on IntegriCloud