summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDokyung Song <dokyungs@google.com>2020-07-08 19:30:53 +0000
committerDokyung Song <dokyungs@google.com>2020-09-09 03:28:53 +0000
commit1bb1eac6b177739429e78703b265e7546792fd64 (patch)
treec886d96f2abbfcadbcdc11423b4895b9a637004c
parentc2b7b9b642b3247061c4850e9c868c903e3b9654 (diff)
downloadllvm-1bb1eac6b177739429e78703b265e7546792fd64.tar.gz
[libFuzzer] Add a command-line option for tracing mutation of corpus inputs in the dot graph format.
This patch adds a new command-line option -mutation_graph_file=FILE for debugging purposes, which traces how corpus inputs evolve during a fuzzing run. For each new input that is added to the corpus, a new vertex corresponding to the added input, as well as a new edge that connects its base input to itself are written to the given file. Each vertex is labeled with the filename of the input, and each edge is labeled with the mutation sequence that led to the input w.r.t. its base input. The format of the mutation graph file is the dot file format. Once prepended and appended with "graph {" and "}", respectively, the graph becomes a valid dot file and can be visualized. Differential Revision: https://reviews.llvm.org/D86560
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerDriver.cpp2
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerFlags.def5
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerIO.cpp13
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerIO.h3
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerLoop.cpp33
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerMutate.cpp9
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerMutate.h2
-rw-r--r--compiler-rt/lib/fuzzer/FuzzerOptions.h1
-rw-r--r--compiler-rt/test/fuzzer/mutation-graph.test17
9 files changed, 85 insertions, 0 deletions
diff --git a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp
index caafd1dbb0a7..57df1238c398 100644
--- a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp
+++ b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp
@@ -755,6 +755,8 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
Options.FeaturesDir = Flags.features_dir;
ValidateDirectoryExists(Options.FeaturesDir, Flags.create_missing_dirs);
}
+ if (Flags.mutation_graph_file)
+ Options.MutationGraphFile = Flags.mutation_graph_file;
if (Flags.collect_data_flow)
Options.CollectDataFlow = Flags.collect_data_flow;
if (Flags.stop_file)
diff --git a/compiler-rt/lib/fuzzer/FuzzerFlags.def b/compiler-rt/lib/fuzzer/FuzzerFlags.def
index fdb8362cef9d..c9a787e03833 100644
--- a/compiler-rt/lib/fuzzer/FuzzerFlags.def
+++ b/compiler-rt/lib/fuzzer/FuzzerFlags.def
@@ -88,6 +88,11 @@ FUZZER_FLAG_STRING(features_dir, "internal flag. Used to dump feature sets on di
"Every time a new input is added to the corpus, a corresponding file in the features_dir"
" is created containing the unique features of that input."
" Features are stored in binary format.")
+FUZZER_FLAG_STRING(mutation_graph_file, "Saves a graph (in DOT format) to"
+ " mutation_graph_file. The graph contains a vertex for each input that has"
+ " unique coverage; directed edges are provided between parents and children"
+ " where the child has unique coverage, and are recorded with the type of"
+ " mutation that caused the child.")
FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
FUZZER_FLAG_INT(use_memmem, 1,
"Use hints from intercepting memmem, strstr, etc")
diff --git a/compiler-rt/lib/fuzzer/FuzzerIO.cpp b/compiler-rt/lib/fuzzer/FuzzerIO.cpp
index c3330c3425d0..54a7219fc0e0 100644
--- a/compiler-rt/lib/fuzzer/FuzzerIO.cpp
+++ b/compiler-rt/lib/fuzzer/FuzzerIO.cpp
@@ -77,6 +77,19 @@ void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path) {
fclose(Out);
}
+void AppendToFile(const std::string &Data, const std::string &Path) {
+ AppendToFile(reinterpret_cast<const uint8_t *>(Data.data()), Data.size(),
+ Path);
+}
+
+void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path) {
+ FILE *Out = fopen(Path.c_str(), "a");
+ if (!Out)
+ return;
+ fwrite(Data, sizeof(Data[0]), Size, Out);
+ fclose(Out);
+}
+
void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V,
long *Epoch, size_t MaxSize, bool ExitOnError) {
long E = Epoch ? *Epoch : 0;
diff --git a/compiler-rt/lib/fuzzer/FuzzerIO.h b/compiler-rt/lib/fuzzer/FuzzerIO.h
index 6e3a0b470c5f..abd25110d07d 100644
--- a/compiler-rt/lib/fuzzer/FuzzerIO.h
+++ b/compiler-rt/lib/fuzzer/FuzzerIO.h
@@ -29,6 +29,9 @@ void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path);
void WriteToFile(const std::string &Data, const std::string &Path);
void WriteToFile(const Unit &U, const std::string &Path);
+void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path);
+void AppendToFile(const std::string &Data, const std::string &Path);
+
void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V,
long *Epoch, size_t MaxSize, bool ExitOnError);
diff --git a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp
index f9986dd8eea5..ce8c2fb74714 100644
--- a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp
+++ b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp
@@ -463,6 +463,37 @@ static void RenameFeatureSetFile(const std::string &FeaturesDir,
DirPlusFile(FeaturesDir, NewFile));
}
+static void WriteEdgeToMutationGraphFile(const std::string &MutationGraphFile,
+ const InputInfo *II,
+ const InputInfo *BaseII,
+ const std::string &MS) {
+ if (MutationGraphFile.empty())
+ return;
+
+ std::string Sha1 = Sha1ToString(II->Sha1);
+
+ std::string OutputString;
+
+ // Add a new vertex.
+ OutputString.append("\"");
+ OutputString.append(Sha1);
+ OutputString.append("\"\n");
+
+ // Add a new edge if there is base input.
+ if (BaseII) {
+ std::string BaseSha1 = Sha1ToString(BaseII->Sha1);
+ OutputString.append("\"");
+ OutputString.append(BaseSha1);
+ OutputString.append("\" -> \"");
+ OutputString.append(Sha1);
+ OutputString.append("\" [label=\"");
+ OutputString.append(MS);
+ OutputString.append("\"];\n");
+ }
+
+ AppendToFile(OutputString, MutationGraphFile);
+}
+
bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
InputInfo *II, bool ForceAddToCorpus,
bool *FoundUniqFeatures) {
@@ -497,6 +528,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);
+ WriteEdgeToMutationGraphFile(Options.MutationGraphFile, NewII, II,
+ MD.MutationSequence());
return true;
}
if (II && FoundUniqFeaturesOfII &&
diff --git a/compiler-rt/lib/fuzzer/FuzzerMutate.cpp b/compiler-rt/lib/fuzzer/FuzzerMutate.cpp
index df9ada45bb03..121b450e8b8c 100644
--- a/compiler-rt/lib/fuzzer/FuzzerMutate.cpp
+++ b/compiler-rt/lib/fuzzer/FuzzerMutate.cpp
@@ -494,6 +494,15 @@ void MutationDispatcher::PrintMutationSequence() {
}
}
+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);
}
diff --git a/compiler-rt/lib/fuzzer/FuzzerMutate.h b/compiler-rt/lib/fuzzer/FuzzerMutate.h
index 6cbce8027624..3ce3159f6893 100644
--- a/compiler-rt/lib/fuzzer/FuzzerMutate.h
+++ b/compiler-rt/lib/fuzzer/FuzzerMutate.h
@@ -26,6 +26,8 @@ public:
void StartMutationSequence();
/// Print the current sequence of mutations.
void PrintMutationSequence();
+ /// 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.
diff --git a/compiler-rt/lib/fuzzer/FuzzerOptions.h b/compiler-rt/lib/fuzzer/FuzzerOptions.h
index b17a7474d38f..706e1c64c706 100644
--- a/compiler-rt/lib/fuzzer/FuzzerOptions.h
+++ b/compiler-rt/lib/fuzzer/FuzzerOptions.h
@@ -59,6 +59,7 @@ struct FuzzingOptions {
std::string DataFlowTrace;
std::string CollectDataFlow;
std::string FeaturesDir;
+ std::string MutationGraphFile;
std::string StopFile;
bool SaveArtifacts = true;
bool PrintNEW = true; // Print a status line when new units are found;
diff --git a/compiler-rt/test/fuzzer/mutation-graph.test b/compiler-rt/test/fuzzer/mutation-graph.test
new file mode 100644
index 000000000000..7774a500395e
--- /dev/null
+++ b/compiler-rt/test/fuzzer/mutation-graph.test
@@ -0,0 +1,17 @@
+REQUIRES: linux, x86_64
+RUN: %cpp_compiler %S/SimpleTest.cpp -o %t-SimpleTest
+
+RUN: rm -rf %t-SimpleTestGraph
+
+RUN: not %run %t-SimpleTest -seed=1 -max_len=3 -mutation_graph_file=%t-SimpleTestGraph 2>&1 | FileCheck %s
+CHECK: BINGO
+
+RUN: cat %t-SimpleTestGraph | FileCheck %s --check-prefix=GRAPH
+
+# A vertex and edge that correspond to the discovery of "H"
+GRAPH: "7cf184f4c67ad58283ecb19349720b0cae756829"
+GRAPH: {{.*}} -> "7cf184f4c67ad58283ecb19349720b0cae756829" [label="{{.*}}"];
+
+# A vertex and edge that correspond to the discovery of "Hi"
+GRAPH: "94dd9e08c129c785f7f256e82fbe0a30e6d1ae40"
+GRAPH: {{.*}} -> "94dd9e08c129c785f7f256e82fbe0a30e6d1ae40" [label="{{.*}}"];