diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:06:48 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 11:06:48 +0000 |
commit | 93c1b73a09a52d4a265f683bf1954b08bb430049 (patch) | |
tree | 5543464d74945196cc890e9d9099e5d0660df7eb /lib/fuzzer | |
parent | 0d8e7490d6e8a13a8f0977d9b7771803b9f64ea0 (diff) | |
download | src-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')
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++) { |