aboutsummaryrefslogtreecommitdiffstats
path: root/lib/fuzzer
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2018-07-28 11:06:48 +0000
committerDimitry Andric <dim@FreeBSD.org>2018-07-28 11:06:48 +0000
commit93c1b73a09a52d4a265f683bf1954b08bb430049 (patch)
tree5543464d74945196cc890e9d9099e5d0660df7eb /lib/fuzzer
parent0d8e7490d6e8a13a8f0977d9b7771803b9f64ea0 (diff)
downloadsrc-93c1b73a09a52d4a265f683bf1954b08bb430049.tar.gz
src-93c1b73a09a52d4a265f683bf1954b08bb430049.zip
Vendor import of compiler-rt trunk r338150:vendor/compiler-rt/compiler-rt-trunk-r338150
Notes
Notes: svn path=/vendor/compiler-rt/dist/; revision=336817 svn path=/vendor/compiler-rt/compiler-rt-trunk-r338150/; revision=336818; tag=vendor/compiler-rt/compiler-rt-trunk-r338150
Diffstat (limited to 'lib/fuzzer')
-rw-r--r--lib/fuzzer/CMakeLists.txt81
-rw-r--r--lib/fuzzer/FuzzerClangCounters.cpp49
-rw-r--r--lib/fuzzer/FuzzerCommand.h3
-rw-r--r--lib/fuzzer/FuzzerCorpus.h59
-rw-r--r--lib/fuzzer/FuzzerDataFlowTrace.cpp91
-rw-r--r--lib/fuzzer/FuzzerDataFlowTrace.h56
-rw-r--r--lib/fuzzer/FuzzerDefs.h45
-rw-r--r--lib/fuzzer/FuzzerDictionary.h12
-rw-r--r--lib/fuzzer/FuzzerDriver.cpp19
-rw-r--r--lib/fuzzer/FuzzerExtFunctions.def4
-rw-r--r--lib/fuzzer/FuzzerExtFunctionsWeak.cpp5
-rw-r--r--lib/fuzzer/FuzzerExtraCounters.cpp3
-rw-r--r--lib/fuzzer/FuzzerFlags.def27
-rw-r--r--lib/fuzzer/FuzzerIO.cpp8
-rw-r--r--lib/fuzzer/FuzzerIO.h2
-rw-r--r--lib/fuzzer/FuzzerIOPosix.cpp2
-rw-r--r--lib/fuzzer/FuzzerInterface.h22
-rw-r--r--lib/fuzzer/FuzzerInternal.h26
-rw-r--r--lib/fuzzer/FuzzerLoop.cpp153
-rw-r--r--lib/fuzzer/FuzzerMain.cpp2
-rw-r--r--lib/fuzzer/FuzzerMutate.cpp93
-rw-r--r--lib/fuzzer/FuzzerMutate.h21
-rw-r--r--lib/fuzzer/FuzzerOptions.h11
-rw-r--r--lib/fuzzer/FuzzerShmemPosix.cpp9
-rw-r--r--lib/fuzzer/FuzzerTracePC.cpp240
-rw-r--r--lib/fuzzer/FuzzerTracePC.h53
-rw-r--r--lib/fuzzer/FuzzerUtil.cpp19
-rw-r--r--lib/fuzzer/FuzzerUtil.h4
-rw-r--r--lib/fuzzer/FuzzerUtilFuchsia.cpp395
-rw-r--r--lib/fuzzer/FuzzerUtilLinux.cpp5
-rw-r--r--lib/fuzzer/FuzzerUtilPosix.cpp3
-rw-r--r--lib/fuzzer/afl/afl_driver.cpp39
-rwxr-xr-xlib/fuzzer/build.sh2
-rw-r--r--lib/fuzzer/dataflow/DataFlow.cpp217
-rwxr-xr-xlib/fuzzer/scripts/collect_data_flow.py79
-rwxr-xr-xlib/fuzzer/scripts/merge_data_flow.py36
-rwxr-xr-xlib/fuzzer/scripts/unbalanced_allocs.py10
-rw-r--r--lib/fuzzer/tests/CMakeLists.txt32
-rw-r--r--lib/fuzzer/tests/FuzzerUnittest.cpp29
39 files changed, 1564 insertions, 402 deletions
diff --git a/lib/fuzzer/CMakeLists.txt b/lib/fuzzer/CMakeLists.txt
index 9769be52ae01..679318e460b5 100644
--- a/lib/fuzzer/CMakeLists.txt
+++ b/lib/fuzzer/CMakeLists.txt
@@ -1,6 +1,6 @@
set(LIBFUZZER_SOURCES
- FuzzerClangCounters.cpp
FuzzerCrossOver.cpp
+ FuzzerDataFlowTrace.cpp
FuzzerDriver.cpp
FuzzerExtFunctionsDlsym.cpp
FuzzerExtFunctionsDlsymWin.cpp
@@ -13,6 +13,7 @@ set(LIBFUZZER_SOURCES
FuzzerMerge.cpp
FuzzerMutate.cpp
FuzzerSHA1.cpp
+ FuzzerShmemFuchsia.cpp
FuzzerShmemPosix.cpp
FuzzerShmemWindows.cpp
FuzzerTracePC.cpp
@@ -21,8 +22,29 @@ set(LIBFUZZER_SOURCES
FuzzerUtilFuchsia.cpp
FuzzerUtilLinux.cpp
FuzzerUtilPosix.cpp
- FuzzerUtilWindows.cpp
- )
+ FuzzerUtilWindows.cpp)
+
+set(LIBFUZZER_HEADERS
+ FuzzerCommand.h
+ FuzzerCorpus.h
+ FuzzerDataFlowTrace.h
+ FuzzerDefs.h
+ FuzzerDictionary.h
+ FuzzerExtFunctions.def
+ FuzzerExtFunctions.h
+ FuzzerFlags.def
+ FuzzerIO.h
+ FuzzerInterface.h
+ FuzzerInternal.h
+ FuzzerMerge.h
+ FuzzerMutate.h
+ FuzzerOptions.h
+ FuzzerRandom.h
+ FuzzerSHA1.h
+ FuzzerShmem.h
+ FuzzerTracePC.h
+ FuzzerUtil.h
+ FuzzerValueBitMap.h)
CHECK_CXX_SOURCE_COMPILES("
static thread_local int blah;
@@ -33,6 +55,14 @@ CHECK_CXX_SOURCE_COMPILES("
set(LIBFUZZER_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+if(OS_NAME MATCHES "Linux|Fuchsia" AND COMPILER_RT_LIBCXX_PATH)
+ list(APPEND LIBFUZZER_CFLAGS -nostdinc++ -D_LIBCPP_ABI_VERSION=Fuzzer)
+ # Remove -stdlib= which is unused when passing -nostdinc++.
+ string(REGEX REPLACE "-stdlib=[a-zA-Z+]*" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
+elseif(TARGET cxx-headers OR HAVE_LIBCXX)
+ set(LIBFUZZER_DEPS cxx-headers)
+endif()
+
append_list_if(COMPILER_RT_HAS_OMIT_FRAME_POINTER_FLAG -fno-omit-frame-pointer LIBFUZZER_CFLAGS)
if (CMAKE_CXX_FLAGS MATCHES "fsanitize-coverage")
@@ -43,21 +73,22 @@ if(NOT HAS_THREAD_LOCAL)
list(APPEND LIBFUZZER_CFLAGS -Dthread_local=__thread)
endif()
-if(APPLE)
- set(FUZZER_SUPPORTED_OS osx)
-endif()
+set(FUZZER_SUPPORTED_OS ${SANITIZER_COMMON_SUPPORTED_OS})
add_compiler_rt_object_libraries(RTfuzzer
OS ${FUZZER_SUPPORTED_OS}
ARCHS ${FUZZER_SUPPORTED_ARCH}
SOURCES ${LIBFUZZER_SOURCES}
- CFLAGS ${LIBFUZZER_CFLAGS})
+ ADDITIONAL_HEADERS ${LIBFUZZER_HEADERS}
+ CFLAGS ${LIBFUZZER_CFLAGS}
+ DEPS ${LIBFUZZER_DEPS})
add_compiler_rt_object_libraries(RTfuzzer_main
OS ${FUZZER_SUPPORTED_OS}
ARCHS ${FUZZER_SUPPORTED_ARCH}
SOURCES FuzzerMain.cpp
- CFLAGS ${LIBFUZZER_CFLAGS})
+ CFLAGS ${LIBFUZZER_CFLAGS}
+ DEPS ${LIBFUZZER_DEPS})
add_compiler_rt_runtime(clang_rt.fuzzer
STATIC
@@ -75,6 +106,40 @@ add_compiler_rt_runtime(clang_rt.fuzzer_no_main
CFLAGS ${LIBFUZZER_CFLAGS}
PARENT_TARGET fuzzer)
+if(OS_NAME MATCHES "Linux|Fuchsia" AND COMPILER_RT_LIBCXX_PATH)
+ macro(partially_link_libcxx name dir arch)
+ set(cxx_${arch}_merge_dir "${CMAKE_CURRENT_BINARY_DIR}/cxx_${arch}_merge.dir")
+ file(MAKE_DIRECTORY ${cxx_${arch}_merge_dir})
+ add_custom_command(TARGET clang_rt.${name}-${arch} POST_BUILD
+ COMMAND ${CMAKE_LINKER} --whole-archive "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>" --no-whole-archive ${dir}/lib/libc++.a -r -o ${name}.o
+ COMMAND ${CMAKE_OBJCOPY} --localize-hidden ${name}.o
+ COMMAND ${CMAKE_COMMAND} -E remove "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>"
+ COMMAND ${CMAKE_AR} qcs "$<TARGET_LINKER_FILE:clang_rt.${name}-${arch}>" ${name}.o
+ WORKING_DIRECTORY ${cxx_${arch}_merge_dir}
+ )
+ endmacro()
+
+ foreach(arch ${FUZZER_SUPPORTED_ARCH})
+ get_target_flags_for_arch(${arch} TARGET_CFLAGS)
+ set(LIBCXX_${arch}_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libcxx_fuzzer_${arch})
+ add_custom_libcxx(libcxx_fuzzer_${arch} ${LIBCXX_${arch}_PREFIX}
+ CFLAGS ${TARGET_CFLAGS}
+ -D_LIBCPP_ABI_VERSION=Fuzzer
+ -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS=1
+ -fvisibility=hidden
+ CMAKE_ARGS -DCMAKE_CXX_COMPILER_WORKS=ON
+ -DLIBCXX_ENABLE_EXCEPTIONS=OFF
+ -DLIBCXX_ENABLE_SHARED=OFF
+ -DLIBCXX_CXX_ABI=none)
+ target_compile_options(RTfuzzer.${arch} PRIVATE -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1)
+ add_dependencies(RTfuzzer.${arch} libcxx_fuzzer_${arch}-build)
+ target_compile_options(RTfuzzer_main.${arch} PRIVATE -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1)
+ add_dependencies(RTfuzzer_main.${arch} libcxx_fuzzer_${arch}-build)
+ partially_link_libcxx(fuzzer_no_main ${LIBCXX_${arch}_PREFIX} ${arch})
+ partially_link_libcxx(fuzzer ${LIBCXX_${arch}_PREFIX} ${arch})
+ endforeach()
+endif()
+
if(COMPILER_RT_INCLUDE_TESTS)
add_subdirectory(tests)
endif()
diff --git a/lib/fuzzer/FuzzerClangCounters.cpp b/lib/fuzzer/FuzzerClangCounters.cpp
deleted file mode 100644
index f69e922cf004..000000000000
--- a/lib/fuzzer/FuzzerClangCounters.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// Coverage counters from Clang's SourceBasedCodeCoverage.
-//===----------------------------------------------------------------------===//
-
-// Support for SourceBasedCodeCoverage is experimental:
-// * Works only for the main binary, not DSOs yet.
-// * Works only on Linux.
-// * Does not implement print_pcs/print_coverage yet.
-// * Is not fully evaluated for performance and sensitivity.
-// We expect large performance drop due to 64-bit counters,
-// and *maybe* better sensitivity due to more fine-grained counters.
-// Preliminary comparison on a single benchmark (RE2) shows
-// a bit worse sensitivity though.
-
-#include "FuzzerDefs.h"
-
-#if LIBFUZZER_LINUX
-__attribute__((weak)) extern uint64_t __start___llvm_prf_cnts;
-__attribute__((weak)) extern uint64_t __stop___llvm_prf_cnts;
-namespace fuzzer {
-uint64_t *ClangCountersBegin() { return &__start___llvm_prf_cnts; }
-uint64_t *ClangCountersEnd() { return &__stop___llvm_prf_cnts; }
-} // namespace fuzzer
-#else
-// TODO: Implement on Mac (if the data shows it's worth it).
-//__attribute__((visibility("hidden")))
-//extern uint64_t CountersStart __asm("section$start$__DATA$__llvm_prf_cnts");
-//__attribute__((visibility("hidden")))
-//extern uint64_t CountersEnd __asm("section$end$__DATA$__llvm_prf_cnts");
-namespace fuzzer {
-uint64_t *ClangCountersBegin() { return nullptr; }
-uint64_t *ClangCountersEnd() { return nullptr; }
-} // namespace fuzzer
-#endif
-
-namespace fuzzer {
-ATTRIBUTE_NO_SANITIZE_ALL
-void ClearClangCounters() { // hand-written memset, don't asan-ify.
- for (auto P = ClangCountersBegin(); P < ClangCountersEnd(); P++)
- *P = 0;
-}
-}
diff --git a/lib/fuzzer/FuzzerCommand.h b/lib/fuzzer/FuzzerCommand.h
index c5500ed21fc1..255f571ecf31 100644
--- a/lib/fuzzer/FuzzerCommand.h
+++ b/lib/fuzzer/FuzzerCommand.h
@@ -29,8 +29,7 @@ public:
// is immutable, meaning this flag effectively marks the end of the mutable
// argument list.
static inline const char *ignoreRemainingArgs() {
- static const char *kIgnoreRemaining = "-ignore_remaining_args=1";
- return kIgnoreRemaining;
+ return "-ignore_remaining_args=1";
}
Command() : CombinedOutAndErr(false) {}
diff --git a/lib/fuzzer/FuzzerCorpus.h b/lib/fuzzer/FuzzerCorpus.h
index 2da929835f45..8ad14656cffc 100644
--- a/lib/fuzzer/FuzzerCorpus.h
+++ b/lib/fuzzer/FuzzerCorpus.h
@@ -12,6 +12,7 @@
#ifndef LLVM_FUZZER_CORPUS
#define LLVM_FUZZER_CORPUS
+#include "FuzzerDataFlowTrace.h"
#include "FuzzerDefs.h"
#include "FuzzerIO.h"
#include "FuzzerRandom.h"
@@ -35,8 +36,9 @@ struct InputInfo {
size_t NumSuccessfullMutations = 0;
bool MayDeleteFile = false;
bool Reduced = false;
+ bool HasFocusFunction = false;
Vector<uint32_t> UniqFeatureSet;
- float FeatureFrequencyScore = 1.0;
+ Vector<uint8_t> DataFlowTraceForFocusFunction;
};
class InputCorpus {
@@ -45,7 +47,6 @@ class InputCorpus {
InputCorpus(const std::string &OutputCorpus) : OutputCorpus(OutputCorpus) {
memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
- memset(FeatureFrequency, 0, sizeof(FeatureFrequency));
}
~InputCorpus() {
for (auto II : Inputs)
@@ -70,10 +71,24 @@ class InputCorpus {
Res = std::max(Res, II->U.size());
return Res;
}
+
+ size_t NumInputsThatTouchFocusFunction() {
+ return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) {
+ return II->HasFocusFunction;
+ });
+ }
+
+ size_t NumInputsWithDataFlowTrace() {
+ return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) {
+ return !II->DataFlowTraceForFocusFunction.empty();
+ });
+ }
+
bool empty() const { return Inputs.empty(); }
const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; }
void AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile,
- const Vector<uint32_t> &FeatureSet) {
+ bool HasFocusFunction, const Vector<uint32_t> &FeatureSet,
+ const DataFlowTrace &DFT, const InputInfo *BaseII) {
assert(!U.empty());
if (FeatureDebug)
Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures);
@@ -83,9 +98,19 @@ class InputCorpus {
II.NumFeatures = NumFeatures;
II.MayDeleteFile = MayDeleteFile;
II.UniqFeatureSet = FeatureSet;
+ II.HasFocusFunction = HasFocusFunction;
std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end());
ComputeSHA1(U.data(), U.size(), II.Sha1);
- Hashes.insert(Sha1ToString(II.Sha1));
+ auto Sha1Str = Sha1ToString(II.Sha1);
+ Hashes.insert(Sha1Str);
+ if (HasFocusFunction)
+ if (auto V = DFT.Get(Sha1Str))
+ II.DataFlowTraceForFocusFunction = *V;
+ // This is a gross heuristic.
+ // Ideally, when we add an element to a corpus we need to know its DFT.
+ // But if we don't, we'll use the DFT of its base input.
+ if (II.DataFlowTraceForFocusFunction.empty() && BaseII)
+ II.DataFlowTraceForFocusFunction = BaseII->DataFlowTraceForFocusFunction;
UpdateCorpusDistribution();
PrintCorpus();
// ValidateFeatureSet();
@@ -157,9 +182,9 @@ class InputCorpus {
void PrintStats() {
for (size_t i = 0; i < Inputs.size(); i++) {
const auto &II = *Inputs[i];
- Printf(" [%zd %s]\tsz: %zd\truns: %zd\tsucc: %zd\n", i,
+ Printf(" [% 3zd %s] sz: % 5zd runs: % 5zd succ: % 5zd focus: %d\n", i,
Sha1ToString(II.Sha1).c_str(), II.U.size(),
- II.NumExecutedMutations, II.NumSuccessfullMutations);
+ II.NumExecutedMutations, II.NumSuccessfullMutations, II.HasFocusFunction);
}
}
@@ -213,18 +238,10 @@ class InputCorpus {
return false;
}
- void UpdateFeatureFrequency(size_t Idx) {
- FeatureFrequency[Idx % kFeatureSetSize]++;
- }
- float GetFeatureFrequency(size_t Idx) const {
- return FeatureFrequency[Idx % kFeatureSetSize];
- }
- void UpdateFeatureFrequencyScore(InputInfo *II) {
- const float kMin = 0.01, kMax = 100.;
- II->FeatureFrequencyScore = kMin;
- for (auto Idx : II->UniqFeatureSet)
- II->FeatureFrequencyScore += 1. / (GetFeatureFrequency(Idx) + 1.);
- II->FeatureFrequencyScore = Min(II->FeatureFrequencyScore, kMax);
+ bool IsFeatureNew(size_t Idx, uint32_t NewSize, bool Shrink) {
+ assert(NewSize);
+ uint32_t OldSize = GetFeature(Idx % kFeatureSetSize);
+ return OldSize == 0 || (Shrink && OldSize > NewSize);
}
size_t NumFeatures() const { return NumAddedFeatures; }
@@ -264,14 +281,11 @@ private:
std::iota(Intervals.begin(), Intervals.end(), 0);
for (size_t i = 0; i < N; i++)
Weights[i] = Inputs[i]->NumFeatures
- ? (i + 1) * Inputs[i]->FeatureFrequencyScore
+ ? (i + 1) * (Inputs[i]->HasFocusFunction ? 1000 : 1)
: 0.;
if (FeatureDebug) {
for (size_t i = 0; i < N; i++)
Printf("%zd ", Inputs[i]->NumFeatures);
- Printf("NUM\n");
- for (size_t i = 0; i < N; i++)
- Printf("%f ", Inputs[i]->FeatureFrequencyScore);
Printf("SCORE\n");
for (size_t i = 0; i < N; i++)
Printf("%f ", Weights[i]);
@@ -292,7 +306,6 @@ private:
size_t NumUpdatedFeatures = 0;
uint32_t InputSizesPerFeature[kFeatureSetSize];
uint32_t SmallestElementPerFeature[kFeatureSetSize];
- float FeatureFrequency[kFeatureSetSize];
std::string OutputCorpus;
};
diff --git a/lib/fuzzer/FuzzerDataFlowTrace.cpp b/lib/fuzzer/FuzzerDataFlowTrace.cpp
new file mode 100644
index 000000000000..764f3e49fd2d
--- /dev/null
+++ b/lib/fuzzer/FuzzerDataFlowTrace.cpp
@@ -0,0 +1,91 @@
+//===- FuzzerDataFlowTrace.cpp - DataFlowTrace ---*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// fuzzer::DataFlowTrace
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerDataFlowTrace.h"
+#include "FuzzerIO.h"
+
+#include <cstdlib>
+#include <fstream>
+#include <string>
+#include <vector>
+
+namespace fuzzer {
+
+void DataFlowTrace::Init(const std::string &DirPath,
+ const std::string &FocusFunction) {
+ if (DirPath.empty()) return;
+ const char *kFunctionsTxt = "functions.txt";
+ Printf("INFO: DataFlowTrace: reading from '%s'\n", DirPath.c_str());
+ Vector<SizedFile> Files;
+ GetSizedFilesFromDir(DirPath, &Files);
+ std::string L;
+
+ // Read functions.txt
+ std::ifstream IF(DirPlusFile(DirPath, kFunctionsTxt));
+ size_t FocusFuncIdx = SIZE_MAX;
+ size_t NumFunctions = 0;
+ while (std::getline(IF, L, '\n')) {
+ NumFunctions++;
+ if (FocusFunction == L)
+ FocusFuncIdx = NumFunctions - 1;
+ }
+ if (!NumFunctions || FocusFuncIdx == SIZE_MAX || Files.size() <= 1)
+ return;
+ // Read traces.
+ size_t NumTraceFiles = 0;
+ size_t NumTracesWithFocusFunction = 0;
+ for (auto &SF : Files) {
+ auto Name = Basename(SF.File);
+ if (Name == kFunctionsTxt) continue;
+ auto ParseError = [&](const char *Err) {
+ Printf("DataFlowTrace: parse error: %s\n File: %s\n Line: %s\n", Err,
+ Name.c_str(), L.c_str());
+ };
+ NumTraceFiles++;
+ // Printf("=== %s\n", Name.c_str());
+ std::ifstream IF(SF.File);
+ while (std::getline(IF, L, '\n')) {
+ size_t SpacePos = L.find(' ');
+ if (SpacePos == std::string::npos)
+ return ParseError("no space in the trace line");
+ if (L.empty() || L[0] != 'F')
+ return ParseError("the trace line doesn't start with 'F'");
+ size_t N = std::atol(L.c_str() + 1);
+ if (N >= NumFunctions)
+ return ParseError("N is greater than the number of functions");
+ if (N == FocusFuncIdx) {
+ NumTracesWithFocusFunction++;
+ const char *Beg = L.c_str() + SpacePos + 1;
+ const char *End = L.c_str() + L.size();
+ assert(Beg < End);
+ size_t Len = End - Beg;
+ Vector<uint8_t> V(Len);
+ for (size_t I = 0; I < Len; I++) {
+ if (Beg[I] != '0' && Beg[I] != '1')
+ ParseError("the trace should contain only 0 or 1");
+ V[I] = Beg[I] == '1';
+ }
+ Traces[Name] = V;
+ // Print just a few small traces.
+ if (NumTracesWithFocusFunction <= 3 && Len <= 16)
+ Printf("%s => |%s|\n", Name.c_str(), L.c_str() + SpacePos + 1);
+ break; // No need to parse the following lines.
+ }
+ }
+ }
+ assert(NumTraceFiles == Files.size() - 1);
+ Printf("INFO: DataFlowTrace: %zd trace files, %zd functions, "
+ "%zd traces with focus function\n",
+ NumTraceFiles, NumFunctions, NumTracesWithFocusFunction);
+}
+
+} // namespace fuzzer
+
diff --git a/lib/fuzzer/FuzzerDataFlowTrace.h b/lib/fuzzer/FuzzerDataFlowTrace.h
new file mode 100644
index 000000000000..ad4faeab7b2f
--- /dev/null
+++ b/lib/fuzzer/FuzzerDataFlowTrace.h
@@ -0,0 +1,56 @@
+//===- FuzzerDataFlowTrace.h - Internal header for the Fuzzer ---*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// fuzzer::DataFlowTrace; reads and handles a data-flow trace.
+//
+// A data flow trace is generated by e.g. dataflow/DataFlow.cpp
+// and is stored on disk in a separate directory.
+//
+// The trace dir contains a file 'functions.txt' which lists function names,
+// oner per line, e.g.
+// ==> functions.txt <==
+// Func2
+// LLVMFuzzerTestOneInput
+// Func1
+//
+// All other files in the dir are the traces, see dataflow/DataFlow.cpp.
+// The name of the file is sha1 of the input used to generate the trace.
+//
+// Current status:
+// the data is parsed and the summary is printed, but the data is not yet
+// used in any other way.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_DATA_FLOW_TRACE
+#define LLVM_FUZZER_DATA_FLOW_TRACE
+
+#include "FuzzerDefs.h"
+
+#include <unordered_map>
+#include <vector>
+#include <string>
+
+namespace fuzzer {
+class DataFlowTrace {
+ public:
+ void Init(const std::string &DirPath, const std::string &FocusFunction);
+ void Clear() { Traces.clear(); }
+ const Vector<uint8_t> *Get(const std::string &InputSha1) const {
+ auto It = Traces.find(InputSha1);
+ if (It != Traces.end())
+ return &It->second;
+ return nullptr;
+ }
+
+ private:
+ // Input's sha1 => DFT for the FocusFunction.
+ std::unordered_map<std::string, Vector<uint8_t> > Traces;
+};
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_DATA_FLOW_TRACE
diff --git a/lib/fuzzer/FuzzerDefs.h b/lib/fuzzer/FuzzerDefs.h
index 5942efc47a4a..a35c7a181b74 100644
--- a/lib/fuzzer/FuzzerDefs.h
+++ b/lib/fuzzer/FuzzerDefs.h
@@ -27,30 +27,56 @@
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 1
#define LIBFUZZER_NETBSD 0
+#define LIBFUZZER_FREEBSD 0
+#define LIBFUZZER_OPENBSD 0
#define LIBFUZZER_WINDOWS 0
#elif __APPLE__
#define LIBFUZZER_APPLE 1
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
+#define LIBFUZZER_FREEBSD 0
+#define LIBFUZZER_OPENBSD 0
#define LIBFUZZER_WINDOWS 0
#elif __NetBSD__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 1
+#define LIBFUZZER_FREEBSD 0
+#define LIBFUZZER_OPENBSD 0
+#define LIBFUZZER_WINDOWS 0
+#elif __FreeBSD__
+#define LIBFUZZER_APPLE 0
+#define LIBFUZZER_FUCHSIA 0
+#define LIBFUZZER_LINUX 0
+#define LIBFUZZER_NETBSD 0
+#define LIBFUZZER_FREEBSD 1
+#define LIBFUZZER_OPENBSD 0
+#define LIBFUZZER_WINDOWS 0
+#elif __OpenBSD__
+#define LIBFUZZER_APPLE 0
+#define LIBFUZZER_FUCHSIA 0
+#define LIBFUZZER_LINUX 0
+#define LIBFUZZER_NETBSD 0
+#define LIBFUZZER_FREEBSD 0
+#define LIBFUZZER_OPENBSD 1
#define LIBFUZZER_WINDOWS 0
#elif _WIN32
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 0
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
+#define LIBFUZZER_FREEBSD 0
+#define LIBFUZZER_OPENBSD 0
#define LIBFUZZER_WINDOWS 1
#elif __Fuchsia__
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_FUCHSIA 1
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_NETBSD 0
+#define LIBFUZZER_FREEBSD 0
+#define LIBFUZZER_OPENBSD 0
#define LIBFUZZER_WINDOWS 0
#else
#error "Support for your platform has not been implemented"
@@ -60,7 +86,9 @@
# define __has_attribute(x) 0
#endif
-#define LIBFUZZER_POSIX (LIBFUZZER_APPLE || LIBFUZZER_LINUX || LIBFUZZER_NETBSD)
+#define LIBFUZZER_POSIX \
+ (LIBFUZZER_APPLE || LIBFUZZER_LINUX || LIBFUZZER_NETBSD || \
+ LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD)
#ifdef __x86_64
# if __has_attribute(target)
@@ -127,6 +155,11 @@ extern ExternalFunctions *EF;
template<typename T>
class fuzzer_allocator: public std::allocator<T> {
public:
+ fuzzer_allocator() = default;
+
+ template<class U>
+ fuzzer_allocator(const fuzzer_allocator<U>&) {}
+
template<class Other>
struct rebind { typedef fuzzer_allocator<Other> other; };
};
@@ -143,12 +176,6 @@ typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
-struct ScopedDoingMyOwnMemOrStr {
- ScopedDoingMyOwnMemOrStr() { DoingMyOwnMemOrStr++; }
- ~ScopedDoingMyOwnMemOrStr() { DoingMyOwnMemOrStr--; }
- static int DoingMyOwnMemOrStr;
-};
-
inline uint8_t Bswap(uint8_t x) { return x; }
inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); }
inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
@@ -158,9 +185,7 @@ uint8_t *ExtraCountersBegin();
uint8_t *ExtraCountersEnd();
void ClearExtraCounters();
-uint64_t *ClangCountersBegin();
-uint64_t *ClangCountersEnd();
-void ClearClangCounters();
+extern bool RunningUserCallback;
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerDictionary.h b/lib/fuzzer/FuzzerDictionary.h
index daf7d003ea91..0d9d91bcd2f1 100644
--- a/lib/fuzzer/FuzzerDictionary.h
+++ b/lib/fuzzer/FuzzerDictionary.h
@@ -33,17 +33,9 @@ public:
}
bool operator==(const FixedWord<kMaxSize> &w) const {
- ScopedDoingMyOwnMemOrStr scoped_doing_my_own_mem_os_str;
return Size == w.Size && 0 == memcmp(Data, w.Data, Size);
}
- bool operator<(const FixedWord<kMaxSize> &w) const {
- ScopedDoingMyOwnMemOrStr scoped_doing_my_own_mem_os_str;
- if (Size != w.Size)
- return Size < w.Size;
- return memcmp(Data, w.Data, Size) < 0;
- }
-
static size_t GetMaxSize() { return kMaxSize; }
const uint8_t *data() const { return Data; }
uint8_t size() const { return Size; }
@@ -115,11 +107,11 @@ private:
};
// Parses one dictionary entry.
-// If successfull, write the enty to Unit and returns true,
+// If successful, write the enty to Unit and returns true,
// otherwise returns false.
bool ParseOneDictionaryEntry(const std::string &Str, Unit *U);
// Parses the dictionary file, fills Units, returns true iff all lines
-// were parsed succesfully.
+// were parsed successfully.
bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units);
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerDriver.cpp b/lib/fuzzer/FuzzerDriver.cpp
index f6b642daeda7..783474a39e16 100644
--- a/lib/fuzzer/FuzzerDriver.cpp
+++ b/lib/fuzzer/FuzzerDriver.cpp
@@ -537,6 +537,8 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
EF = new ExternalFunctions();
if (EF->LLVMFuzzerInitialize)
EF->LLVMFuzzerInitialize(argc, argv);
+ if (EF->__msan_scoped_disable_interceptor_checks)
+ EF->__msan_scoped_disable_interceptor_checks();
const Vector<std::string> Args(*argv, *argv + *argc);
assert(!Args.empty());
ProgName = new std::string(Args[0]);
@@ -567,7 +569,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
FuzzingOptions Options;
Options.Verbosity = Flags.verbosity;
Options.MaxLen = Flags.max_len;
- Options.ExperimentalLenControl = Flags.experimental_len_control;
+ Options.LenControl = Flags.len_control;
Options.UnitTimeoutSec = Flags.timeout;
Options.ErrorExitCode = Flags.error_exitcode;
Options.TimeoutExitCode = Flags.timeout_exitcode;
@@ -613,15 +615,22 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
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.PrintUnstableStats = Flags.print_unstable_stats;
+ if (Flags.handle_unstable == TracePC::MinUnstable ||
+ Flags.handle_unstable == TracePC::ZeroUnstable)
+ Options.HandleUnstable = Flags.handle_unstable;
Options.DumpCoverage = Flags.dump_coverage;
- Options.UseClangCoverage = Flags.use_clang_coverage;
- Options.UseFeatureFrequency = Flags.use_feature_frequency;
if (Flags.exit_on_src_pos)
Options.ExitOnSrcPos = Flags.exit_on_src_pos;
if (Flags.exit_on_item)
Options.ExitOnItem = Flags.exit_on_item;
+ if (Flags.focus_function)
+ Options.FocusFunction = Flags.focus_function;
+ if (Flags.data_flow_trace)
+ Options.DataFlowTrace = Flags.data_flow_trace;
unsigned Seed = Flags.seed;
// Initialize Seed.
@@ -665,6 +674,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
if (Flags.cleanse_crash)
return CleanseCrashInput(Args, Options);
+#if 0 // deprecated, to be removed.
if (auto Name = Flags.run_equivalence_server) {
SMR.Destroy(Name);
if (!SMR.Create(Name)) {
@@ -690,6 +700,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
}
Printf("INFO: EQUIVALENCE CLIENT UP\n");
}
+#endif
if (DoPlainRun) {
Options.SaveArtifacts = false;
@@ -747,7 +758,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
Printf("Dictionary analysis failed\n");
exit(1);
}
- Printf("Dictionary analysis suceeded\n");
+ Printf("Dictionary analysis succeeded\n");
exit(0);
}
diff --git a/lib/fuzzer/FuzzerExtFunctions.def b/lib/fuzzer/FuzzerExtFunctions.def
index 25a655bfd71d..8bfffdde56d4 100644
--- a/lib/fuzzer/FuzzerExtFunctions.def
+++ b/lib/fuzzer/FuzzerExtFunctions.def
@@ -29,6 +29,7 @@ EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t,
EXT_FUNC(__lsan_enable, void, (), false);
EXT_FUNC(__lsan_disable, void, (), false);
EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false);
+EXT_FUNC(__sanitizer_acquire_crash_state, bool, (), true);
EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int,
(void (*malloc_hook)(const volatile void *, size_t),
void (*free_hook)(const volatile void *)),
@@ -45,3 +46,6 @@ EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true);
EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false);
EXT_FUNC(__sanitizer_dump_coverage, void, (const uintptr_t *, uintptr_t),
false);
+EXT_FUNC(__msan_scoped_disable_interceptor_checks, void, (), false);
+EXT_FUNC(__msan_scoped_enable_interceptor_checks, void, (), false);
+EXT_FUNC(__msan_unpoison, void, (const volatile void *, size_t size), false);
diff --git a/lib/fuzzer/FuzzerExtFunctionsWeak.cpp b/lib/fuzzer/FuzzerExtFunctionsWeak.cpp
index 5a90723986af..a4e56fc27b8d 100644
--- a/lib/fuzzer/FuzzerExtFunctionsWeak.cpp
+++ b/lib/fuzzer/FuzzerExtFunctionsWeak.cpp
@@ -13,7 +13,8 @@
// to clients right now.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
-#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FUCHSIA
+#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FUCHSIA || \
+ LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD
#include "FuzzerExtFunctions.h"
#include "FuzzerIO.h"
@@ -51,4 +52,4 @@ ExternalFunctions::ExternalFunctions() {
} // namespace fuzzer
-#endif // LIBFUZZER_LINUX || LIBFUZZER_NETBSD
+#endif
diff --git a/lib/fuzzer/FuzzerExtraCounters.cpp b/lib/fuzzer/FuzzerExtraCounters.cpp
index 0e7a7761bf80..c99cd89be293 100644
--- a/lib/fuzzer/FuzzerExtraCounters.cpp
+++ b/lib/fuzzer/FuzzerExtraCounters.cpp
@@ -11,7 +11,8 @@
#include "FuzzerDefs.h"
-#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD
+#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \
+ LIBFUZZER_OPENBSD
__attribute__((weak)) extern uint8_t __start___libfuzzer_extra_counters;
__attribute__((weak)) extern uint8_t __stop___libfuzzer_extra_counters;
diff --git a/lib/fuzzer/FuzzerFlags.def b/lib/fuzzer/FuzzerFlags.def
index a32102a7da07..ba04bc25fd45 100644
--- a/lib/fuzzer/FuzzerFlags.def
+++ b/lib/fuzzer/FuzzerFlags.def
@@ -17,7 +17,10 @@ FUZZER_FLAG_INT(runs, -1,
FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. "
"If 0, libFuzzer tries to guess a good value based on the corpus "
"and reports it. ")
-FUZZER_FLAG_INT(experimental_len_control, 0, "experimental flag")
+FUZZER_FLAG_INT(len_control, 1000, "Try generating small inputs first, "
+ "then try larger inputs over time. Specifies the rate at which the length "
+ "limit is increased (smaller == faster). If 0, immediately try inputs with "
+ "size up to max_len.")
FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.")
FUZZER_FLAG_INT(mutate_depth, 5,
"Apply this number of consecutive mutations to each input.")
@@ -42,7 +45,7 @@ FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be "
"This flag can be used to minimize a corpus.")
FUZZER_FLAG_STRING(merge_inner, "internal flag")
FUZZER_FLAG_STRING(merge_control_file,
- "Specify a control file used for the merge proccess. "
+ "Specify a control file used for the merge process. "
"If a merge process gets killed it tries to leave this file "
"in a state suitable for resuming the merge. "
"By default a temporary file will be used.")
@@ -107,6 +110,15 @@ FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text"
FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated."
" If 1, dump coverage information as a"
" .sancov file at exit.")
+FUZZER_FLAG_INT(handle_unstable, 0, "Experimental."
+ " Executes every input 3 times in total if a unique feature"
+ " is found during the first execution."
+ " If 1, we only use the minimum hit count from the 3 runs"
+ " to determine whether an input is interesting."
+ " If 2, we disregard edges that are found unstable for"
+ " feature collection.")
+FUZZER_FLAG_INT(print_unstable_stats, 0, "Experimental."
+ " If 1, print unstable statistics at exit.")
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.")
FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.")
@@ -142,9 +154,12 @@ FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum"
FUZZER_FLAG_INT(ignore_remaining_args, 0, "If 1, ignore all arguments passed "
"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")
-FUZZER_FLAG_STRING(run_equivalence_server, "Experimental")
-FUZZER_FLAG_STRING(use_equivalence_server, "Experimental")
+FUZZER_DEPRECATED_FLAG(run_equivalence_server)
+FUZZER_DEPRECATED_FLAG(use_equivalence_server)
FUZZER_FLAG_INT(analyze_dict, 0, "Experimental")
-FUZZER_FLAG_INT(use_clang_coverage, 0, "Experimental")
-FUZZER_FLAG_INT(use_feature_frequency, 0, "Experimental/internal")
+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")
diff --git a/lib/fuzzer/FuzzerIO.cpp b/lib/fuzzer/FuzzerIO.cpp
index dac5ec658f1c..f3ead0ec5357 100644
--- a/lib/fuzzer/FuzzerIO.cpp
+++ b/lib/fuzzer/FuzzerIO.cpp
@@ -100,6 +100,14 @@ std::string DirPlusFile(const std::string &DirPath,
return DirPath + GetSeparator() + FileName;
}
+std::string Basename(const std::string &Path, char Separator) {
+ size_t Pos = Path.rfind(Separator);
+ if (Pos == std::string::npos)
+ return Path;
+ assert(Pos < Path.size());
+ return Path.substr(Pos + 1);
+}
+
void DupAndCloseStderr() {
int OutputFd = DuplicateFile(2);
if (OutputFd > 0) {
diff --git a/lib/fuzzer/FuzzerIO.h b/lib/fuzzer/FuzzerIO.h
index ea9f0d5a6703..6d7757435b7b 100644
--- a/lib/fuzzer/FuzzerIO.h
+++ b/lib/fuzzer/FuzzerIO.h
@@ -67,6 +67,8 @@ struct SizedFile {
void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V);
char GetSeparator();
+// Similar to the basename utility: returns the file name w/o the dir prefix.
+std::string Basename(const std::string &Path, char Separator = GetSeparator());
FILE* OpenFile(int Fd, const char *Mode);
diff --git a/lib/fuzzer/FuzzerIOPosix.cpp b/lib/fuzzer/FuzzerIOPosix.cpp
index 2257751c662d..17e884d3c4c3 100644
--- a/lib/fuzzer/FuzzerIOPosix.cpp
+++ b/lib/fuzzer/FuzzerIOPosix.cpp
@@ -54,7 +54,7 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
DIR *D = opendir(Dir.c_str());
if (!D) {
- Printf("No such directory: %s; exiting\n", Dir.c_str());
+ Printf("%s: %s; exiting\n", strerror(errno), Dir.c_str());
exit(1);
}
while (auto E = readdir(D)) {
diff --git a/lib/fuzzer/FuzzerInterface.h b/lib/fuzzer/FuzzerInterface.h
index c2c0a39843c0..0f7effb2ab6a 100644
--- a/lib/fuzzer/FuzzerInterface.h
+++ b/lib/fuzzer/FuzzerInterface.h
@@ -30,35 +30,39 @@ extern "C" {
// Executes the code under test with [Data, Data+Size) as the input.
// libFuzzer will invoke this function *many* times with different inputs.
// Must return 0.
-int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+__attribute__((visibility("default"))) int
+LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
// Optional user-provided initialization function.
// If provided, this function will be called by libFuzzer once at startup.
// It may read and modify argc/argv.
// Must return 0.
-int LLVMFuzzerInitialize(int *argc, char ***argv);
+__attribute__((visibility("default"))) int LLVMFuzzerInitialize(int *argc,
+ char ***argv);
// Optional user-provided custom mutator.
// Mutates raw data in [Data, Data+Size) inplace.
// Returns the new size, which is not greater than MaxSize.
// Given the same Seed produces the same mutation.
-size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
- unsigned int Seed);
+__attribute__((visibility("default"))) size_t
+LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
+ unsigned int Seed);
// Optional user-provided custom cross-over function.
// Combines pieces of Data1 & Data2 together into Out.
// Returns the new size, which is not greater than MaxOutSize.
// Should produce the same mutation given the same Seed.
-size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
- const uint8_t *Data2, size_t Size2,
- uint8_t *Out, size_t MaxOutSize,
- unsigned int Seed);
+__attribute__((visibility("default"))) size_t
+LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
+ const uint8_t *Data2, size_t Size2, uint8_t *Out,
+ size_t MaxOutSize, unsigned int Seed);
// Experimental, may go away in future.
// libFuzzer-provided function to be used inside LLVMFuzzerCustomMutator.
// Mutates raw data in [Data, Data+Size) inplace.
// Returns the new size, which is not greater than MaxSize.
-size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+__attribute__((visibility("default"))) size_t
+LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
#ifdef __cplusplus
} // extern "C"
diff --git a/lib/fuzzer/FuzzerInternal.h b/lib/fuzzer/FuzzerInternal.h
index 2b2638f1f8f2..bfc898248adb 100644
--- a/lib/fuzzer/FuzzerInternal.h
+++ b/lib/fuzzer/FuzzerInternal.h
@@ -12,6 +12,7 @@
#ifndef LLVM_FUZZER_INTERNAL_H
#define LLVM_FUZZER_INTERNAL_H
+#include "FuzzerDataFlowTrace.h"
#include "FuzzerDefs.h"
#include "FuzzerExtFunctions.h"
#include "FuzzerInterface.h"
@@ -66,6 +67,7 @@ public:
static void StaticGracefulExitCallback();
void ExecuteCallback(const uint8_t *Data, size_t Size);
+ void CheckForUnstableCounters(const uint8_t *Data, size_t Size);
bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false,
InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr);
@@ -116,7 +118,6 @@ private:
uint8_t *CurrentUnitData = nullptr;
std::atomic<size_t> CurrentUnitSize;
uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit.
- bool RunningCB = false;
bool GracefulExitRequested = false;
@@ -134,6 +135,7 @@ private:
InputCorpus &Corpus;
MutationDispatcher &MD;
FuzzingOptions Options;
+ DataFlowTrace DFT;
system_clock::time_point ProcessStartTime = system_clock::now();
system_clock::time_point UnitStartTime, UnitStopTime;
@@ -150,6 +152,28 @@ private:
static thread_local bool IsMyThread;
};
+struct ScopedEnableMsanInterceptorChecks {
+ ScopedEnableMsanInterceptorChecks() {
+ if (EF->__msan_scoped_enable_interceptor_checks)
+ EF->__msan_scoped_enable_interceptor_checks();
+ }
+ ~ScopedEnableMsanInterceptorChecks() {
+ if (EF->__msan_scoped_disable_interceptor_checks)
+ EF->__msan_scoped_disable_interceptor_checks();
+ }
+};
+
+struct ScopedDisableMsanInterceptorChecks {
+ ScopedDisableMsanInterceptorChecks() {
+ if (EF->__msan_scoped_disable_interceptor_checks)
+ EF->__msan_scoped_disable_interceptor_checks();
+ }
+ ~ScopedDisableMsanInterceptorChecks() {
+ if (EF->__msan_scoped_enable_interceptor_checks)
+ EF->__msan_scoped_enable_interceptor_checks();
+ }
+};
+
} // namespace fuzzer
#endif // LLVM_FUZZER_INTERNAL_H
diff --git a/lib/fuzzer/FuzzerLoop.cpp b/lib/fuzzer/FuzzerLoop.cpp
index 5b451ca122d7..4bc88365a0b9 100644
--- a/lib/fuzzer/FuzzerLoop.cpp
+++ b/lib/fuzzer/FuzzerLoop.cpp
@@ -43,6 +43,8 @@ thread_local bool Fuzzer::IsMyThread;
SharedMemoryRegion SMR;
+bool RunningUserCallback = false;
+
// Only one Fuzzer per process.
static Fuzzer *F;
@@ -105,7 +107,7 @@ void MallocHook(const volatile void *ptr, size_t size) {
return;
Printf("MALLOC[%zd] %p %zd\n", N, ptr, size);
if (TraceLevel >= 2 && EF)
- EF->__sanitizer_print_stack_trace();
+ PrintStackTrace();
}
}
@@ -118,7 +120,7 @@ void FreeHook(const volatile void *ptr) {
return;
Printf("FREE[%zd] %p\n", N, ptr);
if (TraceLevel >= 2 && EF)
- EF->__sanitizer_print_stack_trace();
+ PrintStackTrace();
}
}
@@ -129,8 +131,7 @@ void Fuzzer::HandleMalloc(size_t Size) {
Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(),
Size);
Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");
- if (EF->__sanitizer_print_stack_trace)
- EF->__sanitizer_print_stack_trace();
+ PrintStackTrace();
DumpCurrentUnit("oom-");
Printf("SUMMARY: libFuzzer: out-of-memory\n");
PrintFinalStats();
@@ -149,8 +150,7 @@ Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks)
EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook);
TPC.SetUseCounters(Options.UseCounters);
- TPC.SetUseValueProfile(Options.UseValueProfile);
- TPC.SetUseClangCoverage(Options.UseClangCoverage);
+ TPC.SetUseValueProfileMask(Options.UseValueProfile);
if (Options.Verbosity)
TPC.PrintModuleInfo();
@@ -161,6 +161,8 @@ Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
AllocateCurrentUnitData();
CurrentUnitSize = 0;
memset(BaseSha1, 0, sizeof(BaseSha1));
+ TPC.SetFocusFunction(Options.FocusFunction);
+ DFT.Init(Options.DataFlowTrace, Options.FocusFunction);
}
Fuzzer::~Fuzzer() {}
@@ -179,6 +181,7 @@ void Fuzzer::StaticDeathCallback() {
void Fuzzer::DumpCurrentUnit(const char *Prefix) {
if (!CurrentUnitData)
return; // Happens when running individual inputs.
+ ScopedDisableMsanInterceptorChecks S;
MD.PrintMutationSequence();
Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str());
size_t UnitSize = CurrentUnitSize;
@@ -228,9 +231,10 @@ void Fuzzer::StaticFileSizeExceedCallback() {
}
void Fuzzer::CrashCallback() {
+ if (EF->__sanitizer_acquire_crash_state)
+ EF->__sanitizer_acquire_crash_state();
Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());
- if (EF->__sanitizer_print_stack_trace)
- EF->__sanitizer_print_stack_trace();
+ PrintStackTrace();
Printf("NOTE: libFuzzer has rudimentary signal handlers.\n"
" Combine libFuzzer with AddressSanitizer or similar for better "
"crash reports.\n");
@@ -241,11 +245,13 @@ void Fuzzer::CrashCallback() {
}
void Fuzzer::ExitCallback() {
- if (!RunningCB)
+ if (!RunningUserCallback)
return; // This exit did not come from the user callback
+ if (EF->__sanitizer_acquire_crash_state &&
+ !EF->__sanitizer_acquire_crash_state())
+ return;
Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid());
- if (EF->__sanitizer_print_stack_trace)
- EF->__sanitizer_print_stack_trace();
+ PrintStackTrace();
Printf("SUMMARY: libFuzzer: fuzz target exited\n");
DumpCurrentUnit("crash-");
PrintFinalStats();
@@ -273,7 +279,7 @@ void Fuzzer::AlarmCallback() {
if (!InFuzzingThread())
return;
#endif
- if (!RunningCB)
+ if (!RunningUserCallback)
return; // We have not started running units yet.
size_t Seconds =
duration_cast<seconds>(system_clock::now() - UnitStartTime).count();
@@ -282,14 +288,16 @@ void Fuzzer::AlarmCallback() {
if (Options.Verbosity >= 2)
Printf("AlarmCallback %zd\n", Seconds);
if (Seconds >= (size_t)Options.UnitTimeoutSec) {
+ if (EF->__sanitizer_acquire_crash_state &&
+ !EF->__sanitizer_acquire_crash_state())
+ return;
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);
DumpCurrentUnit("timeout-");
Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(),
Seconds);
- if (EF->__sanitizer_print_stack_trace)
- EF->__sanitizer_print_stack_trace();
+ PrintStackTrace();
Printf("SUMMARY: libFuzzer: timeout\n");
PrintFinalStats();
_Exit(Options.TimeoutExitCode); // Stop right now.
@@ -297,12 +305,14 @@ void Fuzzer::AlarmCallback() {
}
void Fuzzer::RssLimitCallback() {
+ if (EF->__sanitizer_acquire_crash_state &&
+ !EF->__sanitizer_acquire_crash_state())
+ return;
Printf(
"==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n",
GetPid(), GetPeakRSSMb(), Options.RssLimitMb);
Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");
- if (EF->__sanitizer_print_memory_profile)
- EF->__sanitizer_print_memory_profile(95, 8);
+ PrintMemoryProfile();
DumpCurrentUnit("oom-");
Printf("SUMMARY: libFuzzer: out-of-memory\n");
PrintFinalStats();
@@ -328,7 +338,11 @@ void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
else
Printf("/%zdMb", N >> 20);
}
+ if (size_t FF = Corpus.NumInputsThatTouchFocusFunction())
+ Printf(" focus: %zd", FF);
}
+ if (TmpMaxMutationLen)
+ Printf(" lim: %zd", TmpMaxMutationLen);
if (Units)
Printf(" units: %zd", Units);
@@ -340,10 +354,13 @@ void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
void Fuzzer::PrintFinalStats() {
if (Options.PrintCoverage)
TPC.PrintCoverage();
+ if (Options.PrintUnstableStats)
+ TPC.PrintUnstableStats();
if (Options.DumpCoverage)
TPC.DumpCoverage();
if (Options.PrintCorpusStats)
Corpus.PrintStats();
+ if (Options.PrintMutationStats) MD.PrintMutationStats();
if (!Options.PrintFinalStats)
return;
size_t ExecPerSec = execPerSec();
@@ -432,6 +449,34 @@ void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) {
}
}
+void Fuzzer::CheckForUnstableCounters(const uint8_t *Data, size_t Size) {
+ auto CBSetupAndRun = [&]() {
+ ScopedEnableMsanInterceptorChecks S;
+ UnitStartTime = system_clock::now();
+ TPC.ResetMaps();
+ RunningUserCallback = true;
+ CB(Data, Size);
+ RunningUserCallback = false;
+ UnitStopTime = system_clock::now();
+ };
+
+ // Copy original run counters into our unstable counters
+ TPC.InitializeUnstableCounters();
+
+ // First Rerun
+ CBSetupAndRun();
+ TPC.UpdateUnstableCounters(Options.HandleUnstable);
+
+ // Second Rerun
+ CBSetupAndRun();
+ TPC.UpdateUnstableCounters(Options.HandleUnstable);
+
+ // Move minimum hit counts back to ModuleInline8bitCounters
+ if (Options.HandleUnstable == TracePC::MinUnstable ||
+ Options.HandleUnstable == TracePC::ZeroUnstable)
+ TPC.ApplyUnstableCounters();
+}
+
bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
InputInfo *II, bool *FoundUniqFeatures) {
if (!Size)
@@ -442,9 +487,18 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
UniqFeatureSetTmp.clear();
size_t FoundUniqFeaturesOfII = 0;
size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();
+ bool NewFeaturesUnstable = false;
+
+ if (Options.HandleUnstable || Options.PrintUnstableStats) {
+ TPC.CollectFeatures([&](size_t Feature) {
+ if (Corpus.IsFeatureNew(Feature, Size, Options.Shrink))
+ NewFeaturesUnstable = true;
+ });
+ if (NewFeaturesUnstable)
+ CheckForUnstableCounters(Data, Size);
+ }
+
TPC.CollectFeatures([&](size_t Feature) {
- if (Options.UseFeatureFrequency)
- Corpus.UpdateFeatureFrequency(Feature);
if (Corpus.AddFeature(Feature, Size, Options.Shrink))
UniqFeatureSetTmp.push_back(Feature);
if (Options.ReduceInputs && II)
@@ -452,17 +506,20 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
II->UniqFeatureSet.end(), Feature))
FoundUniqFeaturesOfII++;
});
+
if (FoundUniqFeatures)
*FoundUniqFeatures = FoundUniqFeaturesOfII;
PrintPulseAndReportSlowInput(Data, Size);
size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore;
+
if (NumNewFeatures) {
TPC.UpdateObservedPCs();
Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,
- UniqFeatureSetTmp);
+ TPC.ObservedFocusFunction(), UniqFeatureSetTmp, DFT, II);
return true;
}
if (II && FoundUniqFeaturesOfII &&
+ II->DataFlowTraceForFocusFunction.empty() &&
FoundUniqFeaturesOfII == II->UniqFeatureSet.size() &&
II->U.size() > Size) {
Corpus.Replace(II, {Data, Data + Size});
@@ -505,19 +562,24 @@ void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
// so that we reliably find buffer overflows in it.
uint8_t *DataCopy = new uint8_t[Size];
memcpy(DataCopy, Data, Size);
+ if (EF->__msan_unpoison)
+ EF->__msan_unpoison(DataCopy, Size);
if (CurrentUnitData && CurrentUnitData != Data)
memcpy(CurrentUnitData, Data, Size);
CurrentUnitSize = Size;
- AllocTracer.Start(Options.TraceMalloc);
- UnitStartTime = system_clock::now();
- TPC.ResetMaps();
- RunningCB = true;
- int Res = CB(DataCopy, Size);
- RunningCB = false;
- UnitStopTime = system_clock::now();
- (void)Res;
- assert(Res == 0);
- HasMoreMallocsThanFrees = AllocTracer.Stop();
+ {
+ ScopedEnableMsanInterceptorChecks S;
+ AllocTracer.Start(Options.TraceMalloc);
+ UnitStartTime = system_clock::now();
+ TPC.ResetMaps();
+ RunningUserCallback = true;
+ int Res = CB(DataCopy, Size);
+ RunningUserCallback = false;
+ UnitStopTime = system_clock::now();
+ (void)Res;
+ assert(Res == 0);
+ HasMoreMallocsThanFrees = AllocTracer.Stop();
+ }
if (!LooseMemeq(DataCopy, Data, Size))
CrashOnOverwrittenData();
CurrentUnitSize = 0;
@@ -618,8 +680,6 @@ void Fuzzer::MutateAndTestOne() {
MD.StartMutationSequence();
auto &II = Corpus.ChooseUnitToMutate(MD.GetRand());
- if (Options.UseFeatureFrequency)
- Corpus.UpdateFeatureFrequencyScore(&II);
const auto &U = II.U;
memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1));
assert(CurrentUnitData);
@@ -638,7 +698,12 @@ void Fuzzer::MutateAndTestOne() {
break;
MaybeExitGracefully();
size_t NewSize = 0;
- NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen);
+ if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() &&
+ Size <= CurrentMaxMutationLen)
+ NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size,
+ II.DataFlowTraceForFocusFunction);
+ else
+ NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen);
assert(NewSize > 0 && "Mutator returned empty unit");
assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit");
Size = NewSize;
@@ -728,7 +793,14 @@ void Fuzzer::ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs) {
}
PrintStats("INITED");
- if (Corpus.empty()) {
+ if (!Options.FocusFunction.empty())
+ Printf("INFO: %zd/%zd inputs touch the focus function\n",
+ Corpus.NumInputsThatTouchFocusFunction(), Corpus.size());
+ if (!Options.DataFlowTrace.empty())
+ Printf("INFO: %zd/%zd inputs have the Data Flow Trace\n",
+ Corpus.NumInputsWithDataFlowTrace(), Corpus.size());
+
+ if (Corpus.empty() && Options.MaxNumberOfRuns) {
Printf("ERROR: no interesting inputs were found. "
"Is the code instrumented for coverage? Exiting.\n");
exit(1);
@@ -737,6 +809,7 @@ void Fuzzer::ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs) {
void Fuzzer::Loop(const Vector<std::string> &CorpusDirs) {
ReadAndExecuteSeedCorpora(CorpusDirs);
+ DFT.Clear(); // No need for DFT any more.
TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs);
system_clock::time_point LastCorpusReload = system_clock::now();
@@ -755,16 +828,12 @@ void Fuzzer::Loop(const Vector<std::string> &CorpusDirs) {
break;
// Update TmpMaxMutationLen
- if (Options.ExperimentalLenControl) {
+ if (Options.LenControl) {
if (TmpMaxMutationLen < MaxMutationLen &&
TotalNumberOfRuns - LastCorpusUpdateRun >
- Options.ExperimentalLenControl * Log(TmpMaxMutationLen)) {
+ Options.LenControl * Log(TmpMaxMutationLen)) {
TmpMaxMutationLen =
Min(MaxMutationLen, TmpMaxMutationLen + Log(TmpMaxMutationLen));
- if (TmpMaxMutationLen <= MaxMutationLen)
- Printf("#%zd\tTEMP_MAX_LEN: %zd (%zd %zd)\n", TotalNumberOfRuns,
- TmpMaxMutationLen, Options.ExperimentalLenControl,
- LastCorpusUpdateRun);
LastCorpusUpdateRun = TotalNumberOfRuns;
}
} else {
@@ -826,13 +895,15 @@ void Fuzzer::AnnounceOutput(const uint8_t *Data, size_t Size) {
extern "C" {
-size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
+__attribute__((visibility("default"))) size_t
+LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
assert(fuzzer::F);
return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);
}
// Experimental
-void LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size) {
+__attribute__((visibility("default"))) void
+LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size) {
assert(fuzzer::F);
fuzzer::F->AnnounceOutput(Data, Size);
}
diff --git a/lib/fuzzer/FuzzerMain.cpp b/lib/fuzzer/FuzzerMain.cpp
index af8657200be2..f2c8e9c7bb11 100644
--- a/lib/fuzzer/FuzzerMain.cpp
+++ b/lib/fuzzer/FuzzerMain.cpp
@@ -16,6 +16,6 @@ extern "C" {
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
} // extern "C"
-int main(int argc, char **argv) {
+__attribute__((visibility("default"))) int main(int argc, char **argv) {
return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput);
}
diff --git a/lib/fuzzer/FuzzerMutate.cpp b/lib/fuzzer/FuzzerMutate.cpp
index 9ee5299f1b60..ff076cca683f 100644
--- a/lib/fuzzer/FuzzerMutate.cpp
+++ b/lib/fuzzer/FuzzerMutate.cpp
@@ -30,39 +30,41 @@ MutationDispatcher::MutationDispatcher(Random &Rand,
DefaultMutators.insert(
DefaultMutators.begin(),
{
- {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
- {&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
+ {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes", 0, 0},
+ {&MutationDispatcher::Mutate_InsertByte, "InsertByte", 0, 0},
{&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", 0, 0},
+ {&MutationDispatcher::Mutate_ChangeByte, "ChangeByte", 0, 0},
+ {&MutationDispatcher::Mutate_ChangeBit, "ChangeBit", 0, 0},
+ {&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes", 0, 0},
+ {&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt", 0,
+ 0},
+ {&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt", 0,
+ 0},
+ {&MutationDispatcher::Mutate_CopyPart, "CopyPart", 0, 0},
+ {&MutationDispatcher::Mutate_CrossOver, "CrossOver", 0, 0},
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
- "ManualDict"},
+ "ManualDict", 0, 0},
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
- "PersAutoDict"},
+ "PersAutoDict", 0, 0},
});
if(Options.UseCmp)
DefaultMutators.push_back(
- {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
+ {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP", 0, 0});
if (EF->LLVMFuzzerCustomMutator)
- Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
+ Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom", 0, 0});
else
Mutators = DefaultMutators;
if (EF->LLVMFuzzerCustomCrossOver)
Mutators.push_back(
- {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
+ {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver", 0, 0});
}
static char RandCh(Random &Rand) {
if (Rand.RandBool()) return Rand(256);
- const char *Special = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
+ const char Special[] = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
return Special[Rand(sizeof(Special) - 1)];
}
@@ -195,7 +197,6 @@ DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
const void *Arg1Mutation, const void *Arg2Mutation,
size_t ArgSize, const uint8_t *Data,
size_t Size) {
- ScopedDoingMyOwnMemOrStr scoped_doing_my_own_mem_os_str;
bool HandleFirst = Rand.RandBool();
const void *ExistingBytes, *DesiredBytes;
Word W;
@@ -339,7 +340,9 @@ size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize,
size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size,
size_t MaxSize) {
if (Size > MaxSize || Size == 0) return 0;
- if (Rand.RandBool())
+ // If Size == MaxSize, `InsertPartOf(...)` will
+ // fail so there's no point using it in this case.
+ if (Size == MaxSize || Rand.RandBool())
return CopyPartOf(Data, Size, Data, Size);
else
return InsertPartOf(Data, Size, Data, Size, MaxSize);
@@ -463,6 +466,7 @@ void MutationDispatcher::RecordSuccessfulMutationSequence() {
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
PersistentAutoDictionary.push_back({DE->GetW(), 1});
}
+ RecordUsefulMutations();
}
void MutationDispatcher::PrintRecommendedDictionary() {
@@ -483,8 +487,7 @@ void MutationDispatcher::PrintRecommendedDictionary() {
void MutationDispatcher::PrintMutationSequence() {
Printf("MS: %zd ", CurrentMutatorSequence.size());
- for (auto M : CurrentMutatorSequence)
- Printf("%s-", M.Name);
+ for (auto M : CurrentMutatorSequence) Printf("%s-", M->Name);
if (!CurrentDictionaryEntrySequence.empty()) {
Printf(" DE: ");
for (auto DE : CurrentDictionaryEntrySequence) {
@@ -512,12 +515,13 @@ size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
// in which case they will return 0.
// Try several times before returning un-mutated data.
for (int Iter = 0; Iter < 100; Iter++) {
- auto M = Mutators[Rand(Mutators.size())];
- size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize);
+ auto M = &Mutators[Rand(Mutators.size())];
+ size_t NewSize = (this->*(M->Fn))(Data, Size, MaxSize);
if (NewSize && NewSize <= MaxSize) {
if (Options.OnlyASCII)
ToASCII(Data, NewSize);
CurrentMutatorSequence.push_back(M);
+ M->TotalCount++;
return NewSize;
}
}
@@ -525,9 +529,54 @@ size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
return 1; // Fallback, should not happen frequently.
}
+// Mask represents the set of Data bytes that are worth mutating.
+size_t MutationDispatcher::MutateWithMask(uint8_t *Data, size_t Size,
+ size_t MaxSize,
+ const Vector<uint8_t> &Mask) {
+ assert(Size <= Mask.size());
+ // * Copy the worthy bytes into a temporary array T
+ // * Mutate T
+ // * Copy T back.
+ // This is totally unoptimized.
+ auto &T = MutateWithMaskTemp;
+ if (T.size() < Size)
+ T.resize(Size);
+ size_t OneBits = 0;
+ for (size_t I = 0; I < Size; I++)
+ if (Mask[I])
+ T[OneBits++] = Data[I];
+
+ assert(!T.empty());
+ size_t NewSize = Mutate(T.data(), OneBits, OneBits);
+ assert(NewSize <= OneBits);
+ (void)NewSize;
+ // Even if NewSize < OneBits we still use all OneBits bytes.
+ for (size_t I = 0, J = 0; I < Size; I++)
+ if (Mask[I])
+ Data[I] = T[J++];
+ return Size;
+}
+
void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
ManualDictionary.push_back(
{W, std::numeric_limits<size_t>::max()});
}
+void MutationDispatcher::RecordUsefulMutations() {
+ for (auto M : CurrentMutatorSequence) M->UsefulCount++;
+}
+
+void MutationDispatcher::PrintMutationStats() {
+ Printf("\nstat::mutation_usefulness: ");
+ for (size_t i = 0; i < Mutators.size(); i++) {
+ double UsefulPercentage =
+ Mutators[i].TotalCount
+ ? (100.0 * Mutators[i].UsefulCount) / Mutators[i].TotalCount
+ : 0;
+ Printf("%.3f", UsefulPercentage);
+ if (i < Mutators.size() - 1) Printf(",");
+ }
+ Printf("\n");
+}
+
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerMutate.h b/lib/fuzzer/FuzzerMutate.h
index 4aa58af9902d..828ecc13d866 100644
--- a/lib/fuzzer/FuzzerMutate.h
+++ b/lib/fuzzer/FuzzerMutate.h
@@ -27,7 +27,7 @@ public:
void StartMutationSequence();
/// Print the current sequence of mutations.
void PrintMutationSequence();
- /// Indicate that the current sequence of mutations was successfull.
+ /// Indicate that the current sequence of mutations was successful.
void RecordSuccessfulMutationSequence();
/// Mutates data by invoking user-provided mutator.
size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize);
@@ -70,6 +70,13 @@ public:
/// Applies one of the configured mutations.
/// Returns the new size of data which could be up to MaxSize.
size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize);
+
+ /// Applies one of the configured mutations to the bytes of Data
+ /// that have '1' in Mask.
+ /// Mask.size() should be >= Size.
+ size_t MutateWithMask(uint8_t *Data, size_t Size, size_t MaxSize,
+ const Vector<uint8_t> &Mask);
+
/// Applies one of the default mutations. Provided as a service
/// to mutation authors.
size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize);
@@ -86,11 +93,16 @@ public:
Random &GetRand() { return Rand; }
-private:
+ void PrintMutationStats();
+
+ void RecordUsefulMutations();
+ private:
struct Mutator {
size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
const char *Name;
+ uint64_t UsefulCount;
+ uint64_t TotalCount;
};
size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size,
@@ -125,11 +137,11 @@ private:
// recreated periodically.
Dictionary TempAutoDictionary;
// Persistent dictionary modified by the fuzzer, consists of
- // entries that led to successfull discoveries in the past mutations.
+ // entries that led to successful discoveries in the past mutations.
Dictionary PersistentAutoDictionary;
- Vector<Mutator> CurrentMutatorSequence;
Vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
+ Vector<Mutator *> CurrentMutatorSequence;
static const size_t kCmpDictionaryEntriesDequeSize = 16;
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
@@ -137,6 +149,7 @@ private:
const InputCorpus *Corpus = nullptr;
Vector<uint8_t> MutateInPlaceHere;
+ Vector<uint8_t> MutateWithMaskTemp;
// CustomCrossOver needs its own buffer as a custom implementation may call
// LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere.
Vector<uint8_t> CustomCrossOverInPlaceHere;
diff --git a/lib/fuzzer/FuzzerOptions.h b/lib/fuzzer/FuzzerOptions.h
index 15a378020b85..ce39c0876cd7 100644
--- a/lib/fuzzer/FuzzerOptions.h
+++ b/lib/fuzzer/FuzzerOptions.h
@@ -18,7 +18,7 @@ namespace fuzzer {
struct FuzzingOptions {
int Verbosity = 1;
size_t MaxLen = 0;
- size_t ExperimentalLenControl = 0;
+ size_t LenControl = 1000;
int UnitTimeoutSec = 300;
int TimeoutExitCode = 77;
int ErrorExitCode = 77;
@@ -31,7 +31,7 @@ struct FuzzingOptions {
bool UseCounters = false;
bool UseMemmem = true;
bool UseCmp = false;
- bool UseValueProfile = false;
+ int UseValueProfile = false;
bool Shrink = false;
bool ReduceInputs = false;
int ReloadIntervalSec = 1;
@@ -45,18 +45,21 @@ struct FuzzingOptions {
std::string ExactArtifactPath;
std::string ExitOnSrcPos;
std::string ExitOnItem;
+ std::string FocusFunction;
+ std::string DataFlowTrace;
bool SaveArtifacts = true;
bool PrintNEW = true; // Print a status line when new units are found;
bool PrintNewCovPcs = false;
int PrintNewCovFuncs = 0;
bool PrintFinalStats = false;
+ bool PrintMutationStats = false;
bool PrintCorpusStats = false;
bool PrintCoverage = false;
+ bool PrintUnstableStats = false;
+ int HandleUnstable = 0;
bool DumpCoverage = false;
- bool UseClangCoverage = false;
bool DetectLeaks = true;
int PurgeAllocatorIntervalSec = 1;
- int UseFeatureFrequency = false;
int TraceMalloc = 0;
bool HandleAbrt = false;
bool HandleBus = false;
diff --git a/lib/fuzzer/FuzzerShmemPosix.cpp b/lib/fuzzer/FuzzerShmemPosix.cpp
index 50cdcfb509dc..41a93f61004b 100644
--- a/lib/fuzzer/FuzzerShmemPosix.cpp
+++ b/lib/fuzzer/FuzzerShmemPosix.cpp
@@ -32,6 +32,11 @@ std::string SharedMemoryRegion::Path(const char *Name) {
std::string SharedMemoryRegion::SemName(const char *Name, int Idx) {
std::string Res(Name);
+ // When passing a name without a leading <slash> character to
+ // sem_open, the behaviour is unspecified in POSIX. Add a leading
+ // <slash> character for the name if there is no such one.
+ if (!Res.empty() && Res[0] != '/')
+ Res.insert(Res.begin(), '/');
return Res + (char)('0' + Idx);
}
@@ -52,7 +57,7 @@ bool SharedMemoryRegion::Create(const char *Name) {
for (int i = 0; i < 2; i++) {
sem_unlink(SemName(Name, i).c_str());
Semaphore[i] = sem_open(SemName(Name, i).c_str(), O_CREAT, 0644, 0);
- if (Semaphore[i] == (void *)-1)
+ if (Semaphore[i] == SEM_FAILED)
return false;
}
IAmServer = true;
@@ -70,7 +75,7 @@ bool SharedMemoryRegion::Open(const char *Name) {
return false;
for (int i = 0; i < 2; i++) {
Semaphore[i] = sem_open(SemName(Name, i).c_str(), 0);
- if (Semaphore[i] == (void *)-1)
+ if (Semaphore[i] == SEM_FAILED)
return false;
}
IAmServer = false;
diff --git a/lib/fuzzer/FuzzerTracePC.cpp b/lib/fuzzer/FuzzerTracePC.cpp
index 5e9f9f2f6dcc..29ffc8e34fc0 100644
--- a/lib/fuzzer/FuzzerTracePC.cpp
+++ b/lib/fuzzer/FuzzerTracePC.cpp
@@ -39,8 +39,6 @@ namespace fuzzer {
TracePC TPC;
-int ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr;
-
uint8_t *TracePC::Counters() const {
return __sancov_trace_pc_guard_8bit_counters;
}
@@ -59,6 +57,49 @@ size_t TracePC::GetTotalPCCoverage() {
return Res;
}
+template<class CallBack>
+void TracePC::IterateInline8bitCounters(CallBack CB) const {
+ if (NumInline8bitCounters && NumInline8bitCounters == NumPCsInPCTables) {
+ size_t CounterIdx = 0;
+ for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
+ uint8_t *Beg = ModuleCounters[i].Start;
+ size_t Size = ModuleCounters[i].Stop - Beg;
+ assert(Size == (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
+ for (size_t j = 0; j < Size; j++, CounterIdx++)
+ CB(i, j, CounterIdx);
+ }
+ }
+}
+
+// Initializes unstable counters by copying Inline8bitCounters to unstable
+// counters.
+void TracePC::InitializeUnstableCounters() {
+ IterateInline8bitCounters([&](int i, int j, int UnstableIdx) {
+ UnstableCounters[UnstableIdx].Counter = ModuleCounters[i].Start[j];
+ });
+}
+
+// Compares the current counters with counters from previous runs
+// and records differences as unstable edges.
+void TracePC::UpdateUnstableCounters(int UnstableMode) {
+ IterateInline8bitCounters([&](int i, int j, int UnstableIdx) {
+ if (ModuleCounters[i].Start[j] != UnstableCounters[UnstableIdx].Counter) {
+ UnstableCounters[UnstableIdx].IsUnstable = true;
+ if (UnstableMode == ZeroUnstable)
+ UnstableCounters[UnstableIdx].Counter = 0;
+ else if (UnstableMode == MinUnstable)
+ UnstableCounters[UnstableIdx].Counter = std::min(
+ ModuleCounters[i].Start[j], UnstableCounters[UnstableIdx].Counter);
+ }
+ });
+}
+
+// Moves the minimum hit counts to ModuleCounters.
+void TracePC::ApplyUnstableCounters() {
+ IterateInline8bitCounters([&](int i, int j, int UnstableIdx) {
+ ModuleCounters[i].Start[j] = UnstableCounters[UnstableIdx].Counter;
+ });
+}
void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) {
if (Start == Stop) return;
@@ -132,8 +173,8 @@ void TracePC::PrintModuleInfo() {
_Exit(1);
}
}
- if (size_t NumClangCounters = ClangCountersEnd() - ClangCountersBegin())
- Printf("INFO: %zd Clang Coverage Counters\n", NumClangCounters);
+ if (size_t NumExtraCounters = ExtraCountersEnd() - ExtraCountersBegin())
+ Printf("INFO: %zd Extra Counters\n", NumExtraCounters);
}
ATTRIBUTE_NO_SANITIZE_ALL
@@ -147,28 +188,25 @@ void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
void TracePC::UpdateObservedPCs() {
Vector<uintptr_t> CoveredFuncs;
auto ObservePC = [&](uintptr_t PC) {
- if (ObservedPCs.insert(PC).second && DoPrintNewPCs)
- PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PC + 1);
+ if (ObservedPCs.insert(PC).second && DoPrintNewPCs) {
+ PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", PC + 1);
+ Printf("\n");
+ }
};
auto Observe = [&](const PCTableEntry &TE) {
if (TE.PCFlags & 1)
- if (ObservedFuncs.insert(TE.PC).second && NumPrintNewFuncs)
+ if (++ObservedFuncs[TE.PC] == 1 && NumPrintNewFuncs)
CoveredFuncs.push_back(TE.PC);
ObservePC(TE.PC);
};
if (NumPCsInPCTables) {
if (NumInline8bitCounters == NumPCsInPCTables) {
- for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
- uint8_t *Beg = ModuleCounters[i].Start;
- size_t Size = ModuleCounters[i].Stop - Beg;
- assert(Size ==
- (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
- for (size_t j = 0; j < Size; j++)
- if (Beg[j])
- Observe(ModulePCTable[i].Start[j]);
- }
+ IterateInline8bitCounters([&](int i, int j, int CounterIdx) {
+ if (ModuleCounters[i].Start[j])
+ Observe(ModulePCTable[i].Start[j]);
+ });
} else if (NumGuards == NumPCsInPCTables) {
size_t GuardIdx = 1;
for (size_t i = 0; i < NumModules; i++) {
@@ -182,17 +220,12 @@ void TracePC::UpdateObservedPCs() {
}
}
}
- if (size_t NumClangCounters =
- ClangCountersEnd() - ClangCountersBegin()) {
- auto P = ClangCountersBegin();
- for (size_t Idx = 0; Idx < NumClangCounters; Idx++)
- if (P[Idx])
- ObservePC((uintptr_t)Idx);
- }
- for (size_t i = 0, N = Min(CoveredFuncs.size(), NumPrintNewFuncs); i < N; i++) {
- Printf("\tNEW_FUNC[%zd/%zd]: ", i, CoveredFuncs.size());
- PrintPC("%p %F %L\n", "%p\n", CoveredFuncs[i] + 1);
+ for (size_t i = 0, N = Min(CoveredFuncs.size(), NumPrintNewFuncs); i < N;
+ i++) {
+ Printf("\tNEW_FUNC[%zd/%zd]: ", i + 1, CoveredFuncs.size());
+ PrintPC("%p %F %L", "%p", CoveredFuncs[i] + 1);
+ Printf("\n");
}
}
@@ -218,6 +251,57 @@ static std::string GetModuleName(uintptr_t PC) {
return ModulePathRaw;
}
+template<class CallBack>
+void TracePC::IterateCoveredFunctions(CallBack CB) {
+ for (size_t i = 0; i < NumPCTables; i++) {
+ auto &M = ModulePCTable[i];
+ assert(M.Start < M.Stop);
+ auto ModuleName = GetModuleName(M.Start->PC);
+ for (auto NextFE = M.Start; NextFE < M.Stop; ) {
+ auto FE = NextFE;
+ assert((FE->PCFlags & 1) && "Not a function entry point");
+ do {
+ NextFE++;
+ } while (NextFE < M.Stop && !(NextFE->PCFlags & 1));
+ if (ObservedFuncs.count(FE->PC))
+ CB(FE, NextFE, ObservedFuncs[FE->PC]);
+ }
+ }
+}
+
+void TracePC::SetFocusFunction(const std::string &FuncName) {
+ // This function should be called once.
+ assert(FocusFunction.first > NumModulesWithInline8bitCounters);
+ if (FuncName.empty())
+ return;
+ for (size_t M = 0; M < NumModulesWithInline8bitCounters; M++) {
+ auto &PCTE = ModulePCTable[M];
+ size_t N = PCTE.Stop - PCTE.Start;
+ for (size_t I = 0; I < N; I++) {
+ if (!(PCTE.Start[I].PCFlags & 1)) continue; // not a function entry.
+ auto Name = DescribePC("%F", GetNextInstructionPc(PCTE.Start[I].PC));
+ if (Name[0] == 'i' && Name[1] == 'n' && Name[2] == ' ')
+ Name = Name.substr(3, std::string::npos);
+ if (FuncName != Name) continue;
+ Printf("INFO: Focus function is set to '%s'\n", Name.c_str());
+ FocusFunction = {M, I};
+ return;
+ }
+ }
+}
+
+bool TracePC::ObservedFocusFunction() {
+ size_t I = FocusFunction.first;
+ size_t J = FocusFunction.second;
+ if (I >= NumModulesWithInline8bitCounters)
+ return false;
+ auto &MC = ModuleCounters[I];
+ size_t Size = MC.Stop - MC.Start;
+ if (J >= Size)
+ return false;
+ return MC.Start[J] != 0;
+}
+
void TracePC::PrintCoverage() {
if (!EF->__sanitizer_symbolize_pc ||
!EF->__sanitizer_get_module_and_offset_for_pc) {
@@ -227,53 +311,33 @@ void TracePC::PrintCoverage() {
return;
}
Printf("COVERAGE:\n");
- std::string LastFunctionName = "";
- std::string LastFileStr = "";
- Set<size_t> UncoveredLines;
- Set<size_t> CoveredLines;
-
- auto FunctionEndCallback = [&](const std::string &CurrentFunc,
- const std::string &CurrentFile) {
- if (LastFunctionName != CurrentFunc) {
- if (CoveredLines.empty() && !UncoveredLines.empty()) {
- Printf("UNCOVERED_FUNC: %s\n", LastFunctionName.c_str());
- } else {
- for (auto Line : UncoveredLines) {
- if (!CoveredLines.count(Line))
- Printf("UNCOVERED_LINE: %s %s:%zd\n", LastFunctionName.c_str(),
- LastFileStr.c_str(), Line);
- }
- }
-
- UncoveredLines.clear();
- CoveredLines.clear();
- LastFunctionName = CurrentFunc;
- LastFileStr = CurrentFile;
- }
+ auto CoveredFunctionCallback = [&](const PCTableEntry *First,
+ const PCTableEntry *Last,
+ uintptr_t Counter) {
+ assert(First < Last);
+ auto VisualizePC = GetNextInstructionPc(First->PC);
+ std::string FileStr = DescribePC("%s", VisualizePC);
+ if (!IsInterestingCoverageFile(FileStr))
+ return;
+ std::string FunctionStr = DescribePC("%F", VisualizePC);
+ if (FunctionStr.find("in ") == 0)
+ FunctionStr = FunctionStr.substr(3);
+ std::string LineStr = DescribePC("%l", VisualizePC);
+ size_t Line = std::stoul(LineStr);
+ size_t NumEdges = Last - First;
+ Vector<uintptr_t> UncoveredPCs;
+ for (auto TE = First; TE < Last; TE++)
+ if (!ObservedPCs.count(TE->PC))
+ UncoveredPCs.push_back(TE->PC);
+ Printf("COVERED_FUNC: hits: %zd", Counter);
+ Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges);
+ Printf(" %s %s:%zd\n", FunctionStr.c_str(), FileStr.c_str(), Line);
+ for (auto PC: UncoveredPCs)
+ Printf(" UNCOVERED_PC: %s\n",
+ DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str());
};
- for (size_t i = 0; i < NumPCTables; i++) {
- auto &M = ModulePCTable[i];
- assert(M.Start < M.Stop);
- auto ModuleName = GetModuleName(M.Start->PC);
- for (auto Ptr = M.Start; Ptr < M.Stop; Ptr++) {
- auto PC = Ptr->PC;
- auto VisualizePC = GetNextInstructionPc(PC);
- bool IsObserved = ObservedPCs.count(PC);
- std::string FileStr = DescribePC("%s", VisualizePC);
- if (!IsInterestingCoverageFile(FileStr)) continue;
- std::string FunctionStr = DescribePC("%F", VisualizePC);
- FunctionEndCallback(FunctionStr, FileStr);
- std::string LineStr = DescribePC("%l", VisualizePC);
- size_t Line = std::stoul(LineStr);
- if (IsObserved && CoveredLines.insert(Line).second)
- Printf("COVERED: %s %s:%zd\n", FunctionStr.c_str(), FileStr.c_str(),
- Line);
- else
- UncoveredLines.insert(Line);
- }
- }
- FunctionEndCallback("", "");
+ IterateCoveredFunctions(CoveredFunctionCallback);
}
void TracePC::DumpCoverage() {
@@ -285,6 +349,15 @@ void TracePC::DumpCoverage() {
}
}
+void TracePC::PrintUnstableStats() {
+ size_t count = 0;
+ for (size_t i = 0; i < NumInline8bitCounters; i++)
+ if (UnstableCounters[i].IsUnstable)
+ count++;
+ Printf("stat::stability_rate: %.2f\n",
+ 100 - static_cast<float>(count * 100) / NumInline8bitCounters);
+}
+
// Value profile.
// We keep track of various values that affect control flow.
// These values are inserted into a bit-set-based hash map.
@@ -334,7 +407,14 @@ void TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) {
TORC4.Insert(ArgXor, Arg1, Arg2);
else if (sizeof(T) == 8)
TORC8.Insert(ArgXor, Arg1, Arg2);
- ValueProfileMap.AddValue(Idx);
+ // TODO: remove these flags and instead use all metrics at once.
+ if (UseValueProfileMask & 1)
+ ValueProfileMap.AddValue(Idx);
+ if (UseValueProfileMask & 2)
+ ValueProfileMap.AddValue(
+ PC * 64 + (Arg1 == Arg2 ? 0 : __builtin_clzll(Arg1 - Arg2) + 1));
+ if (UseValueProfileMask & 4) // alternative way to use the hamming distance
+ ValueProfileMap.AddValue(PC * 64 + ArgDistance);
}
static size_t InternalStrnlen(const char *S, size_t MaxLen) {
@@ -536,7 +616,7 @@ void __sanitizer_cov_trace_gep(uintptr_t Idx) {
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
const void *s2, size_t n, int result) {
- if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
+ if (!fuzzer::RunningUserCallback) return;
if (result == 0) return; // No reason to mutate.
if (n <= 1) return; // Not interesting.
fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/false);
@@ -545,7 +625,7 @@ void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
const char *s2, size_t n, int result) {
- if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
+ if (!fuzzer::RunningUserCallback) return;
if (result == 0) return; // No reason to mutate.
size_t Len1 = fuzzer::InternalStrnlen(s1, n);
size_t Len2 = fuzzer::InternalStrnlen(s2, n);
@@ -558,7 +638,7 @@ void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
const char *s2, int result) {
- if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
+ if (!fuzzer::RunningUserCallback) return;
if (result == 0) return; // No reason to mutate.
size_t N = fuzzer::InternalStrnlen2(s1, s2);
if (N <= 1) return; // Not interesting.
@@ -568,35 +648,35 @@ void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
const char *s2, size_t n, int result) {
- if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
+ if (!fuzzer::RunningUserCallback) return;
return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
const char *s2, int result) {
- if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
+ if (!fuzzer::RunningUserCallback) return;
return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result);
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1,
const char *s2, char *result) {
- if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
+ if (!fuzzer::RunningUserCallback) return;
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
const char *s2, char *result) {
- if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
+ if (!fuzzer::RunningUserCallback) return;
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
}
ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY
void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
const void *s2, size_t len2, void *result) {
- if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return;
+ if (!fuzzer::RunningUserCallback) return;
fuzzer::TPC.MMT.Add(reinterpret_cast<const uint8_t *>(s2), len2);
}
} // extern "C"
diff --git a/lib/fuzzer/FuzzerTracePC.h b/lib/fuzzer/FuzzerTracePC.h
index c3f241b905b1..097ba69bdc08 100644
--- a/lib/fuzzer/FuzzerTracePC.h
+++ b/lib/fuzzer/FuzzerTracePC.h
@@ -17,6 +17,7 @@
#include "FuzzerValueBitMap.h"
#include <set>
+#include <unordered_map>
namespace fuzzer {
@@ -73,6 +74,11 @@ class TracePC {
// How many bits of PC are used from __sanitizer_cov_trace_pc.
static const size_t kTracePcBits = 18;
+ enum HandleUnstableOptions {
+ MinUnstable = 1,
+ ZeroUnstable = 2,
+ };
+
void HandleInit(uint32_t *Start, uint32_t *Stop);
void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop);
void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop);
@@ -80,8 +86,7 @@ class TracePC {
template <class T> void HandleCmp(uintptr_t PC, T Arg1, T Arg2);
size_t GetTotalPCCoverage();
void SetUseCounters(bool UC) { UseCounters = UC; }
- void SetUseClangCoverage(bool UCC) { UseClangCoverage = UCC; }
- void SetUseValueProfile(bool VP) { UseValueProfile = VP; }
+ void SetUseValueProfileMask(uint32_t VPMask) { UseValueProfileMask = VPMask; }
void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; }
void SetPrintNewFuncs(size_t P) { NumPrintNewFuncs = P; }
void UpdateObservedPCs();
@@ -93,8 +98,6 @@ class TracePC {
memset(Counters(), 0, GetNumPCs());
ClearExtraCounters();
ClearInlineCounters();
- if (UseClangCoverage)
- ClearClangCounters();
}
void ClearInlineCounters();
@@ -106,6 +109,10 @@ class TracePC {
void PrintCoverage();
void DumpCoverage();
+ void PrintUnstableStats();
+
+ template<class CallBack>
+ void IterateCoveredFunctions(CallBack CB);
void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
size_t n, bool StopAtZero);
@@ -132,10 +139,23 @@ class TracePC {
CB(PC);
}
+ void SetFocusFunction(const std::string &FuncName);
+ bool ObservedFocusFunction();
+
+ void InitializeUnstableCounters();
+ void UpdateUnstableCounters(int UnstableMode);
+ void ApplyUnstableCounters();
+
private:
+ struct UnstableEdge {
+ uint8_t Counter;
+ bool IsUnstable;
+ };
+
+ UnstableEdge UnstableCounters[kNumPCs];
+
bool UseCounters = false;
- bool UseValueProfile = false;
- bool UseClangCoverage = false;
+ uint32_t UseValueProfileMask = false;
bool DoPrintNewPCs = false;
size_t NumPrintNewFuncs = 0;
@@ -163,7 +183,12 @@ private:
uintptr_t *PCs() const;
Set<uintptr_t> ObservedPCs;
- Set<uintptr_t> ObservedFuncs;
+ std::unordered_map<uintptr_t, uintptr_t> ObservedFuncs; // PC => Counter.
+
+ template <class Callback>
+ void IterateInline8bitCounters(Callback CB) const;
+
+ std::pair<size_t, size_t> FocusFunction = {-1, -1}; // Module and PC IDs.
ValueBitMap ValueProfileMap;
uintptr_t InitialStack;
@@ -251,23 +276,11 @@ void TracePC::CollectFeatures(Callback HandleFeature) const {
}
}
- if (size_t NumClangCounters = ClangCountersEnd() - ClangCountersBegin()) {
- auto P = ClangCountersBegin();
- for (size_t Idx = 0; Idx < NumClangCounters; Idx++)
- if (auto Cnt = P[Idx]) {
- if (UseCounters)
- HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Cnt));
- else
- HandleFeature(FirstFeature + Idx);
- }
- FirstFeature += NumClangCounters;
- }
-
ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), FirstFeature,
Handle8bitCounter);
FirstFeature += (ExtraCountersEnd() - ExtraCountersBegin()) * 8;
- if (UseValueProfile) {
+ if (UseValueProfileMask) {
ValueProfileMap.ForEach([&](size_t Idx) {
HandleFeature(FirstFeature + Idx);
});
diff --git a/lib/fuzzer/FuzzerUtil.cpp b/lib/fuzzer/FuzzerUtil.cpp
index 96b37d34815d..6286f9a718ad 100644
--- a/lib/fuzzer/FuzzerUtil.cpp
+++ b/lib/fuzzer/FuzzerUtil.cpp
@@ -16,6 +16,7 @@
#include <chrono>
#include <cstring>
#include <errno.h>
+#include <mutex>
#include <signal.h>
#include <sstream>
#include <stdio.h>
@@ -179,8 +180,12 @@ std::string Base64(const Unit &U) {
return Res;
}
+static std::mutex SymbolizeMutex;
+
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) {
- if (!EF->__sanitizer_symbolize_pc) return "<can not symbolize>";
+ std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock);
+ if (!EF->__sanitizer_symbolize_pc || !l.owns_lock())
+ return "<can not symbolize>";
char PcDescr[1024] = {};
EF->__sanitizer_symbolize_pc(reinterpret_cast<void*>(PC),
SymbolizedFMT, PcDescr, sizeof(PcDescr));
@@ -195,6 +200,18 @@ void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) {
Printf(FallbackFMT, PC);
}
+void PrintStackTrace() {
+ std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock);
+ if (EF->__sanitizer_print_stack_trace && l.owns_lock())
+ EF->__sanitizer_print_stack_trace();
+}
+
+void PrintMemoryProfile() {
+ std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock);
+ if (EF->__sanitizer_print_memory_profile && l.owns_lock())
+ EF->__sanitizer_print_memory_profile(95, 8);
+}
+
unsigned NumberOfCpuCores() {
unsigned N = std::thread::hardware_concurrency();
if (!N) {
diff --git a/lib/fuzzer/FuzzerUtil.h b/lib/fuzzer/FuzzerUtil.h
index f2ed028ce78e..8c5c57c3ab83 100644
--- a/lib/fuzzer/FuzzerUtil.h
+++ b/lib/fuzzer/FuzzerUtil.h
@@ -40,6 +40,10 @@ void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC);
std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC);
+void PrintStackTrace();
+
+void PrintMemoryProfile();
+
unsigned NumberOfCpuCores();
// Platform specific functions.
diff --git a/lib/fuzzer/FuzzerUtilFuchsia.cpp b/lib/fuzzer/FuzzerUtilFuchsia.cpp
index a5fdc37fb5a6..cd2bb7438e9d 100644
--- a/lib/fuzzer/FuzzerUtilFuchsia.cpp
+++ b/lib/fuzzer/FuzzerUtilFuchsia.cpp
@@ -17,29 +17,54 @@
#include <cerrno>
#include <cinttypes>
#include <cstdint>
-#include <fbl/unique_fd.h>
#include <fcntl.h>
-#include <launchpad/launchpad.h>
+#include <lib/fdio/spawn.h>
#include <string>
+#include <sys/select.h>
#include <thread>
+#include <unistd.h>
#include <zircon/errors.h>
+#include <zircon/process.h>
+#include <zircon/sanitizer.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
+#include <zircon/syscalls/debug.h>
+#include <zircon/syscalls/exception.h>
#include <zircon/syscalls/port.h>
#include <zircon/types.h>
-#include <zx/object.h>
-#include <zx/port.h>
-#include <zx/process.h>
-#include <zx/time.h>
namespace fuzzer {
+// Given that Fuchsia doesn't have the POSIX signals that libFuzzer was written
+// around, the general approach is to spin up dedicated threads to watch for
+// each requested condition (alarm, interrupt, crash). Of these, the crash
+// handler is the most involved, as it requires resuming the crashed thread in
+// order to invoke the sanitizers to get the needed state.
+
+// Forward declaration of assembly trampoline needed to resume crashed threads.
+// This appears to have external linkage to C++, which is why it's not in the
+// anonymous namespace. The assembly definition inside MakeTrampoline()
+// actually defines the symbol with internal linkage only.
+void CrashTrampolineAsm() __asm__("CrashTrampolineAsm");
+
namespace {
+// TODO(phosek): remove this and replace it with ZX_TIME_INFINITE
+#define ZX_TIME_INFINITE_OLD INT64_MAX
+
// A magic value for the Zircon exception port, chosen to spell 'FUZZING'
// when interpreted as a byte sequence on little-endian platforms.
const uint64_t kFuzzingCrash = 0x474e495a5a5546;
+// Helper function to handle Zircon syscall failures.
+void ExitOnErr(zx_status_t Status, const char *Syscall) {
+ if (Status != ZX_OK) {
+ Printf("libFuzzer: %s failed: %s\n", Syscall,
+ _zx_status_get_string(Status));
+ exit(1);
+ }
+}
+
void AlarmHandler(int Seconds) {
while (true) {
SleepSeconds(Seconds);
@@ -48,37 +73,226 @@ void AlarmHandler(int Seconds) {
}
void InterruptHandler() {
+ fd_set readfds;
// Ctrl-C sends ETX in Zircon.
- while (getchar() != 0x03);
+ do {
+ FD_ZERO(&readfds);
+ FD_SET(STDIN_FILENO, &readfds);
+ select(STDIN_FILENO + 1, &readfds, nullptr, nullptr, nullptr);
+ } while(!FD_ISSET(STDIN_FILENO, &readfds) || getchar() != 0x03);
Fuzzer::StaticInterruptCallback();
}
-void CrashHandler(zx::port *Port) {
- std::unique_ptr<zx::port> ExceptionPort(Port);
- zx_port_packet_t Packet;
- ExceptionPort->wait(ZX_TIME_INFINITE, &Packet, 0);
- // Unbind as soon as possible so we don't receive exceptions from this thread.
- if (zx_task_bind_exception_port(ZX_HANDLE_INVALID, ZX_HANDLE_INVALID,
- kFuzzingCrash, 0) != ZX_OK) {
- // Shouldn't happen; if it does the safest option is to just exit.
- Printf("libFuzzer: unable to unbind exception port; aborting!\n");
- exit(1);
- }
- if (Packet.key != kFuzzingCrash) {
- Printf("libFuzzer: invalid crash key: %" PRIx64 "; aborting!\n",
- Packet.key);
- exit(1);
- }
- // CrashCallback should not return from this call
+// For the crash handler, we need to call Fuzzer::StaticCrashSignalCallback
+// without POSIX signal handlers. To achieve this, we use an assembly function
+// to add the necessary CFI unwinding information and a C function to bridge
+// from that back into C++.
+
+// FIXME: This works as a short-term solution, but this code really shouldn't be
+// architecture dependent. A better long term solution is to implement remote
+// unwinding and expose the necessary APIs through sanitizer_common and/or ASAN
+// to allow the exception handling thread to gather the crash state directly.
+//
+// Alternatively, Fuchsia may in future actually implement basic signal
+// handling for the machine trap signals.
+#if defined(__x86_64__)
+#define FOREACH_REGISTER(OP_REG, OP_NUM) \
+ OP_REG(rax) \
+ OP_REG(rbx) \
+ OP_REG(rcx) \
+ OP_REG(rdx) \
+ OP_REG(rsi) \
+ OP_REG(rdi) \
+ OP_REG(rbp) \
+ OP_REG(rsp) \
+ OP_REG(r8) \
+ OP_REG(r9) \
+ OP_REG(r10) \
+ OP_REG(r11) \
+ OP_REG(r12) \
+ OP_REG(r13) \
+ OP_REG(r14) \
+ OP_REG(r15) \
+ OP_REG(rip)
+
+#elif defined(__aarch64__)
+#define FOREACH_REGISTER(OP_REG, OP_NUM) \
+ OP_NUM(0) \
+ OP_NUM(1) \
+ OP_NUM(2) \
+ OP_NUM(3) \
+ OP_NUM(4) \
+ OP_NUM(5) \
+ OP_NUM(6) \
+ OP_NUM(7) \
+ OP_NUM(8) \
+ OP_NUM(9) \
+ OP_NUM(10) \
+ OP_NUM(11) \
+ OP_NUM(12) \
+ OP_NUM(13) \
+ OP_NUM(14) \
+ OP_NUM(15) \
+ OP_NUM(16) \
+ OP_NUM(17) \
+ OP_NUM(18) \
+ OP_NUM(19) \
+ OP_NUM(20) \
+ OP_NUM(21) \
+ OP_NUM(22) \
+ OP_NUM(23) \
+ OP_NUM(24) \
+ OP_NUM(25) \
+ OP_NUM(26) \
+ OP_NUM(27) \
+ OP_NUM(28) \
+ OP_NUM(29) \
+ OP_NUM(30) \
+ OP_REG(sp)
+
+#else
+#error "Unsupported architecture for fuzzing on Fuchsia"
+#endif
+
+// Produces a CFI directive for the named or numbered register.
+#define CFI_OFFSET_REG(reg) ".cfi_offset " #reg ", %c[" #reg "]\n"
+#define CFI_OFFSET_NUM(num) CFI_OFFSET_REG(r##num)
+
+// Produces an assembler input operand for the named or numbered register.
+#define ASM_OPERAND_REG(reg) \
+ [reg] "i"(offsetof(zx_thread_state_general_regs_t, reg)),
+#define ASM_OPERAND_NUM(num) \
+ [r##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num])),
+
+// Trampoline to bridge from the assembly below to the static C++ crash
+// callback.
+__attribute__((noreturn))
+static void StaticCrashHandler() {
Fuzzer::StaticCrashSignalCallback();
+ for (;;) {
+ _Exit(1);
+ }
+}
+
+// Creates the trampoline with the necessary CFI information to unwind through
+// to the crashing call stack. The attribute is necessary because the function
+// is never called; it's just a container around the assembly to allow it to
+// use operands for compile-time computed constants.
+__attribute__((used))
+void MakeTrampoline() {
+ __asm__(".cfi_endproc\n"
+ ".pushsection .text.CrashTrampolineAsm\n"
+ ".type CrashTrampolineAsm,STT_FUNC\n"
+"CrashTrampolineAsm:\n"
+ ".cfi_startproc simple\n"
+ ".cfi_signal_frame\n"
+#if defined(__x86_64__)
+ ".cfi_return_column rip\n"
+ ".cfi_def_cfa rsp, 0\n"
+ FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)
+ "call %c[StaticCrashHandler]\n"
+ "ud2\n"
+#elif defined(__aarch64__)
+ ".cfi_return_column 33\n"
+ ".cfi_def_cfa sp, 0\n"
+ ".cfi_offset 33, %c[pc]\n"
+ FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM)
+ "bl %[StaticCrashHandler]\n"
+#else
+#error "Unsupported architecture for fuzzing on Fuchsia"
+#endif
+ ".cfi_endproc\n"
+ ".size CrashTrampolineAsm, . - CrashTrampolineAsm\n"
+ ".popsection\n"
+ ".cfi_startproc\n"
+ : // No outputs
+ : FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM)
+#if defined(__aarch64__)
+ ASM_OPERAND_REG(pc)
+#endif
+ [StaticCrashHandler] "i" (StaticCrashHandler));
+}
+
+void CrashHandler(zx_handle_t *Event) {
+ // This structure is used to ensure we close handles to objects we create in
+ // this handler.
+ struct ScopedHandle {
+ ~ScopedHandle() { _zx_handle_close(Handle); }
+ zx_handle_t Handle = ZX_HANDLE_INVALID;
+ };
+
+ // Create and bind the exception port. We need to claim to be a "debugger" so
+ // the kernel will allow us to modify and resume dying threads (see below).
+ // Once the port is set, we can signal the main thread to continue and wait
+ // for the exception to arrive.
+ ScopedHandle Port;
+ ExitOnErr(_zx_port_create(0, &Port.Handle), "_zx_port_create");
+ zx_handle_t Self = _zx_process_self();
+
+ ExitOnErr(_zx_task_bind_exception_port(Self, Port.Handle, kFuzzingCrash,
+ ZX_EXCEPTION_PORT_DEBUGGER),
+ "_zx_task_bind_exception_port");
+
+ ExitOnErr(_zx_object_signal(*Event, 0, ZX_USER_SIGNAL_0),
+ "_zx_object_signal");
+
+ zx_port_packet_t Packet;
+ ExitOnErr(_zx_port_wait(Port.Handle, ZX_TIME_INFINITE_OLD, &Packet),
+ "_zx_port_wait");
+
+ // At this point, we want to get the state of the crashing thread, but
+ // libFuzzer and the sanitizers assume this will happen from that same thread
+ // via a POSIX signal handler. "Resurrecting" the thread in the middle of the
+ // appropriate callback is as simple as forcibly setting the instruction
+ // pointer/program counter, provided we NEVER EVER return from that function
+ // (since otherwise our stack will not be valid).
+ ScopedHandle Thread;
+ ExitOnErr(_zx_object_get_child(Self, Packet.exception.tid,
+ ZX_RIGHT_SAME_RIGHTS, &Thread.Handle),
+ "_zx_object_get_child");
+
+ zx_thread_state_general_regs_t GeneralRegisters;
+ ExitOnErr(_zx_thread_read_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,
+ &GeneralRegisters, sizeof(GeneralRegisters)),
+ "_zx_thread_read_state");
+
+ // To unwind properly, we need to push the crashing thread's register state
+ // onto the stack and jump into a trampoline with CFI instructions on how
+ // to restore it.
+#if defined(__x86_64__)
+ uintptr_t StackPtr =
+ (GeneralRegisters.rsp - (128 + sizeof(GeneralRegisters))) &
+ -(uintptr_t)16;
+ __unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,
+ sizeof(GeneralRegisters));
+ GeneralRegisters.rsp = StackPtr;
+ GeneralRegisters.rip = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);
+
+#elif defined(__aarch64__)
+ uintptr_t StackPtr =
+ (GeneralRegisters.sp - sizeof(GeneralRegisters)) & -(uintptr_t)16;
+ __unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters,
+ sizeof(GeneralRegisters));
+ GeneralRegisters.sp = StackPtr;
+ GeneralRegisters.pc = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm);
+
+#else
+#error "Unsupported architecture for fuzzing on Fuchsia"
+#endif
+
+ // Now force the crashing thread's state.
+ ExitOnErr(_zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS,
+ &GeneralRegisters, sizeof(GeneralRegisters)),
+ "_zx_thread_write_state");
+
+ ExitOnErr(_zx_task_resume_from_exception(Thread.Handle, Port.Handle, 0),
+ "_zx_task_resume_from_exception");
}
} // namespace
// Platform specific functions.
void SetSignalHandler(const FuzzingOptions &Options) {
- zx_status_t rc;
-
// Set up alarm handler if needed.
if (Options.UnitTimeoutSec > 0) {
std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1);
@@ -96,37 +310,30 @@ void SetSignalHandler(const FuzzingOptions &Options) {
!Options.HandleFpe && !Options.HandleAbrt)
return;
- // Create an exception port
- zx::port *ExceptionPort = new zx::port();
- if ((rc = zx::port::create(0, ExceptionPort)) != ZX_OK) {
- Printf("libFuzzer: zx_port_create failed: %s\n", zx_status_get_string(rc));
- exit(1);
- }
+ // Set up the crash handler and wait until it is ready before proceeding.
+ zx_handle_t Event;
+ ExitOnErr(_zx_event_create(0, &Event), "_zx_event_create");
- // Bind the port to receive exceptions from our process
- if ((rc = zx_task_bind_exception_port(zx_process_self(), ExceptionPort->get(),
- kFuzzingCrash, 0)) != ZX_OK) {
- Printf("libFuzzer: unable to bind exception port: %s\n",
- zx_status_get_string(rc));
- exit(1);
- }
+ std::thread T(CrashHandler, &Event);
+ zx_status_t Status = _zx_object_wait_one(Event, ZX_USER_SIGNAL_0,
+ ZX_TIME_INFINITE_OLD, nullptr);
+ _zx_handle_close(Event);
+ ExitOnErr(Status, "_zx_object_wait_one");
- // Set up the crash handler.
- std::thread T(CrashHandler, ExceptionPort);
T.detach();
}
void SleepSeconds(int Seconds) {
- zx::nanosleep(zx::deadline_after(ZX_SEC(Seconds)));
+ _zx_nanosleep(_zx_deadline_after(ZX_SEC(Seconds)));
}
unsigned long GetPid() {
zx_status_t rc;
zx_info_handle_basic_t Info;
- if ((rc = zx_object_get_info(zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info,
- sizeof(Info), NULL, NULL)) != ZX_OK) {
+ if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info,
+ sizeof(Info), NULL, NULL)) != ZX_OK) {
Printf("libFuzzer: unable to get info about self: %s\n",
- zx_status_get_string(rc));
+ _zx_status_get_string(rc));
exit(1);
}
return Info.koid;
@@ -135,15 +342,30 @@ unsigned long GetPid() {
size_t GetPeakRSSMb() {
zx_status_t rc;
zx_info_task_stats_t Info;
- if ((rc = zx_object_get_info(zx_process_self(), ZX_INFO_TASK_STATS, &Info,
- sizeof(Info), NULL, NULL)) != ZX_OK) {
+ if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_TASK_STATS, &Info,
+ sizeof(Info), NULL, NULL)) != ZX_OK) {
Printf("libFuzzer: unable to get info about self: %s\n",
- zx_status_get_string(rc));
+ _zx_status_get_string(rc));
exit(1);
}
return (Info.mem_private_bytes + Info.mem_shared_bytes) >> 20;
}
+template <typename Fn>
+class RunOnDestruction {
+ public:
+ explicit RunOnDestruction(Fn fn) : fn_(fn) {}
+ ~RunOnDestruction() { fn_(); }
+
+ private:
+ Fn fn_;
+};
+
+template <typename Fn>
+RunOnDestruction<Fn> at_scope_exit(Fn fn) {
+ return RunOnDestruction<Fn>(fn);
+}
+
int ExecuteCommand(const Command &Cmd) {
zx_status_t rc;
@@ -151,30 +373,24 @@ int ExecuteCommand(const Command &Cmd) {
auto Args = Cmd.getArguments();
size_t Argc = Args.size();
assert(Argc != 0);
- std::unique_ptr<const char *[]> Argv(new const char *[Argc]);
+ std::unique_ptr<const char *[]> Argv(new const char *[Argc + 1]);
for (size_t i = 0; i < Argc; ++i)
Argv[i] = Args[i].c_str();
-
- // Create the basic launchpad. Clone everything except stdio.
- launchpad_t *lp;
- launchpad_create(ZX_HANDLE_INVALID, Argv[0], &lp);
- launchpad_load_from_file(lp, Argv[0]);
- launchpad_set_args(lp, Argc, Argv.get());
- launchpad_clone(lp, LP_CLONE_ALL & (~LP_CLONE_FDIO_STDIO));
+ Argv[Argc] = nullptr;
// Determine stdout
int FdOut = STDOUT_FILENO;
- fbl::unique_fd OutputFile;
+
if (Cmd.hasOutputFile()) {
auto Filename = Cmd.getOutputFile();
- OutputFile.reset(open(Filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0));
- if (!OutputFile) {
+ FdOut = open(Filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0);
+ if (FdOut == -1) {
Printf("libFuzzer: failed to open %s: %s\n", Filename.c_str(),
strerror(errno));
return ZX_ERR_IO;
}
- FdOut = OutputFile.get();
}
+ auto CloseFdOut = at_scope_exit([&]() { close(FdOut); } );
// Determine stderr
int FdErr = STDERR_FILENO;
@@ -182,36 +398,59 @@ int ExecuteCommand(const Command &Cmd) {
FdErr = FdOut;
// Clone the file descriptors into the new process
- if ((rc = launchpad_clone_fd(lp, STDIN_FILENO, STDIN_FILENO)) != ZX_OK ||
- (rc = launchpad_clone_fd(lp, FdOut, STDOUT_FILENO)) != ZX_OK ||
- (rc = launchpad_clone_fd(lp, FdErr, STDERR_FILENO)) != ZX_OK) {
- Printf("libFuzzer: failed to clone FDIO: %s\n", zx_status_get_string(rc));
- return rc;
- }
-
- // Start the process
+ fdio_spawn_action_t SpawnAction[] = {
+ {
+ .action = FDIO_SPAWN_ACTION_CLONE_FD,
+ .fd =
+ {
+ .local_fd = STDIN_FILENO,
+ .target_fd = STDIN_FILENO,
+ },
+ },
+ {
+ .action = FDIO_SPAWN_ACTION_CLONE_FD,
+ .fd =
+ {
+ .local_fd = FdOut,
+ .target_fd = STDOUT_FILENO,
+ },
+ },
+ {
+ .action = FDIO_SPAWN_ACTION_CLONE_FD,
+ .fd =
+ {
+ .local_fd = FdErr,
+ .target_fd = STDERR_FILENO,
+ },
+ },
+ };
+
+ // Start the process.
+ char ErrorMsg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
zx_handle_t ProcessHandle = ZX_HANDLE_INVALID;
- const char *ErrorMsg = nullptr;
- if ((rc = launchpad_go(lp, &ProcessHandle, &ErrorMsg)) != ZX_OK) {
+ rc = fdio_spawn_etc(
+ ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL & (~FDIO_SPAWN_CLONE_STDIO),
+ Argv[0], Argv.get(), nullptr, 3, SpawnAction, &ProcessHandle, ErrorMsg);
+ if (rc != ZX_OK) {
Printf("libFuzzer: failed to launch '%s': %s, %s\n", Argv[0], ErrorMsg,
- zx_status_get_string(rc));
+ _zx_status_get_string(rc));
return rc;
}
- zx::process Process(ProcessHandle);
+ auto CloseHandle = at_scope_exit([&]() { _zx_handle_close(ProcessHandle); });
// Now join the process and return the exit status.
- if ((rc = Process.wait_one(ZX_PROCESS_TERMINATED, ZX_TIME_INFINITE,
- nullptr)) != ZX_OK) {
+ if ((rc = _zx_object_wait_one(ProcessHandle, ZX_PROCESS_TERMINATED,
+ ZX_TIME_INFINITE_OLD, nullptr)) != ZX_OK) {
Printf("libFuzzer: failed to join '%s': %s\n", Argv[0],
- zx_status_get_string(rc));
+ _zx_status_get_string(rc));
return rc;
}
zx_info_process_t Info;
- if ((rc = Process.get_info(ZX_INFO_PROCESS, &Info, sizeof(Info), nullptr,
- nullptr)) != ZX_OK) {
+ if ((rc = _zx_object_get_info(ProcessHandle, ZX_INFO_PROCESS, &Info,
+ sizeof(Info), nullptr, nullptr)) != ZX_OK) {
Printf("libFuzzer: unable to get return code from '%s': %s\n", Argv[0],
- zx_status_get_string(rc));
+ _zx_status_get_string(rc));
return rc;
}
diff --git a/lib/fuzzer/FuzzerUtilLinux.cpp b/lib/fuzzer/FuzzerUtilLinux.cpp
index c7cf2c0a778b..c103fd230b0c 100644
--- a/lib/fuzzer/FuzzerUtilLinux.cpp
+++ b/lib/fuzzer/FuzzerUtilLinux.cpp
@@ -9,7 +9,8 @@
// Misc utils for Linux.
//===----------------------------------------------------------------------===//
#include "FuzzerDefs.h"
-#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD
+#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \
+ LIBFUZZER_OPENBSD
#include "FuzzerCommand.h"
#include <stdlib.h>
@@ -23,4 +24,4 @@ int ExecuteCommand(const Command &Cmd) {
} // namespace fuzzer
-#endif // LIBFUZZER_LINUX || LIBFUZZER_NETBSD
+#endif
diff --git a/lib/fuzzer/FuzzerUtilPosix.cpp b/lib/fuzzer/FuzzerUtilPosix.cpp
index 934b7aa98ff1..bc64d3293702 100644
--- a/lib/fuzzer/FuzzerUtilPosix.cpp
+++ b/lib/fuzzer/FuzzerUtilPosix.cpp
@@ -118,7 +118,8 @@ size_t GetPeakRSSMb() {
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage))
return 0;
- if (LIBFUZZER_LINUX) {
+ if (LIBFUZZER_LINUX || LIBFUZZER_FREEBSD || LIBFUZZER_NETBSD ||
+ LIBFUZZER_OPENBSD) {
// ru_maxrss is in KiB
return usage.ru_maxrss >> 10;
} else if (LIBFUZZER_APPLE) {
diff --git a/lib/fuzzer/afl/afl_driver.cpp b/lib/fuzzer/afl/afl_driver.cpp
index bbe5be795ed4..fa494c03bde0 100644
--- a/lib/fuzzer/afl/afl_driver.cpp
+++ b/lib/fuzzer/afl/afl_driver.cpp
@@ -69,20 +69,38 @@ statistics from the file. If that fails then the process will quit.
#define LIBFUZZER_LINUX 1
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_NETBSD 0
+#define LIBFUZZER_FREEBSD 0
+#define LIBFUZZER_OPENBSD 0
#elif __APPLE__
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_APPLE 1
#define LIBFUZZER_NETBSD 0
+#define LIBFUZZER_FREEBSD 0
+#define LIBFUZZER_OPENBSD 0
#elif __NetBSD__
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_NETBSD 1
+#define LIBFUZZER_FREEBSD 0
+#define LIBFUZZER_OPENBSD 0
+#elif __FreeBSD__
+#define LIBFUZZER_LINUX 0
+#define LIBFUZZER_APPLE 0
+#define LIBFUZZER_NETBSD 0
+#define LIBFUZZER_FREEBSD 1
+#define LIBFUZZER_OPENBSD 0
+#elif __OpenBSD__
+#define LIBFUZZER_LINUX 0
+#define LIBFUZZER_APPLE 0
+#define LIBFUZZER_NETBSD 0
+#define LIBFUZZER_FREEBSD 0
+#define LIBFUZZER_OPENBSD 1
#else
#error "Support for your platform has not been implemented"
#endif
// Used to avoid repeating error checking boilerplate. If cond is false, a
-// fatal error has occured in the program. In this event print error_message
+// fatal error has occurred in the program. In this event print error_message
// to stderr and abort(). Otherwise do nothing. Note that setting
// AFL_DRIVER_STDERR_DUPLICATE_FILENAME may cause error_message to be appended
// to the file as well, if the error occurs after the duplication is performed.
@@ -120,12 +138,24 @@ static const int kNumExtraStats = 2;
static const char *kExtraStatsFormatString = "peak_rss_mb : %u\n"
"slowest_unit_time_sec : %u\n";
+// Experimental feature to use afl_driver without AFL's deferred mode.
+// Needs to run before __afl_auto_init.
+__attribute__((constructor(0))) void __decide_deferred_forkserver(void) {
+ if (getenv("AFL_DRIVER_DONT_DEFER")) {
+ if (unsetenv("__AFL_DEFER_FORKSRV")) {
+ perror("Failed to unset __AFL_DEFER_FORKSRV");
+ abort();
+ }
+ }
+}
+
// Copied from FuzzerUtil.cpp.
size_t GetPeakRSSMb() {
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage))
return 0;
- if (LIBFUZZER_LINUX || LIBFUZZER_NETBSD) {
+ if (LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD ||
+ LIBFUZZER_OPENBSD) {
// ru_maxrss is in KiB
return usage.ru_maxrss >> 10;
} else if (LIBFUZZER_APPLE) {
@@ -270,7 +300,7 @@ int ExecuteFilesOnyByOne(int argc, char **argv) {
assert(in);
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
bytes.size());
- std::cout << "Execution successfull" << std::endl;
+ std::cout << "Execution successful" << std::endl;
}
return 0;
}
@@ -296,7 +326,8 @@ int main(int argc, char **argv) {
maybe_duplicate_stderr();
maybe_initialize_extra_stats();
- __afl_manual_init();
+ if (!getenv("AFL_DRIVER_DONT_DEFER"))
+ __afl_manual_init();
int N = 1000;
if (argc == 2 && argv[1][0] == '-')
diff --git a/lib/fuzzer/build.sh b/lib/fuzzer/build.sh
index 4556af5daf7d..504e54e3a819 100755
--- a/lib/fuzzer/build.sh
+++ b/lib/fuzzer/build.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
LIBFUZZER_SRC_DIR=$(dirname $0)
CXX="${CXX:-clang}"
for f in $LIBFUZZER_SRC_DIR/*.cpp; do
diff --git a/lib/fuzzer/dataflow/DataFlow.cpp b/lib/fuzzer/dataflow/DataFlow.cpp
new file mode 100644
index 000000000000..a79c796ac456
--- /dev/null
+++ b/lib/fuzzer/dataflow/DataFlow.cpp
@@ -0,0 +1,217 @@
+/*===- DataFlow.cpp - a standalone DataFlow tracer -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// An experimental data-flow tracer for fuzz targets.
+// It is based on DFSan and SanitizerCoverage.
+// https://clang.llvm.org/docs/DataFlowSanitizer.html
+// https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flow
+//
+// It executes the fuzz target on the given input while monitoring the
+// data flow for every instrumented comparison instruction.
+//
+// The output shows which functions depend on which bytes of the input.
+//
+// Build:
+// 1. Compile this file with -fsanitize=dataflow
+// 2. Build the fuzz target with -g -fsanitize=dataflow
+// -fsanitize-coverage=trace-pc-guard,pc-table,func,trace-cmp
+// 3. Link those together with -fsanitize=dataflow
+//
+// -fsanitize-coverage=trace-cmp inserts callbacks around every comparison
+// instruction, DFSan modifies the calls to pass the data flow labels.
+// The callbacks update the data flow label for the current function.
+// See e.g. __dfsw___sanitizer_cov_trace_cmp1 below.
+//
+// -fsanitize-coverage=trace-pc-guard,pc-table,func instruments function
+// entries so that the comparison callback knows that current function.
+//
+//
+// Run:
+// # Collect data flow for INPUT_FILE, write to OUTPUT_FILE (default: stdout)
+// ./a.out INPUT_FILE [OUTPUT_FILE]
+//
+// # Print all instrumented functions. llvm-symbolizer must be present in PATH
+// ./a.out
+//
+// Example output:
+// ===============
+// F0 11111111111111
+// F1 10000000000000
+// ===============
+// "FN xxxxxxxxxx": tells what bytes of the input does the function N depend on.
+// The byte string is LEN+1 bytes. The last byte is set if the function
+// depends on the input length.
+//===----------------------------------------------------------------------===*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <execinfo.h> // backtrace_symbols_fd
+
+#include <sanitizer/dfsan_interface.h>
+
+extern "C" {
+extern int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size);
+__attribute__((weak)) extern int LLVMFuzzerInitialize(int *argc, char ***argv);
+} // extern "C"
+
+static size_t InputLen;
+static size_t NumFuncs;
+static const uintptr_t *FuncsBeg;
+static __thread size_t CurrentFunc;
+static dfsan_label *FuncLabels; // Array of NumFuncs elements.
+static char *PrintableStringForLabel; // InputLen + 2 bytes.
+static bool LabelSeen[1 << 8 * sizeof(dfsan_label)];
+
+// Prints all instrumented functions.
+static int PrintFunctions() {
+ // We don't have the symbolizer integrated with dfsan yet.
+ // So use backtrace_symbols_fd and pipe it through llvm-symbolizer.
+ // TODO(kcc): this is pretty ugly and may break in lots of ways.
+ // We'll need to make a proper in-process symbolizer work with DFSan.
+ FILE *Pipe = popen("sed 's/(+/ /g; s/).*//g' "
+ "| llvm-symbolizer "
+ "| grep 'dfs\\$' "
+ "| sed 's/dfs\\$//g'", "w");
+ for (size_t I = 0; I < NumFuncs; I++) {
+ uintptr_t PC = FuncsBeg[I * 2];
+ void *const Buf[1] = {(void*)PC};
+ backtrace_symbols_fd(Buf, 1, fileno(Pipe));
+ }
+ pclose(Pipe);
+ return 0;
+}
+
+extern "C"
+void SetBytesForLabel(dfsan_label L, char *Bytes) {
+ if (LabelSeen[L])
+ return;
+ LabelSeen[L] = true;
+ assert(L);
+ if (L <= InputLen + 1) {
+ Bytes[L - 1] = '1';
+ } else {
+ auto *DLI = dfsan_get_label_info(L);
+ SetBytesForLabel(DLI->l1, Bytes);
+ SetBytesForLabel(DLI->l2, Bytes);
+ }
+}
+
+static char *GetPrintableStringForLabel(dfsan_label L) {
+ memset(PrintableStringForLabel, '0', InputLen + 1);
+ PrintableStringForLabel[InputLen + 1] = 0;
+ memset(LabelSeen, 0, sizeof(LabelSeen));
+ SetBytesForLabel(L, PrintableStringForLabel);
+ return PrintableStringForLabel;
+}
+
+static void PrintDataFlow(FILE *Out) {
+ for (size_t I = 0; I < NumFuncs; I++)
+ if (FuncLabels[I])
+ fprintf(Out, "F%zd %s\n", I, GetPrintableStringForLabel(FuncLabels[I]));
+}
+
+int main(int argc, char **argv) {
+ if (LLVMFuzzerInitialize)
+ LLVMFuzzerInitialize(&argc, &argv);
+ if (argc == 1)
+ return PrintFunctions();
+ assert(argc == 4 || argc == 5);
+ size_t Beg = atoi(argv[1]);
+ size_t End = atoi(argv[2]);
+ assert(Beg < End);
+
+ const char *Input = argv[3];
+ fprintf(stderr, "INFO: reading '%s'\n", Input);
+ FILE *In = fopen(Input, "r");
+ assert(In);
+ fseek(In, 0, SEEK_END);
+ InputLen = ftell(In);
+ fseek(In, 0, SEEK_SET);
+ unsigned char *Buf = (unsigned char*)malloc(InputLen);
+ size_t NumBytesRead = fread(Buf, 1, InputLen, In);
+ assert(NumBytesRead == InputLen);
+ PrintableStringForLabel = (char*)malloc(InputLen + 2);
+ fclose(In);
+
+ fprintf(stderr, "INFO: running '%s'\n", Input);
+ for (size_t I = 1; I <= InputLen; I++) {
+ dfsan_label L = dfsan_create_label("", nullptr);
+ assert(L == I);
+ size_t Idx = I - 1;
+ if (Idx >= Beg && Idx < End)
+ dfsan_set_label(L, Buf + Idx, 1);
+ }
+ dfsan_label SizeL = dfsan_create_label("", nullptr);
+ assert(SizeL == InputLen + 1);
+ dfsan_set_label(SizeL, &InputLen, sizeof(InputLen));
+
+ LLVMFuzzerTestOneInput(Buf, InputLen);
+ free(Buf);
+
+ bool OutIsStdout = argc == 4;
+ fprintf(stderr, "INFO: writing dataflow to %s\n",
+ OutIsStdout ? "<stdout>" : argv[4]);
+ FILE *Out = OutIsStdout ? stdout : fopen(argv[4], "w");
+ PrintDataFlow(Out);
+ if (!OutIsStdout) fclose(Out);
+}
+
+extern "C" {
+
+void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
+ uint32_t *stop) {
+ assert(NumFuncs == 0 && "This tool does not support DSOs");
+ assert(start < stop && "The code is not instrumented for coverage");
+ if (start == stop || *start) return; // Initialize only once.
+ for (uint32_t *x = start; x < stop; x++)
+ *x = ++NumFuncs; // The first index is 1.
+ FuncLabels = (dfsan_label*)calloc(NumFuncs, sizeof(dfsan_label));
+ fprintf(stderr, "INFO: %zd instrumented function(s) observed\n", NumFuncs);
+}
+
+void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
+ const uintptr_t *pcs_end) {
+ assert(NumFuncs == (pcs_end - pcs_beg) / 2);
+ FuncsBeg = pcs_beg;
+}
+
+void __sanitizer_cov_trace_pc_indir(uint64_t x){} // unused.
+
+void __sanitizer_cov_trace_pc_guard(uint32_t *guard){
+ uint32_t FuncNum = *guard - 1; // Guards start from 1.
+ assert(FuncNum < NumFuncs);
+ CurrentFunc = FuncNum;
+}
+
+void __dfsw___sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases,
+ dfsan_label L1, dfsan_label UnusedL) {
+ assert(CurrentFunc < NumFuncs);
+ FuncLabels[CurrentFunc] = dfsan_union(FuncLabels[CurrentFunc], L1);
+}
+
+#define HOOK(Name, Type) \
+ void Name(Type Arg1, Type Arg2, dfsan_label L1, dfsan_label L2) { \
+ assert(CurrentFunc < NumFuncs); \
+ FuncLabels[CurrentFunc] = \
+ dfsan_union(FuncLabels[CurrentFunc], dfsan_union(L1, L2)); \
+ }
+
+HOOK(__dfsw___sanitizer_cov_trace_const_cmp1, uint8_t)
+HOOK(__dfsw___sanitizer_cov_trace_const_cmp2, uint16_t)
+HOOK(__dfsw___sanitizer_cov_trace_const_cmp4, uint32_t)
+HOOK(__dfsw___sanitizer_cov_trace_const_cmp8, uint64_t)
+HOOK(__dfsw___sanitizer_cov_trace_cmp1, uint8_t)
+HOOK(__dfsw___sanitizer_cov_trace_cmp2, uint16_t)
+HOOK(__dfsw___sanitizer_cov_trace_cmp4, uint32_t)
+HOOK(__dfsw___sanitizer_cov_trace_cmp8, uint64_t)
+
+} // extern "C"
diff --git a/lib/fuzzer/scripts/collect_data_flow.py b/lib/fuzzer/scripts/collect_data_flow.py
new file mode 100755
index 000000000000..3edff66bb9d1
--- /dev/null
+++ b/lib/fuzzer/scripts/collect_data_flow.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+#===- lib/fuzzer/scripts/collect_data_flow.py ------------------------------===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+# Runs the data-flow tracer several times on the same input in order to collect
+# the complete trace for all input bytes (running it on all bytes at once
+# may fail if DFSan runs out of labels).
+# Usage:
+#
+# # Collect dataflow for one input, store it in OUTPUT (default is stdout)
+# collect_data_flow.py BINARY INPUT [OUTPUT]
+#
+# # Collect dataflow for all inputs in CORPUS_DIR, store them in OUTPUT_DIR
+# collect_data_flow.py BINARY CORPUS_DIR OUTPUT_DIR
+#===------------------------------------------------------------------------===#
+import atexit
+import hashlib
+import sys
+import os
+import subprocess
+import tempfile
+import shutil
+
+tmpdir = ""
+
+def cleanup(d):
+ print("removing: %s" % d)
+ shutil.rmtree(d)
+
+def collect_dataflow_for_corpus(self, exe, corpus_dir, output_dir):
+ print("Collecting dataflow for corpus: %s output_dir: %s" % (corpus_dir,
+ output_dir))
+ assert not os.path.exists(output_dir)
+ os.mkdir(output_dir)
+ for root, dirs, files in os.walk(corpus_dir):
+ for f in files:
+ path = os.path.join(root, f)
+ sha1 = hashlib.sha1(open(path).read()).hexdigest()
+ output = os.path.join(output_dir, sha1)
+ subprocess.call([self, exe, path, output])
+ functions_txt = open(os.path.join(output_dir, "functions.txt"), "w")
+ subprocess.call([exe], stdout=functions_txt)
+
+
+def main(argv):
+ exe = argv[1]
+ inp = argv[2]
+ if os.path.isdir(inp):
+ return collect_dataflow_for_corpus(argv[0], exe, inp, argv[3])
+ size = os.path.getsize(inp)
+ q = [[0, size]]
+ tmpdir = tempfile.mkdtemp(prefix="libfuzzer-tmp-")
+ atexit.register(cleanup, tmpdir)
+ print "tmpdir: ", tmpdir
+ outputs = []
+ while len(q):
+ r = q.pop()
+ print "******* Trying: ", r
+ tmpfile = os.path.join(tmpdir, str(r[0]) + "-" + str(r[1]))
+ ret = subprocess.call([exe, str(r[0]), str(r[1]), inp, tmpfile])
+ if ret and r[1] - r[0] >= 2:
+ q.append([r[0], (r[1] + r[0]) / 2])
+ q.append([(r[1] + r[0]) / 2, r[1]])
+ else:
+ outputs.append(tmpfile)
+ print "******* Success: ", r
+ f = sys.stdout
+ if len(argv) >= 4:
+ f = open(argv[3], "w")
+ merge = os.path.join(os.path.dirname(argv[0]), "merge_data_flow.py")
+ subprocess.call([merge] + outputs, stdout=f)
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/lib/fuzzer/scripts/merge_data_flow.py b/lib/fuzzer/scripts/merge_data_flow.py
new file mode 100755
index 000000000000..d2f5081e7b8c
--- /dev/null
+++ b/lib/fuzzer/scripts/merge_data_flow.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+#===- lib/fuzzer/scripts/merge_data_flow.py ------------------------------===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+# Merge several data flow traces into one.
+# Usage:
+# merge_data_flow.py trace1 trace2 ... > result
+#===------------------------------------------------------------------------===#
+import sys
+import fileinput
+from array import array
+
+def Merge(a, b):
+ res = array('b')
+ for i in range(0, len(a)):
+ res.append(ord('1' if a[i] == '1' or b[i] == '1' else '0'))
+ return res.tostring()
+
+def main(argv):
+ D = {}
+ for line in fileinput.input():
+ [F,BV] = line.strip().split(' ')
+ if F in D:
+ D[F] = Merge(D[F], BV)
+ else:
+ D[F] = BV;
+ for F in D.keys():
+ print("%s %s" % (F, D[F]))
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/lib/fuzzer/scripts/unbalanced_allocs.py b/lib/fuzzer/scripts/unbalanced_allocs.py
index a4ce187679d7..74478ad55af0 100755
--- a/lib/fuzzer/scripts/unbalanced_allocs.py
+++ b/lib/fuzzer/scripts/unbalanced_allocs.py
@@ -24,9 +24,9 @@ def PrintStack(line, stack):
global _skip
if _skip > 0:
return
- print 'Unbalanced ' + line.rstrip();
+ print('Unbalanced ' + line.rstrip());
for l in stack:
- print l.rstrip()
+ print(l.rstrip())
def ProcessStack(line, f):
stack = []
@@ -63,15 +63,15 @@ def ProcessRun(line, f):
return ProcessMalloc(line, f, {})
allocs = {}
- print line.rstrip()
+ print(line.rstrip())
line = f.readline()
while line:
if line.startswith('MallocFreeTracer: STOP'):
global _skip
_skip = _skip - 1
- for _, (l, s) in allocs.iteritems():
+ for _, (l, s) in allocs.items():
PrintStack(l, s)
- print line.rstrip()
+ print(line.rstrip())
return f.readline()
line = ProcessMalloc(line, f, allocs)
return line
diff --git a/lib/fuzzer/tests/CMakeLists.txt b/lib/fuzzer/tests/CMakeLists.txt
index dac8773597e8..ed5807168301 100644
--- a/lib/fuzzer/tests/CMakeLists.txt
+++ b/lib/fuzzer/tests/CMakeLists.txt
@@ -3,22 +3,32 @@ set(LIBFUZZER_UNITTEST_CFLAGS
${COMPILER_RT_GTEST_CFLAGS}
-I${COMPILER_RT_SOURCE_DIR}/lib/fuzzer
-fno-rtti
- -Werror
-O2)
+if (APPLE)
+ set(FUZZER_SUPPORTED_OS osx)
+endif()
+
add_custom_target(FuzzerUnitTests)
set_target_properties(FuzzerUnitTests PROPERTIES FOLDER "Compiler-RT Tests")
set(LIBFUZZER_UNITTEST_LINK_FLAGS ${COMPILER_RT_UNITTEST_LINK_FLAGS})
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS --driver-mode=g++)
-if(APPLE)
- list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lc++)
+if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+ list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lc++ -lpthread)
else()
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lstdc++ -lpthread)
endif()
-foreach(arch ${FUZZER_SUPPORTED_ARCH})
+if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND COMPILER_RT_LIBCXX_PATH)
+ list(APPEND LIBFUZZER_UNITTEST_CFLAGS -nostdinc++ -D_LIBCPP_ABI_VERSION=Fuzzer)
+endif()
+
+if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST FUZZER_SUPPORTED_ARCH)
+ # libFuzzer unit tests are only run on the host machine.
+ set(arch ${COMPILER_RT_DEFAULT_TARGET_ARCH})
+
set(LIBFUZZER_TEST_RUNTIME RTFuzzerTest.${arch})
if(APPLE)
set(LIBFUZZER_TEST_RUNTIME_OBJECTS
@@ -33,14 +43,20 @@ foreach(arch ${FUZZER_SUPPORTED_ARCH})
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
FOLDER "Compiler-RT Runtime tests")
+ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND COMPILER_RT_LIBCXX_PATH)
+ set(LIBFUZZER_TEST_RUNTIME_DEPS libcxx_fuzzer_${arch}-build)
+ set(LIBFUZZER_TEST_RUNTIME_CFLAGS -isystem ${COMPILER_RT_LIBCXX_PATH}/include)
+ set(LIBFUZZER_TEST_RUNTIME_LINK_FLAGS ${LIBCXX_${arch}_PREFIX}/lib/libc++.a)
+ endif()
+
set(FuzzerTestObjects)
generate_compiler_rt_tests(FuzzerTestObjects
FuzzerUnitTests "Fuzzer-${arch}-Test" ${arch}
SOURCES FuzzerUnittest.cpp ${COMPILER_RT_GTEST_SOURCE}
RUNTIME ${LIBFUZZER_TEST_RUNTIME}
- DEPS gtest
- CFLAGS ${LIBFUZZER_UNITTEST_CFLAGS}
- LINK_FLAGS ${LIBFUZZER_UNITTEST_LINK_FLAGS})
+ DEPS gtest ${LIBFUZZER_TEST_RUNTIME_DEPS}
+ CFLAGS ${LIBFUZZER_UNITTEST_CFLAGS} ${LIBFUZZER_TEST_RUNTIME_CFLAGS}
+ LINK_FLAGS ${LIBFUZZER_UNITTEST_LINK_FLAGS} ${LIBFUZZER_TEST_RUNTIME_LINK_FLAGS})
set_target_properties(FuzzerUnitTests PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
-endforeach()
+endif()
diff --git a/lib/fuzzer/tests/FuzzerUnittest.cpp b/lib/fuzzer/tests/FuzzerUnittest.cpp
index 97ec3b4bb6af..e3b06702603d 100644
--- a/lib/fuzzer/tests/FuzzerUnittest.cpp
+++ b/lib/fuzzer/tests/FuzzerUnittest.cpp
@@ -28,6 +28,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
abort();
}
+TEST(Fuzzer, Basename) {
+ EXPECT_EQ(Basename("foo/bar"), "bar");
+ EXPECT_EQ(Basename("bar"), "bar");
+ EXPECT_EQ(Basename("/bar"), "bar");
+ EXPECT_EQ(Basename("foo/x"), "x");
+ EXPECT_EQ(Basename("foo/"), "");
+}
+
TEST(Fuzzer, CrossOver) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
fuzzer::EF = t.get();
@@ -328,7 +336,7 @@ void TestShuffleBytes(Mutator M, int NumIter) {
}
TEST(FuzzerMutate, ShuffleBytes1) {
- TestShuffleBytes(&MutationDispatcher::Mutate_ShuffleBytes, 1 << 16);
+ TestShuffleBytes(&MutationDispatcher::Mutate_ShuffleBytes, 1 << 17);
}
TEST(FuzzerMutate, ShuffleBytes2) {
TestShuffleBytes(&MutationDispatcher::Mutate, 1 << 20);
@@ -381,6 +389,21 @@ TEST(FuzzerMutate, CopyPart1) {
TEST(FuzzerMutate, CopyPart2) {
TestCopyPart(&MutationDispatcher::Mutate, 1 << 13);
}
+TEST(FuzzerMutate, CopyPartNoInsertAtMaxSize) {
+ // This (non exhaustively) tests if `Mutate_CopyPart` tries to perform an
+ // insert on an input of size `MaxSize`. Performing an insert in this case
+ // will lead to the mutation failing.
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ Random Rand(0);
+ std::unique_ptr<MutationDispatcher> MD(new MutationDispatcher(Rand, {}));
+ uint8_t Data[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11, 0x22};
+ size_t MaxSize = sizeof(Data);
+ for (int count = 0; count < (1 << 18); ++count) {
+ size_t NewSize = MD->Mutate_CopyPart(Data, MaxSize, MaxSize);
+ ASSERT_EQ(NewSize, MaxSize);
+ }
+}
void TestAddWordFromDictionary(Mutator M, int NumIter) {
std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
@@ -559,12 +582,14 @@ TEST(FuzzerUtil, Base64) {
}
TEST(Corpus, Distribution) {
+ DataFlowTrace DFT;
Random Rand(0);
std::unique_ptr<InputCorpus> C(new InputCorpus(""));
size_t N = 10;
size_t TriesPerUnit = 1<<16;
for (size_t i = 0; i < N; i++)
- C->AddToCorpus(Unit{ static_cast<uint8_t>(i) }, 1, false, {});
+ C->AddToCorpus(Unit{static_cast<uint8_t>(i)}, 1, false, false, {}, DFT,
+ nullptr);
Vector<size_t> Hist(N);
for (size_t i = 0; i < N * TriesPerUnit; i++) {