diff options
Diffstat (limited to 'lib/Fuzzer')
97 files changed, 1818 insertions, 813 deletions
diff --git a/lib/Fuzzer/CMakeLists.txt b/lib/Fuzzer/CMakeLists.txt index 70bd017bae6b..59cef04cdece 100644 --- a/lib/Fuzzer/CMakeLists.txt +++ b/lib/Fuzzer/CMakeLists.txt @@ -12,8 +12,9 @@ if( LLVM_USE_SANITIZE_COVERAGE ) FuzzerCrossOver.cpp FuzzerDriver.cpp FuzzerExtFunctionsDlsym.cpp + FuzzerExtFunctionsDlsymWin.cpp FuzzerExtFunctionsWeak.cpp - FuzzerExtFunctionsWeakAlias.cpp + FuzzerExtraCounters.cpp FuzzerIO.cpp FuzzerIOPosix.cpp FuzzerIOWindows.cpp @@ -21,6 +22,8 @@ if( LLVM_USE_SANITIZE_COVERAGE ) FuzzerMerge.cpp FuzzerMutate.cpp FuzzerSHA1.cpp + FuzzerShmemPosix.cpp + FuzzerShmemWindows.cpp FuzzerTracePC.cpp FuzzerTraceState.cpp FuzzerUtil.cpp @@ -32,12 +35,12 @@ if( LLVM_USE_SANITIZE_COVERAGE ) add_library(LLVMFuzzerNoMain STATIC $<TARGET_OBJECTS:LLVMFuzzerNoMainObjects> ) - target_link_libraries(LLVMFuzzerNoMain ${PTHREAD_LIB}) + target_link_libraries(LLVMFuzzerNoMain ${LLVM_PTHREAD_LIB}) add_library(LLVMFuzzer STATIC FuzzerMain.cpp $<TARGET_OBJECTS:LLVMFuzzerNoMainObjects> ) - target_link_libraries(LLVMFuzzer ${PTHREAD_LIB}) + target_link_libraries(LLVMFuzzer ${LLVM_PTHREAD_LIB}) if( LLVM_INCLUDE_TESTS ) add_subdirectory(test) diff --git a/lib/Fuzzer/FuzzerCorpus.h b/lib/Fuzzer/FuzzerCorpus.h index 468d5e5ddc70..0f0573994a03 100644 --- a/lib/Fuzzer/FuzzerCorpus.h +++ b/lib/Fuzzer/FuzzerCorpus.h @@ -37,8 +37,8 @@ struct InputInfo { }; class InputCorpus { + static const size_t kFeatureSetSize = 1 << 21; public: - static const size_t kFeatureSetSize = 1 << 16; InputCorpus(const std::string &OutputCorpus) : OutputCorpus(OutputCorpus) { memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature)); memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature)); @@ -68,7 +68,8 @@ class InputCorpus { } 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 = false) { + void AddToCorpus(const Unit &U, size_t NumFeatures, + bool MayDeleteFile = false) { assert(!U.empty()); uint8_t Hash[kSHA1NumBytes]; if (FeatureDebug) @@ -82,7 +83,7 @@ class InputCorpus { II.MayDeleteFile = MayDeleteFile; memcpy(II.Sha1, Hash, kSHA1NumBytes); UpdateCorpusDistribution(); - ValidateFeatureSet(); + // ValidateFeatureSet(); } bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); } @@ -97,7 +98,7 @@ class InputCorpus { // Hypothesis: units added to the corpus last are more likely to be // interesting. This function gives more weight to the more recent units. size_t ChooseUnitIdxToMutate(Random &Rand) { - size_t Idx = static_cast<size_t>(CorpusDistribution(Rand.Get_mt19937())); + size_t Idx = static_cast<size_t>(CorpusDistribution(Rand)); assert(Idx < Inputs.size()); return Idx; } @@ -132,7 +133,7 @@ class InputCorpus { Printf("EVICTED %zd\n", Idx); } - bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) { + void AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) { assert(NewSize); Idx = Idx % kFeatureSetSize; uint32_t OldSize = GetFeature(Idx); @@ -144,23 +145,20 @@ class InputCorpus { II.NumFeatures--; if (II.NumFeatures == 0) DeleteInput(OldIdx); + } else { + NumAddedFeatures++; } + NumUpdatedFeatures++; if (FeatureDebug) Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize); SmallestElementPerFeature[Idx] = Inputs.size(); InputSizesPerFeature[Idx] = NewSize; CountingFeatures = true; - return true; } - return false; } - size_t NumFeatures() const { - size_t Res = 0; - for (size_t i = 0; i < kFeatureSetSize; i++) - Res += GetFeature(i) != 0; - return Res; - } + size_t NumFeatures() const { return NumAddedFeatures; } + size_t NumFeatureUpdates() const { return NumUpdatedFeatures; } void ResetFeatureSet() { assert(Inputs.empty()); @@ -213,6 +211,8 @@ private: std::vector<InputInfo*> Inputs; bool CountingFeatures = false; + size_t NumAddedFeatures = 0; + size_t NumUpdatedFeatures = 0; uint32_t InputSizesPerFeature[kFeatureSetSize]; uint32_t SmallestElementPerFeature[kFeatureSetSize]; diff --git a/lib/Fuzzer/FuzzerDefs.h b/lib/Fuzzer/FuzzerDefs.h index 0f5b8a7cf211..bd1827508002 100644 --- a/lib/Fuzzer/FuzzerDefs.h +++ b/lib/Fuzzer/FuzzerDefs.h @@ -47,8 +47,30 @@ #ifdef __clang__ // avoid gcc warning. # define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +# define ALWAYS_INLINE __attribute__((always_inline)) #else # define ATTRIBUTE_NO_SANITIZE_MEMORY +# define ALWAYS_INLINE +#endif // __clang__ + +#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) + +#if defined(__has_feature) +# if __has_feature(address_sanitizer) +# define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS +# elif __has_feature(memory_sanitizer) +# define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_MEMORY +# else +# define ATTRIBUTE_NO_SANITIZE_ALL +# endif +#else +# define ATTRIBUTE_NO_SANITIZE_ALL +#endif + +#if LIBFUZZER_WINDOWS +#define ATTRIBUTE_INTERFACE __declspec(dllexport) +#else +#define ATTRIBUTE_INTERFACE __attribute__((visibility("default"))) #endif namespace fuzzer { @@ -74,9 +96,10 @@ typedef int (*UserCallback)(const uint8_t *Data, size_t Size); int FuzzerDriver(int *argc, char ***argv, UserCallback Callback); -struct ScopedDoingMyOwnMemmem { - ScopedDoingMyOwnMemmem(); - ~ScopedDoingMyOwnMemmem(); +struct ScopedDoingMyOwnMemOrStr { + ScopedDoingMyOwnMemOrStr() { DoingMyOwnMemOrStr++; } + ~ScopedDoingMyOwnMemOrStr() { DoingMyOwnMemOrStr--; } + static int DoingMyOwnMemOrStr; }; inline uint8_t Bswap(uint8_t x) { return x; } @@ -84,6 +107,10 @@ inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); } inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); } inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); } +uint8_t *ExtraCountersBegin(); +uint8_t *ExtraCountersEnd(); +void ClearExtraCounters(); + } // namespace fuzzer #endif // LLVM_FUZZER_DEFS_H diff --git a/lib/Fuzzer/FuzzerDictionary.h b/lib/Fuzzer/FuzzerDictionary.h index eba0eabb6838..84cee87b8971 100644 --- a/lib/Fuzzer/FuzzerDictionary.h +++ b/lib/Fuzzer/FuzzerDictionary.h @@ -20,8 +20,9 @@ namespace fuzzer { // A simple POD sized array of bytes. -template <size_t kMaxSize> class FixedWord { +template <size_t kMaxSizeT> class FixedWord { public: + static const size_t kMaxSize = kMaxSizeT; FixedWord() {} FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); } @@ -32,10 +33,12 @@ 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; @@ -50,7 +53,7 @@ private: uint8_t Data[kMaxSize]; }; -typedef FixedWord<27> Word; // 28 bytes. +typedef FixedWord<64> Word; class DictionaryEntry { public: diff --git a/lib/Fuzzer/FuzzerDriver.cpp b/lib/Fuzzer/FuzzerDriver.cpp index 2bbcb25275e4..0fb83ca64de6 100644 --- a/lib/Fuzzer/FuzzerDriver.cpp +++ b/lib/Fuzzer/FuzzerDriver.cpp @@ -15,6 +15,7 @@ #include "FuzzerIO.h" #include "FuzzerMutate.h" #include "FuzzerRandom.h" +#include "FuzzerShmem.h" #include "FuzzerTracePC.h" #include <algorithm> #include <atomic> @@ -277,7 +278,19 @@ static bool AllInputsAreFiles() { return true; } -int MinimizeCrashInput(const std::vector<std::string> &Args) { +static std::string GetDedupTokenFromFile(const std::string &Path) { + auto S = FileToString(Path); + auto Beg = S.find("DEDUP_TOKEN:"); + if (Beg == std::string::npos) + return ""; + auto End = S.find('\n', Beg); + if (End == std::string::npos) + return ""; + return S.substr(Beg, End - Beg); +} + +int MinimizeCrashInput(const std::vector<std::string> &Args, + const FuzzingOptions &Options) { if (Inputs->size() != 1) { Printf("ERROR: -minimize_crash should be given one input file\n"); exit(1); @@ -294,19 +307,18 @@ int MinimizeCrashInput(const std::vector<std::string> &Args) { "INFO: defaulting to -max_total_time=600\n"); BaseCmd += " -max_total_time=600"; } - // BaseCmd += " > /dev/null 2>&1 "; + + auto LogFilePath = DirPlusFile( + TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt"); + auto LogFileRedirect = " > " + LogFilePath + " 2>&1 "; std::string CurrentFilePath = InputFilePath; while (true) { Unit U = FileToVector(CurrentFilePath); - if (U.size() < 2) { - Printf("CRASH_MIN: '%s' is small enough\n", CurrentFilePath.c_str()); - return 0; - } Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n", CurrentFilePath.c_str(), U.size()); - auto Cmd = BaseCmd + " " + CurrentFilePath; + auto Cmd = BaseCmd + " " + CurrentFilePath + LogFileRedirect; Printf("CRASH_MIN: executing: %s\n", Cmd.c_str()); int ExitCode = ExecuteCommand(Cmd); @@ -317,12 +329,19 @@ int MinimizeCrashInput(const std::vector<std::string> &Args) { Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize " "it further\n", CurrentFilePath.c_str(), U.size()); - - std::string ArtifactPath = "minimized-from-" + Hash(U); + auto DedupToken1 = GetDedupTokenFromFile(LogFilePath); + if (!DedupToken1.empty()) + Printf("CRASH_MIN: DedupToken1: %s\n", DedupToken1.c_str()); + + std::string ArtifactPath = + Flags.exact_artifact_path + ? Flags.exact_artifact_path + : Options.ArtifactPrefix + "minimized-from-" + Hash(U); Cmd += " -minimize_crash_internal_step=1 -exact_artifact_path=" + ArtifactPath; Printf("CRASH_MIN: executing: %s\n", Cmd.c_str()); ExitCode = ExecuteCommand(Cmd); + CopyFileToErr(LogFilePath); if (ExitCode == 0) { if (Flags.exact_artifact_path) { CurrentFilePath = Flags.exact_artifact_path; @@ -330,11 +349,26 @@ int MinimizeCrashInput(const std::vector<std::string> &Args) { } Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n", CurrentFilePath.c_str(), U.size()); - return 0; + break; } + auto DedupToken2 = GetDedupTokenFromFile(LogFilePath); + if (!DedupToken2.empty()) + Printf("CRASH_MIN: DedupToken2: %s\n", DedupToken2.c_str()); + + if (DedupToken1 != DedupToken2) { + if (Flags.exact_artifact_path) { + CurrentFilePath = Flags.exact_artifact_path; + WriteToFile(U, CurrentFilePath); + } + Printf("CRASH_MIN: mismatch in dedup tokens" + " (looks like a different bug). Won't minimize further\n"); + break; + } + CurrentFilePath = ArtifactPath; - Printf("\n\n\n\n\n\n*********************************\n"); + Printf("*********************************\n"); } + RemoveFile(LogFilePath); return 0; } @@ -342,8 +376,11 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { assert(Inputs->size() == 1); std::string InputFilePath = Inputs->at(0); Unit U = FileToVector(InputFilePath); - assert(U.size() > 2); Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size()); + if (U.size() < 2) { + Printf("INFO: The input is small enough, exiting\n"); + exit(0); + } Corpus->AddToCorpus(U, 0); F->SetMaxInputLen(U.size()); F->SetMaxMutationLen(U.size() - 1); @@ -353,24 +390,94 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { return 0; } +int AnalyzeDictionary(Fuzzer *F, const std::vector<Unit>& Dict, + UnitVector& Corpus) { + Printf("Started dictionary minimization (up to %d tests)\n", + Dict.size() * Corpus.size() * 2); + + // Scores and usage count for each dictionary unit. + std::vector<int> Scores(Dict.size()); + std::vector<int> Usages(Dict.size()); + + std::vector<size_t> InitialFeatures; + std::vector<size_t> ModifiedFeatures; + for (auto &C : Corpus) { + // Get coverage for the testcase without modifications. + F->ExecuteCallback(C.data(), C.size()); + InitialFeatures.clear(); + TPC.CollectFeatures([&](size_t Feature) -> bool { + InitialFeatures.push_back(Feature); + return true; + }); + + for (size_t i = 0; i < Dict.size(); ++i) { + auto Data = C; + auto StartPos = std::search(Data.begin(), Data.end(), + Dict[i].begin(), Dict[i].end()); + // Skip dictionary unit, if the testcase does not contain it. + if (StartPos == Data.end()) + continue; + + ++Usages[i]; + while (StartPos != Data.end()) { + // Replace all occurrences of dictionary unit in the testcase. + auto EndPos = StartPos + Dict[i].size(); + for (auto It = StartPos; It != EndPos; ++It) + *It ^= 0xFF; + + StartPos = std::search(EndPos, Data.end(), + Dict[i].begin(), Dict[i].end()); + } + + // Get coverage for testcase with masked occurrences of dictionary unit. + F->ExecuteCallback(Data.data(), Data.size()); + ModifiedFeatures.clear(); + TPC.CollectFeatures([&](size_t Feature) -> bool { + ModifiedFeatures.push_back(Feature); + return true; + }); + + if (InitialFeatures == ModifiedFeatures) + --Scores[i]; + else + Scores[i] += 2; + } + } + + Printf("###### Useless dictionary elements. ######\n"); + for (size_t i = 0; i < Dict.size(); ++i) { + // Dictionary units with positive score are treated as useful ones. + if (Scores[i] > 0) + continue; + + Printf("\""); + PrintASCII(Dict[i].data(), Dict[i].size(), "\""); + Printf(" # Score: %d, Used: %d\n", Scores[i], Usages[i]); + } + Printf("###### End of useless dictionary elements. ######\n"); + return 0; +} + int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { using namespace fuzzer; assert(argc && argv && "Argument pointers cannot be nullptr"); + std::string Argv0((*argv)[0]); EF = new ExternalFunctions(); if (EF->LLVMFuzzerInitialize) EF->LLVMFuzzerInitialize(argc, argv); const std::vector<std::string> Args(*argv, *argv + *argc); assert(!Args.empty()); ProgName = new std::string(Args[0]); + if (Argv0 != *ProgName) { + Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n"); + exit(1); + } ParseFlags(Args); if (Flags.help) { PrintHelp(); return 0; } - if (Flags.minimize_crash) - return MinimizeCrashInput(Args); - if (Flags.close_fd_mask & 2) DupAndCloseStderr(); if (Flags.close_fd_mask & 1) @@ -401,7 +508,6 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.MutateDepth = Flags.mutate_depth; Options.UseCounters = Flags.use_counters; Options.UseIndirCalls = Flags.use_indir_calls; - Options.UseMemcmp = Flags.use_memcmp; Options.UseMemmem = Flags.use_memmem; Options.UseCmp = Flags.use_cmp; Options.UseValueProfile = Flags.use_value_profile; @@ -471,9 +577,37 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.HandleXfsz = Flags.handle_xfsz; SetSignalHandler(Options); + if (Flags.minimize_crash) + return MinimizeCrashInput(Args, Options); + if (Flags.minimize_crash_internal_step) return MinimizeCrashInputInternalStep(F, Corpus); + if (auto Name = Flags.run_equivalence_server) { + SMR.Destroy(Name); + if (!SMR.Create(Name)) { + Printf("ERROR: can't create shared memory region\n"); + return 1; + } + Printf("INFO: EQUIVALENCE SERVER UP\n"); + while (true) { + SMR.WaitClient(); + size_t Size = SMR.ReadByteArraySize(); + SMR.WriteByteArray(nullptr, 0); + F->RunOne(SMR.GetByteArray(), Size); + SMR.PostServer(); + } + return 0; + } + + if (auto Name = Flags.use_equivalence_server) { + if (!SMR.Open(Name)) { + Printf("ERROR: can't open shared memory region\n"); + return 1; + } + Printf("INFO: EQUIVALENCE CLIENT UP\n"); + } + if (DoPlainRun) { Options.SaveArtifacts = false; int Runs = std::max(1, Flags.runs); @@ -499,14 +633,12 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { if (Flags.merge) { if (Options.MaxLen == 0) F->SetMaxInputLen(kMaxSaneLen); - if (TPC.UsingTracePcGuard()) { - if (Flags.merge_control_file) - F->CrashResistantMergeInternalStep(Flags.merge_control_file); - else - F->CrashResistantMerge(Args, *Inputs); - } else { - F->Merge(*Inputs); - } + if (Flags.merge_control_file) + F->CrashResistantMergeInternalStep(Flags.merge_control_file); + else + F->CrashResistantMerge(Args, *Inputs, + Flags.load_coverage_summary, + Flags.save_coverage_summary); exit(0); } @@ -519,6 +651,19 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { TemporaryMaxLen, /*ExitOnError=*/false); } + if (Flags.analyze_dict) { + if (Dictionary.empty() || Inputs->empty()) { + Printf("ERROR: can't analyze dict without dict and corpus provided\n"); + return 1; + } + if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) { + Printf("Dictionary analysis failed\n"); + exit(1); + } + Printf("Dictionary analysis suceeded\n"); + exit(0); + } + if (Options.MaxLen == 0) { size_t MaxLen = 0; for (auto &U : InitialCorpus) @@ -536,7 +681,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { F->Loop(); if (Flags.verbosity) - Printf("Done %d runs in %zd second(s)\n", F->getTotalNumberOfRuns(), + Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(), F->secondsSinceProcessStartUp()); F->PrintFinalStats(); diff --git a/lib/Fuzzer/FuzzerExtFunctions.def b/lib/Fuzzer/FuzzerExtFunctions.def index 61c72e4a209e..3bc5302c31c6 100644 --- a/lib/Fuzzer/FuzzerExtFunctions.def +++ b/lib/Fuzzer/FuzzerExtFunctions.def @@ -29,22 +29,18 @@ 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_get_number_of_counters, size_t, (), false); EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int, (void (*malloc_hook)(const volatile void *, size_t), void (*free_hook)(const volatile void *)), false); -EXT_FUNC(__sanitizer_get_total_unique_caller_callee_pairs, size_t, (), false); -EXT_FUNC(__sanitizer_get_total_unique_coverage, size_t, (), true); -EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t), false); +EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t, size_t), false); EXT_FUNC(__sanitizer_print_stack_trace, void, (), true); EXT_FUNC(__sanitizer_symbolize_pc, void, (void *, const char *fmt, char *out_buf, size_t out_buf_size), false); EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int, (void *pc, char *module_path, size_t module_path_len,void **pc_offset), false); -EXT_FUNC(__sanitizer_reset_coverage, void, (), true); EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true); EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false); -EXT_FUNC(__sanitizer_update_counter_bitset_and_clear_counters, uintptr_t, - (uint8_t*), false); +EXT_FUNC(__sanitizer_dump_coverage, void, (const uintptr_t *, uintptr_t), + false); diff --git a/lib/Fuzzer/FuzzerExtFunctionsDlsymWin.cpp b/lib/Fuzzer/FuzzerExtFunctionsDlsymWin.cpp new file mode 100644 index 000000000000..77521698c80a --- /dev/null +++ b/lib/Fuzzer/FuzzerExtFunctionsDlsymWin.cpp @@ -0,0 +1,60 @@ +//===- FuzzerExtFunctionsDlsymWin.cpp - Interface to external functions ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation using dynamic loading for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_WINDOWS + +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "Windows.h" +#include "Psapi.h" + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { + HMODULE Modules[1024]; + DWORD BytesNeeded; + HANDLE CurrentProcess = GetCurrentProcess(); + + if (!EnumProcessModules(CurrentProcess, Modules, sizeof(Modules), + &BytesNeeded)) { + Printf("EnumProcessModules failed (error: %d).\n", GetLastError()); + exit(1); + } + + if (sizeof(Modules) < BytesNeeded) { + Printf("Error: the array is not big enough to hold all loaded modules.\n"); + exit(1); + } + + for (size_t i = 0; i < (BytesNeeded / sizeof(HMODULE)); i++) + { + FARPROC Fn; +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + if (this->NAME == nullptr) { \ + Fn = GetProcAddress(Modules[i], #NAME); \ + if (Fn == nullptr) \ + Fn = GetProcAddress(Modules[i], #NAME "__dll"); \ + this->NAME = (decltype(ExternalFunctions::NAME)) Fn; \ + } +#include "FuzzerExtFunctions.def" +#undef EXT_FUNC + } + +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + if (this->NAME == nullptr && WARN) \ + Printf("WARNING: Failed to find function \"%s\".\n", #NAME); +#include "FuzzerExtFunctions.def" +#undef EXT_FUNC +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS diff --git a/lib/Fuzzer/FuzzerExtraCounters.cpp b/lib/Fuzzer/FuzzerExtraCounters.cpp new file mode 100644 index 000000000000..07dbe0fdee76 --- /dev/null +++ b/lib/Fuzzer/FuzzerExtraCounters.cpp @@ -0,0 +1,41 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// Extra coverage counters defined by user code. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" + +#if LIBFUZZER_LINUX +__attribute__((weak)) extern uint8_t __start___libfuzzer_extra_counters; +__attribute__((weak)) extern uint8_t __stop___libfuzzer_extra_counters; + +namespace fuzzer { +uint8_t *ExtraCountersBegin() { return &__start___libfuzzer_extra_counters; } +uint8_t *ExtraCountersEnd() { return &__stop___libfuzzer_extra_counters; } +ATTRIBUTE_NO_SANITIZE_ALL +void ClearExtraCounters() { // hand-written memset, don't asan-ify. + uintptr_t *Beg = reinterpret_cast<uintptr_t*>(ExtraCountersBegin()); + uintptr_t *End = reinterpret_cast<uintptr_t*>(ExtraCountersEnd()); + for (; Beg < End; Beg++) { + *Beg = 0; + __asm__ __volatile__("" : : : "memory"); + } +} + +} // namespace fuzzer + +#else +// TODO: implement for other platforms. +namespace fuzzer { +uint8_t *ExtraCountersBegin() { return nullptr; } +uint8_t *ExtraCountersEnd() { return nullptr; } +void ClearExtraCounters() {} +} // namespace fuzzer + +#endif diff --git a/lib/Fuzzer/FuzzerFlags.def b/lib/Fuzzer/FuzzerFlags.def index 22aad353acec..28bf0ca8ce69 100644 --- a/lib/Fuzzer/FuzzerFlags.def +++ b/lib/Fuzzer/FuzzerFlags.def @@ -39,14 +39,19 @@ FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be " "merged into the 1-st corpus. Only interesting units will be taken. " "This flag can be used to minimize a corpus.") FUZZER_FLAG_STRING(merge_control_file, "internal flag") +FUZZER_FLAG_STRING(save_coverage_summary, "Experimental:" + " save coverage summary to a given file." + " Used with -merge=1") +FUZZER_FLAG_STRING(load_coverage_summary, "Experimental:" + " load coverage summary from a given file." + " Treat this coverage as belonging to the first corpus. " + " Used with -merge=1") FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided" " crash input. Use with -runs=N or -max_total_time=N to limit " "the number attempts") FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag") FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters") FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters") -FUZZER_FLAG_INT(use_memcmp, 1, - "Use hints from intercepting memcmp, strcmp, etc") FUZZER_FLAG_INT(use_memmem, 1, "Use hints from intercepting memmem, strstr, etc") FUZZER_FLAG_INT(use_value_profile, 0, @@ -85,7 +90,7 @@ FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information at exit." FUZZER_FLAG_INT(dump_coverage, 0, "If 1, dump coverage information at exit." " Experimental, only with trace-pc-guard") FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") -FUZZER_FLAG_INT(handle_bus, 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.") FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.") FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.") @@ -108,6 +113,10 @@ FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum" " was added to the corpus. " "Used primarily for testing libFuzzer itself.") +FUZZER_FLAG_STRING(run_equivalence_server, "Experimental") +FUZZER_FLAG_STRING(use_equivalence_server, "Experimental") +FUZZER_FLAG_INT(analyze_dict, 0, "Experimental") + FUZZER_DEPRECATED_FLAG(exit_on_first) FUZZER_DEPRECATED_FLAG(save_minimized_corpus) FUZZER_DEPRECATED_FLAG(sync_command) diff --git a/lib/Fuzzer/FuzzerIO.cpp b/lib/Fuzzer/FuzzerIO.cpp index eda8e8772930..e3f609ed8a80 100644 --- a/lib/Fuzzer/FuzzerIO.cpp +++ b/lib/Fuzzer/FuzzerIO.cpp @@ -96,14 +96,15 @@ void DupAndCloseStderr() { if (NewOutputFile) { OutputFile = NewOutputFile; if (EF->__sanitizer_set_report_fd) - EF->__sanitizer_set_report_fd(reinterpret_cast<void *>(OutputFd)); - CloseFile(2); + EF->__sanitizer_set_report_fd( + reinterpret_cast<void *>(GetHandleFromFd(OutputFd))); + DiscardOutput(2); } } } void CloseStdout() { - CloseFile(1); + DiscardOutput(1); } void Printf(const char *Fmt, ...) { diff --git a/lib/Fuzzer/FuzzerIO.h b/lib/Fuzzer/FuzzerIO.h index 15bfd3d34727..3b66a52d1a64 100644 --- a/lib/Fuzzer/FuzzerIO.h +++ b/lib/Fuzzer/FuzzerIO.h @@ -40,12 +40,17 @@ std::string DirName(const std::string &FileName); // Returns path to a TmpDir. std::string TmpDir(); +bool IsInterestingCoverageFile(const std::string &FileName); + void DupAndCloseStderr(); void CloseStdout(); void Printf(const char *Fmt, ...); +// Print using raw syscalls, useful when printing at early init stages. +void RawPrint(const char *Str); + // Platform specific functions: bool IsFile(const std::string &Path); @@ -62,6 +67,10 @@ int DuplicateFile(int Fd); void RemoveFile(const std::string &Path); +void DiscardOutput(int Fd); + +intptr_t GetHandleFromFd(int fd); + } // namespace fuzzer #endif // LLVM_FUZZER_IO_H diff --git a/lib/Fuzzer/FuzzerIOPosix.cpp b/lib/Fuzzer/FuzzerIOPosix.cpp index 6d8edf6ff538..c5ebdbac467b 100644 --- a/lib/Fuzzer/FuzzerIOPosix.cpp +++ b/lib/Fuzzer/FuzzerIOPosix.cpp @@ -75,6 +75,18 @@ void RemoveFile(const std::string &Path) { unlink(Path.c_str()); } +void DiscardOutput(int Fd) { + FILE* Temp = fopen("/dev/null", "w"); + if (!Temp) + return; + dup2(fileno(Temp), Fd); + fclose(Temp); +} + +intptr_t GetHandleFromFd(int fd) { + return static_cast<intptr_t>(fd); +} + std::string DirName(const std::string &FileName) { char *Tmp = new char[FileName.size() + 1]; memcpy(Tmp, FileName.c_str(), FileName.size() + 1); @@ -89,6 +101,23 @@ std::string TmpDir() { return "/tmp"; } +bool IsInterestingCoverageFile(const std::string &FileName) { + if (FileName.find("compiler-rt/lib/") != std::string::npos) + return false; // sanitizer internal. + if (FileName.find("/usr/lib/") != std::string::npos) + return false; + if (FileName.find("/usr/include/") != std::string::npos) + return false; + if (FileName == "<null>") + return false; + return true; +} + + +void RawPrint(const char *Str) { + write(2, Str, strlen(Str)); +} + } // namespace fuzzer #endif // LIBFUZZER_POSIX diff --git a/lib/Fuzzer/FuzzerIOWindows.cpp b/lib/Fuzzer/FuzzerIOWindows.cpp index 056f0721a336..75d4e3a06071 100644 --- a/lib/Fuzzer/FuzzerIOWindows.cpp +++ b/lib/Fuzzer/FuzzerIOWindows.cpp @@ -89,8 +89,10 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo)); if (FindHandle == INVALID_HANDLE_VALUE) { - Printf("No file found in: %s.\n", Dir.c_str()); - return; + if (GetLastError() == ERROR_FILE_NOT_FOUND) + return; + Printf("No such directory: %s; exiting\n", Dir.c_str()); + exit(1); } do { @@ -139,6 +141,18 @@ void RemoveFile(const std::string &Path) { _unlink(Path.c_str()); } +void DiscardOutput(int Fd) { + FILE* Temp = fopen("nul", "w"); + if (!Temp) + return; + _dup2(_fileno(Temp), Fd); + fclose(Temp); +} + +intptr_t GetHandleFromFd(int fd) { + return _get_osfhandle(fd); +} + static bool IsSeparator(char C) { return C == '\\' || C == '/'; } @@ -277,7 +291,32 @@ std::string DirName(const std::string &FileName) { return FileName.substr(0, LocationLen + DirLen); } -std::string TmpDir() { return "TODO: implement TmpDir"; } +std::string TmpDir() { + std::string Tmp; + Tmp.resize(MAX_PATH + 1); + DWORD Size = GetTempPathA(Tmp.size(), &Tmp[0]); + if (Size == 0) { + Printf("Couldn't get Tmp path.\n"); + exit(1); + } + Tmp.resize(Size); + return Tmp; +} + +bool IsInterestingCoverageFile(const std::string &FileName) { + if (FileName.find("Program Files") != std::string::npos) + return false; + if (FileName.find("compiler-rt\\lib\\") != std::string::npos) + return false; // sanitizer internal. + if (FileName == "<null>") + return false; + return true; +} + +void RawPrint(const char *Str) { + // Not tested, may or may not work. Fix if needed. + Printf("%s", Str); +} } // namespace fuzzer diff --git a/lib/Fuzzer/FuzzerInterface.h b/lib/Fuzzer/FuzzerInterface.h index d47e20e3a2b9..c2c0a39843c0 100644 --- a/lib/Fuzzer/FuzzerInterface.h +++ b/lib/Fuzzer/FuzzerInterface.h @@ -55,7 +55,7 @@ size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, unsigned int Seed); // Experimental, may go away in future. -// libFuzzer-provided function to be used inside LLVMFuzzerTestOneInput. +// 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); diff --git a/lib/Fuzzer/FuzzerInternal.h b/lib/Fuzzer/FuzzerInternal.h index 0d2c7a78aca8..c26615631ecd 100644 --- a/lib/Fuzzer/FuzzerInternal.h +++ b/lib/Fuzzer/FuzzerInternal.h @@ -32,26 +32,6 @@ using namespace std::chrono; class Fuzzer { public: - // Aggregates all available coverage measurements. - struct Coverage { - Coverage() { Reset(); } - - void Reset() { - BlockCoverage = 0; - CallerCalleeCoverage = 0; - CounterBitmapBits = 0; - CounterBitmap.clear(); - VPMap.Reset(); - } - - size_t BlockCoverage; - size_t CallerCalleeCoverage; - // Precalculated number of bits in CounterBitmap. - size_t CounterBitmapBits; - std::vector<uint8_t> CounterBitmap; - ValueBitMap VPMap; - }; - Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, FuzzingOptions Options); ~Fuzzer(); @@ -90,25 +70,23 @@ public: // Merge Corpora[1:] into Corpora[0]. void Merge(const std::vector<std::string> &Corpora); void CrashResistantMerge(const std::vector<std::string> &Args, - const std::vector<std::string> &Corpora); + const std::vector<std::string> &Corpora, + const char *CoverageSummaryInputPathOrNull, + const char *CoverageSummaryOutputPathOrNull); void CrashResistantMergeInternalStep(const std::string &ControlFilePath); - // Returns a subset of 'Extra' that adds coverage to 'Initial'. - UnitVector FindExtraUnits(const UnitVector &Initial, const UnitVector &Extra); MutationDispatcher &GetMD() { return MD; } void PrintFinalStats(); void SetMaxInputLen(size_t MaxInputLen); void SetMaxMutationLen(size_t MaxMutationLen); void RssLimitCallback(); - // Public for tests. - void ResetCoverage(); - bool InFuzzingThread() const { return IsMyThread; } size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const; void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, bool DuringInitialCorpusExecution); void HandleMalloc(size_t Size); + void AnnounceOutput(const uint8_t *Data, size_t Size); private: void AlarmCallback(); @@ -134,16 +112,10 @@ private: // Stop tracing. void StopTraceRecording(); - void SetDeathCallback(); static void StaticDeathCallback(); void DumpCurrentUnit(const char *Prefix); void DeathCallback(); - void ResetEdgeCoverage(); - void ResetCounters(); - void PrepareCounters(Fuzzer::Coverage *C); - bool RecordMaxCoverage(Fuzzer::Coverage *C); - void AllocateCurrentUnitData(); uint8_t *CurrentUnitData = nullptr; std::atomic<size_t> CurrentUnitSize; @@ -166,16 +138,11 @@ private: long TimeOfLongestUnitInSeconds = 0; long EpochOfLastReadOfOutputCorpus = 0; - // Maximum recorded coverage. - Coverage MaxCoverage; - size_t MaxInputLen = 0; size_t MaxMutationLen = 0; // Need to know our own thread. static thread_local bool IsMyThread; - - bool InMergeMode = false; }; }; // namespace fuzzer diff --git a/lib/Fuzzer/FuzzerLoop.cpp b/lib/Fuzzer/FuzzerLoop.cpp index 9f49d1557990..704092896eb6 100644 --- a/lib/Fuzzer/FuzzerLoop.cpp +++ b/lib/Fuzzer/FuzzerLoop.cpp @@ -14,6 +14,7 @@ #include "FuzzerIO.h" #include "FuzzerMutate.h" #include "FuzzerRandom.h" +#include "FuzzerShmem.h" #include "FuzzerTracePC.h" #include <algorithm> #include <cstring> @@ -42,73 +43,11 @@ static const size_t kMaxUnitSizeToPrint = 256; thread_local bool Fuzzer::IsMyThread; -static void MissingExternalApiFunction(const char *FnName) { - Printf("ERROR: %s is not defined. Exiting.\n" - "Did you use -fsanitize-coverage=... to build your code?\n", - FnName); - exit(1); -} - -#define CHECK_EXTERNAL_FUNCTION(fn) \ - do { \ - if (!(EF->fn)) \ - MissingExternalApiFunction(#fn); \ - } while (false) +SharedMemoryRegion SMR; // Only one Fuzzer per process. static Fuzzer *F; -void Fuzzer::ResetEdgeCoverage() { - CHECK_EXTERNAL_FUNCTION(__sanitizer_reset_coverage); - EF->__sanitizer_reset_coverage(); -} - -void Fuzzer::ResetCounters() { - if (Options.UseCounters) - EF->__sanitizer_update_counter_bitset_and_clear_counters(0); -} - -void Fuzzer::PrepareCounters(Fuzzer::Coverage *C) { - if (Options.UseCounters) { - size_t NumCounters = EF->__sanitizer_get_number_of_counters(); - C->CounterBitmap.resize(NumCounters); - } -} - -// Records data to a maximum coverage tracker. Returns true if additional -// coverage was discovered. -bool Fuzzer::RecordMaxCoverage(Fuzzer::Coverage *C) { - bool Res = false; - - uint64_t NewBlockCoverage = EF->__sanitizer_get_total_unique_coverage(); - if (NewBlockCoverage > C->BlockCoverage) { - Res = true; - C->BlockCoverage = NewBlockCoverage; - } - - if (Options.UseIndirCalls && - EF->__sanitizer_get_total_unique_caller_callee_pairs) { - uint64_t NewCallerCalleeCoverage = - EF->__sanitizer_get_total_unique_caller_callee_pairs(); - if (NewCallerCalleeCoverage > C->CallerCalleeCoverage) { - Res = true; - C->CallerCalleeCoverage = NewCallerCalleeCoverage; - } - } - - if (Options.UseCounters) { - uint64_t CounterDelta = - EF->__sanitizer_update_counter_bitset_and_clear_counters( - C->CounterBitmap.data()); - if (CounterDelta > 0) { - Res = true; - C->CounterBitmapBits += CounterDelta; - } - } - - return Res; -} - // Leak detection is expensive, so we first check if there were more mallocs // than frees (using the sanitizer malloc hooks) and only then try to call lsan. struct MallocFreeTracer { @@ -176,12 +115,12 @@ void Fuzzer::HandleMalloc(size_t Size) { Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, FuzzingOptions Options) : CB(CB), Corpus(Corpus), MD(MD), Options(Options) { - SetDeathCallback(); + if (EF->__sanitizer_set_death_callback) + EF->__sanitizer_set_death_callback(StaticDeathCallback); InitializeTraceState(); assert(!F); F = this; TPC.ResetMaps(); - ResetCoverage(); IsMyThread = true; if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks) EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook); @@ -206,33 +145,12 @@ void Fuzzer::AllocateCurrentUnitData() { CurrentUnitData = new uint8_t[MaxInputLen]; } -void Fuzzer::SetDeathCallback() { - CHECK_EXTERNAL_FUNCTION(__sanitizer_set_death_callback); - EF->__sanitizer_set_death_callback(StaticDeathCallback); -} - void Fuzzer::StaticDeathCallback() { assert(F); F->DeathCallback(); } -static void WarnOnUnsuccessfullMerge(bool DoWarn) { - if (!DoWarn) return; - Printf( - "***\n" - "***\n" - "***\n" - "*** NOTE: merge did not succeed due to a failure on one of the inputs.\n" - "*** You will need to filter out crashes from the corpus, e.g. like this:\n" - "*** for f in WITH_CRASHES/*; do ./fuzzer $f && cp $f NO_CRASHES; done\n" - "*** Future versions may have crash-resistant merge, stay tuned.\n" - "***\n" - "***\n" - "***\n"); -} - void Fuzzer::DumpCurrentUnit(const char *Prefix) { - WarnOnUnsuccessfullMerge(InMergeMode); if (!CurrentUnitData) return; // Happens when running individual inputs. MD.PrintMutationSequence(); Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str()); @@ -293,7 +211,10 @@ void Fuzzer::InterruptCallback() { NO_SANITIZE_MEMORY void Fuzzer::AlarmCallback() { assert(Options.UnitTimeoutSec > 0); + // In Windows Alarm callback is executed by a different thread. +#if !LIBFUZZER_WINDOWS if (!InFuzzingThread()) return; +#endif if (!RunningCB) return; // We have not started running units yet. size_t Seconds = @@ -323,7 +244,7 @@ void Fuzzer::RssLimitCallback() { 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); + EF->__sanitizer_print_memory_profile(95, 8); DumpCurrentUnit("oom-"); Printf("SUMMARY: libFuzzer: out-of-memory\n"); PrintFinalStats(); @@ -338,26 +259,18 @@ void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) { csvHeaderPrinted = true; Printf("runs,block_cov,bits,cc_cov,corpus,execs_per_sec,tbms,reason\n"); } - Printf("%zd,%zd,%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns, - MaxCoverage.BlockCoverage, MaxCoverage.CounterBitmapBits, - MaxCoverage.CallerCalleeCoverage, Corpus.size(), ExecPerSec, Where); + Printf("%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns, + TPC.GetTotalPCCoverage(), + Corpus.size(), ExecPerSec, Where); } if (!Options.Verbosity) return; Printf("#%zd\t%s", TotalNumberOfRuns, Where); - if (MaxCoverage.BlockCoverage) - Printf(" cov: %zd", MaxCoverage.BlockCoverage); - if (size_t N = MaxCoverage.VPMap.GetNumBitsSinceLastMerge()) - Printf(" vp: %zd", N); if (size_t N = TPC.GetTotalPCCoverage()) Printf(" cov: %zd", N); - if (auto TB = MaxCoverage.CounterBitmapBits) - Printf(" bits: %zd", TB); if (size_t N = Corpus.NumFeatures()) Printf( " ft: %zd", N); - if (MaxCoverage.CallerCalleeCoverage) - Printf(" indir: %zd", MaxCoverage.CallerCalleeCoverage); if (!Corpus.empty()) { Printf(" corp: %zd", Corpus.NumActiveUnits()); if (size_t N = Corpus.SizeInBytes()) { @@ -456,7 +369,7 @@ void Fuzzer::RereadOutputCorpus(size_t MaxSize) { } void Fuzzer::ShuffleCorpus(UnitVector *V) { - std::random_shuffle(V->begin(), V->end(), MD.GetRand()); + std::shuffle(V->begin(), V->end(), MD.GetRand()); if (Options.PreferSmall) std::stable_sort(V->begin(), V->end(), [](const Unit &A, const Unit &B) { return A.size() < B.size(); @@ -476,8 +389,6 @@ void Fuzzer::ShuffleAndMinimize(UnitVector *InitialCorpus) { if (size_t NumFeatures = RunOne(U)) { CheckExitOnSrcPosOrItem(); Corpus.AddToCorpus(U, NumFeatures); - if (Options.Verbosity >= 2) - Printf("NEW0: %zd L %zd\n", MaxCoverage.BlockCoverage, U.size()); } TryDetectingAMemoryLeak(U.data(), U.size(), /*DuringInitialCorpusExecution*/ true); @@ -496,18 +407,11 @@ size_t Fuzzer::RunOne(const uint8_t *Data, size_t Size) { ExecuteCallback(Data, Size); - size_t Res = 0; - if (size_t NumFeatures = TPC.CollectFeatures([&](size_t Feature) -> bool { - return Corpus.AddFeature(Feature, Size, Options.Shrink); - })) - Res = NumFeatures; - - if (!TPC.UsingTracePcGuard()) { - if (TPC.UpdateValueProfileMap(&MaxCoverage.VPMap)) - Res = 1; - if (!Res && RecordMaxCoverage(&MaxCoverage)) - Res = 1; - } + size_t NumUpdatesBefore = Corpus.NumFeatureUpdates(); + TPC.CollectFeatures([&](size_t Feature) { + Corpus.AddFeature(Feature, Size, Options.Shrink); + }); + size_t NumUpdatesAfter = Corpus.NumFeatureUpdates(); auto TimeOfUnit = duration_cast<seconds>(UnitStopTime - UnitStartTime).count(); @@ -520,7 +424,7 @@ size_t Fuzzer::RunOne(const uint8_t *Data, size_t Size) { Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds); WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-"); } - return Res; + return NumUpdatesAfter - NumUpdatesBefore; } size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { @@ -531,6 +435,8 @@ size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { assert(InFuzzingThread()); + if (SMR.IsClient()) + SMR.WriteByteArray(Data, Size); // We copy the contents of Unit into a separate heap buffer // so that we reliably find buffer overflows in it. uint8_t *DataCopy = new uint8_t[Size]; @@ -540,7 +446,6 @@ void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { CurrentUnitSize = Size; AllocTracer.Start(Options.TraceMalloc); UnitStartTime = system_clock::now(); - ResetCounters(); // Reset coverage right before the callback. TPC.ResetMaps(); RunningCB = true; int Res = CB(DataCopy, Size); @@ -597,77 +502,6 @@ void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) { TPC.PrintNewPCs(); } -// Finds minimal number of units in 'Extra' that add coverage to 'Initial'. -// We do it by actually executing the units, sometimes more than once, -// because we may be using different coverage-like signals and the only -// common thing between them is that we can say "this unit found new stuff". -UnitVector Fuzzer::FindExtraUnits(const UnitVector &Initial, - const UnitVector &Extra) { - UnitVector Res = Extra; - UnitVector Tmp; - size_t OldSize = Res.size(); - for (int Iter = 0; Iter < 10; Iter++) { - ShuffleCorpus(&Res); - TPC.ResetMaps(); - Corpus.ResetFeatureSet(); - ResetCoverage(); - - for (auto &U : Initial) { - TPC.ResetMaps(); - RunOne(U); - } - - Tmp.clear(); - for (auto &U : Res) { - TPC.ResetMaps(); - if (RunOne(U)) - Tmp.push_back(U); - } - - char Stat[7] = "MIN "; - Stat[3] = '0' + Iter; - PrintStats(Stat, "\n", Tmp.size()); - - size_t NewSize = Tmp.size(); - assert(NewSize <= OldSize); - Res.swap(Tmp); - - if (NewSize + 5 >= OldSize) - break; - OldSize = NewSize; - } - return Res; -} - -void Fuzzer::Merge(const std::vector<std::string> &Corpora) { - if (Corpora.size() <= 1) { - Printf("Merge requires two or more corpus dirs\n"); - return; - } - InMergeMode = true; - std::vector<std::string> ExtraCorpora(Corpora.begin() + 1, Corpora.end()); - - assert(MaxInputLen > 0); - UnitVector Initial, Extra; - ReadDirToVectorOfUnits(Corpora[0].c_str(), &Initial, nullptr, MaxInputLen, - true); - for (auto &C : ExtraCorpora) - ReadDirToVectorOfUnits(C.c_str(), &Extra, nullptr, MaxInputLen, true); - - if (!Initial.empty()) { - Printf("=== Minimizing the initial corpus of %zd units\n", Initial.size()); - Initial = FindExtraUnits({}, Initial); - } - - Printf("=== Merging extra %zd units\n", Extra.size()); - auto Res = FindExtraUnits(Initial, Extra); - - for (auto &U: Res) - WriteToOutputCorpus(U); - - Printf("=== Merge: written %zd units\n", Res.size()); -} - // Tries detecting a memory leak on the particular input that we have just // executed before calling this function. void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, @@ -762,12 +596,6 @@ void Fuzzer::MutateAndTestOne() { } } -void Fuzzer::ResetCoverage() { - ResetEdgeCoverage(); - MaxCoverage.Reset(); - PrepareCounters(&MaxCoverage); -} - void Fuzzer::Loop() { TPC.InitializePrintNewPCs(); system_clock::time_point LastCorpusReload = system_clock::now(); @@ -792,7 +620,7 @@ void Fuzzer::Loop() { } void Fuzzer::MinimizeCrashLoop(const Unit &U) { - if (U.size() <= 2) return; + if (U.size() <= 1) return; while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) { MD.StartMutationSequence(); memcpy(CurrentUnitData, U.data(), U.size()); @@ -806,6 +634,29 @@ void Fuzzer::MinimizeCrashLoop(const Unit &U) { } } +void Fuzzer::AnnounceOutput(const uint8_t *Data, size_t Size) { + if (SMR.IsServer()) { + SMR.WriteByteArray(Data, Size); + } else if (SMR.IsClient()) { + SMR.PostClient(); + SMR.WaitServer(); + size_t OtherSize = SMR.ReadByteArraySize(); + uint8_t *OtherData = SMR.GetByteArray(); + if (Size != OtherSize || memcmp(Data, OtherData, Size) != 0) { + size_t i = 0; + for (i = 0; i < Min(Size, OtherSize); i++) + if (Data[i] != OtherData[i]) + break; + Printf("==%lu== ERROR: libFuzzer: equivalence-mismatch. Sizes: %zd %zd; " + "offset %zd\n", GetPid(), Size, OtherSize, i); + DumpCurrentUnit("mismatch-"); + Printf("SUMMARY: libFuzzer: equivalence-mismatch\n"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); + } + } +} + } // namespace fuzzer extern "C" { @@ -814,4 +665,10 @@ 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) { + assert(fuzzer::F); + fuzzer::F->AnnounceOutput(Data, Size); +} } // extern "C" diff --git a/lib/Fuzzer/FuzzerMerge.cpp b/lib/Fuzzer/FuzzerMerge.cpp index 9e559115680c..e66460c29e2f 100644 --- a/lib/Fuzzer/FuzzerMerge.cpp +++ b/lib/Fuzzer/FuzzerMerge.cpp @@ -17,6 +17,7 @@ #include <fstream> #include <iterator> +#include <set> #include <sstream> namespace fuzzer { @@ -73,6 +74,7 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) { size_t ExpectedStartMarker = 0; const size_t kInvalidStartMarker = -1; size_t LastSeenStartMarker = kInvalidStartMarker; + std::vector<uint32_t> TmpFeatures; while (std::getline(IS, Line, '\n')) { std::istringstream ISS1(Line); std::string Marker; @@ -88,17 +90,17 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) { assert(ExpectedStartMarker < Files.size()); ExpectedStartMarker++; } else if (Marker == "DONE") { - // DONE FILE_SIZE COV1 COV2 COV3 ... + // DONE FILE_ID COV1 COV2 COV3 ... size_t CurrentFileIdx = N; if (CurrentFileIdx != LastSeenStartMarker) return false; LastSeenStartMarker = kInvalidStartMarker; if (ParseCoverage) { - auto &V = Files[CurrentFileIdx].Features; - V.clear(); + TmpFeatures.clear(); // use a vector from outer scope to avoid resizes. while (ISS1 >> std::hex >> N) - V.push_back(N); - std::sort(V.begin(), V.end()); + TmpFeatures.push_back(N); + std::sort(TmpFeatures.begin(), TmpFeatures.end()); + Files[CurrentFileIdx].Features = TmpFeatures; } } else { return false; @@ -111,12 +113,20 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) { return true; } +size_t Merger::ApproximateMemoryConsumption() const { + size_t Res = 0; + for (const auto &F: Files) + Res += sizeof(F) + F.Features.size() * sizeof(F.Features[0]); + return Res; +} + // Decides which files need to be merged (add thost to NewFiles). // Returns the number of new features added. -size_t Merger::Merge(std::vector<std::string> *NewFiles) { +size_t Merger::Merge(const std::set<uint32_t> &InitialFeatures, + std::vector<std::string> *NewFiles) { NewFiles->clear(); assert(NumFilesInFirstCorpus <= Files.size()); - std::set<uint32_t> AllFeatures; + std::set<uint32_t> AllFeatures(InitialFeatures); // What features are in the initial corpus? for (size_t i = 0; i < NumFilesInFirstCorpus; i++) { @@ -158,6 +168,42 @@ size_t Merger::Merge(std::vector<std::string> *NewFiles) { return AllFeatures.size() - InitialNumFeatures; } +void Merger::PrintSummary(std::ostream &OS) { + for (auto &File : Files) { + OS << std::hex; + OS << File.Name << " size: " << File.Size << " features: "; + for (auto Feature : File.Features) + OS << " " << Feature; + OS << "\n"; + } +} + +std::set<uint32_t> Merger::AllFeatures() const { + std::set<uint32_t> S; + for (auto &File : Files) + S.insert(File.Features.begin(), File.Features.end()); + return S; +} + +std::set<uint32_t> Merger::ParseSummary(std::istream &IS) { + std::string Line, Tmp; + std::set<uint32_t> Res; + while (std::getline(IS, Line, '\n')) { + size_t N; + std::istringstream ISS1(Line); + ISS1 >> Tmp; // Name + ISS1 >> Tmp; // size: + assert(Tmp == "size:" && "Corrupt summary file"); + ISS1 >> std::hex; + ISS1 >> N; // File Size + ISS1 >> Tmp; // features: + assert(Tmp == "features:" && "Corrupt summary file"); + while (ISS1 >> std::hex >> N) + Res.insert(N); + } + return Res; +} + // Inner process. May crash if the target crashes. void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str()); @@ -208,7 +254,9 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { // Outer process. Does not call the target code and thus sohuld not fail. void Fuzzer::CrashResistantMerge(const std::vector<std::string> &Args, - const std::vector<std::string> &Corpora) { + const std::vector<std::string> &Corpora, + const char *CoverageSummaryInputPathOrNull, + const char *CoverageSummaryOutputPathOrNull) { if (Corpora.size() <= 1) { Printf("Merge requires two or more corpus dirs\n"); return; @@ -239,15 +287,21 @@ void Fuzzer::CrashResistantMerge(const std::vector<std::string> &Args, // Execute the inner process untill it passes. // Every inner process should execute at least one input. std::string BaseCmd = CloneArgsWithoutX(Args, "keep-all-flags"); + bool Success = false; for (size_t i = 1; i <= AllFiles.size(); i++) { Printf("MERGE-OUTER: attempt %zd\n", i); auto ExitCode = ExecuteCommand(BaseCmd + " -merge_control_file=" + CFPath); if (!ExitCode) { Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", i); + Success = true; break; } } + if (!Success) { + Printf("MERGE-OUTER: zero succesfull attempts, exiting\n"); + exit(1); + } // Read the control file and do the merge. Merger M; std::ifstream IF(CFPath); @@ -256,8 +310,23 @@ void Fuzzer::CrashResistantMerge(const std::vector<std::string> &Args, IF.seekg(0, IF.beg); M.ParseOrExit(IF, true); IF.close(); + Printf("MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n", + M.ApproximateMemoryConsumption() >> 20, GetPeakRSSMb()); + if (CoverageSummaryOutputPathOrNull) { + Printf("MERGE-OUTER: writing coverage summary for %zd files to %s\n", + M.Files.size(), CoverageSummaryOutputPathOrNull); + std::ofstream SummaryOut(CoverageSummaryOutputPathOrNull); + M.PrintSummary(SummaryOut); + } std::vector<std::string> NewFiles; - size_t NumNewFeatures = M.Merge(&NewFiles); + std::set<uint32_t> InitialFeatures; + if (CoverageSummaryInputPathOrNull) { + std::ifstream SummaryIn(CoverageSummaryInputPathOrNull); + InitialFeatures = M.ParseSummary(SummaryIn); + Printf("MERGE-OUTER: coverage summary loaded from %s, %zd features found\n", + CoverageSummaryInputPathOrNull, InitialFeatures.size()); + } + size_t NumNewFeatures = M.Merge(InitialFeatures, &NewFiles); Printf("MERGE-OUTER: %zd new files with %zd new features added\n", NewFiles.size(), NumNewFeatures); for (auto &F: NewFiles) diff --git a/lib/Fuzzer/FuzzerMerge.h b/lib/Fuzzer/FuzzerMerge.h index 8a2fe5d74f88..cf4a0863571d 100644 --- a/lib/Fuzzer/FuzzerMerge.h +++ b/lib/Fuzzer/FuzzerMerge.h @@ -43,7 +43,9 @@ #include "FuzzerDefs.h" #include <istream> +#include <ostream> #include <set> +#include <vector> namespace fuzzer { @@ -62,7 +64,15 @@ struct Merger { bool Parse(std::istream &IS, bool ParseCoverage); bool Parse(const std::string &Str, bool ParseCoverage); void ParseOrExit(std::istream &IS, bool ParseCoverage); - size_t Merge(std::vector<std::string> *NewFiles); + void PrintSummary(std::ostream &OS); + std::set<uint32_t> ParseSummary(std::istream &IS); + size_t Merge(const std::set<uint32_t> &InitialFeatures, + std::vector<std::string> *NewFiles); + size_t Merge(std::vector<std::string> *NewFiles) { + return Merge({}, NewFiles); + } + size_t ApproximateMemoryConsumption() const; + std::set<uint32_t> AllFeatures() const; }; } // namespace fuzzer diff --git a/lib/Fuzzer/FuzzerMutate.cpp b/lib/Fuzzer/FuzzerMutate.cpp index 96a87b879d6f..cd846c7deec5 100644 --- a/lib/Fuzzer/FuzzerMutate.cpp +++ b/lib/Fuzzer/FuzzerMutate.cpp @@ -81,8 +81,8 @@ size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size, const Unit &Other = (*Corpus)[Idx]; if (Other.empty()) return 0; - MutateInPlaceHere.resize(MaxSize); - auto &U = MutateInPlaceHere; + CustomCrossOverInPlaceHere.resize(MaxSize); + auto &U = CustomCrossOverInPlaceHere; size_t NewSize = EF->LLVMFuzzerCustomCrossOver( Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand()); if (!NewSize) @@ -94,21 +94,18 @@ size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize) { - if (Size > MaxSize) return 0; - assert(Size); + if (Size > MaxSize || Size == 0) return 0; size_t ShuffleAmount = Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size. size_t ShuffleStart = Rand(Size - ShuffleAmount); assert(ShuffleStart + ShuffleAmount <= Size); - std::random_shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, - Rand); + std::shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, Rand); return Size; } size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize) { - assert(Size); - if (Size == 1) return 0; + if (Size <= 1) return 0; size_t N = Rand(Size / 2) + 1; assert(N < Size); size_t Idx = Rand(Size - N + 1); @@ -200,28 +197,27 @@ size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size, // It first tries to find one of the arguments (possibly swapped) in the // input and if it succeeds it creates a DE with a position hint. // Otherwise it creates a DE with one of the arguments w/o a position hint. -template <class T> DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( - T Arg1, T Arg2, const uint8_t *Data, size_t Size) { - ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem; + const void *Arg1, const void *Arg2, + 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(); - T ExistingBytes, DesiredBytes; + const void *ExistingBytes, *DesiredBytes; Word W; const uint8_t *End = Data + Size; for (int Arg = 0; Arg < 2; Arg++) { ExistingBytes = HandleFirst ? Arg1 : Arg2; - DesiredBytes = HandleFirst ? Arg2 : Arg1; - DesiredBytes += Rand(-1, 1); - if (Rand.RandBool()) ExistingBytes = Bswap(ExistingBytes); - if (Rand.RandBool()) DesiredBytes = Bswap(DesiredBytes); + DesiredBytes = HandleFirst ? Arg2Mutation : Arg1Mutation; HandleFirst = !HandleFirst; - W.Set(reinterpret_cast<uint8_t*>(&DesiredBytes), sizeof(T)); + W.Set(reinterpret_cast<const uint8_t*>(DesiredBytes), ArgSize); const size_t kMaxNumPositions = 8; size_t Positions[kMaxNumPositions]; size_t NumPositions = 0; for (const uint8_t *Cur = Data; Cur < End && NumPositions < kMaxNumPositions; Cur++) { - Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &ExistingBytes, sizeof(T)); + Cur = (uint8_t *)SearchMemory(Cur, End - Cur, ExistingBytes, ArgSize); if (!Cur) break; Positions[NumPositions++] = Cur - Data; } @@ -232,21 +228,48 @@ DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( return DE; } + +template <class T> +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + T Arg1, T Arg2, const uint8_t *Data, size_t Size) { + if (Rand.RandBool()) Arg1 = Bswap(Arg1); + if (Rand.RandBool()) Arg2 = Bswap(Arg2); + T Arg1Mutation = Arg1 + Rand(-1, 1); + T Arg2Mutation = Arg2 + Rand(-1, 1); + return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation, + sizeof(Arg1), Data, Size); +} + +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + const Word &Arg1, const Word &Arg2, const uint8_t *Data, size_t Size) { + return MakeDictionaryEntryFromCMP(Arg1.data(), Arg2.data(), Arg1.data(), + Arg2.data(), Arg1.size(), Data, Size); +} + size_t MutationDispatcher::Mutate_AddWordFromTORC( uint8_t *Data, size_t Size, size_t MaxSize) { Word W; DictionaryEntry DE; - if (Rand.RandBool()) { + switch (Rand(3)) { + case 0: { auto X = TPC.TORC8.Get(Rand.Rand()); DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); - } else { + } break; + case 1: { auto X = TPC.TORC4.Get(Rand.Rand()); if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool()) - DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data, - Size); + DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data, Size); else DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + } break; + case 2: { + auto X = TPC.TORCW.Get(Rand.Rand()); + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + } break; + default: + assert(0); } + if (!DE.GetW().size()) return 0; Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE); if (!Size) return 0; DictionaryEntry &DERef = @@ -317,7 +340,7 @@ 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) return 0; + if (Size > MaxSize || Size == 0) return 0; if (Rand.RandBool()) return CopyPartOf(Data, Size, Data, Size); else @@ -413,9 +436,9 @@ size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size, break; case 1: NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize); - if (NewSize) - break; - // LLVM_FALLTHROUGH; + if (!NewSize) + NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size()); + break; case 2: NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size()); break; @@ -437,6 +460,7 @@ void MutationDispatcher::RecordSuccessfulMutationSequence() { for (auto DE : CurrentDictionaryEntrySequence) { // PersistentAutoDictionary.AddWithSuccessCountOne(DE); DE->IncSuccessCount(); + assert(DE->GetW().size()); // Linear search is fine here as this happens seldom. if (!PersistentAutoDictionary.ContainsWord(DE->GetW())) PersistentAutoDictionary.push_back({DE->GetW(), 1}); @@ -451,6 +475,7 @@ void MutationDispatcher::PrintRecommendedDictionary() { if (V.empty()) return; Printf("###### Recommended dictionary. ######\n"); for (auto &DE: V) { + assert(DE.GetW().size()); Printf("\""); PrintASCII(DE.GetW(), "\""); Printf(" # Uses: %zd\n", DE.GetUseCount()); @@ -485,14 +510,6 @@ size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize, const std::vector<Mutator> &Mutators) { assert(MaxSize > 0); - if (Size == 0) { - for (size_t i = 0; i < Min(size_t(4), MaxSize); i++) - Data[i] = RandCh(Rand); - if (Options.OnlyASCII) - ToASCII(Data, MaxSize); - return MaxSize; - } - assert(Size > 0); // Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize), // in which case they will return 0. // Try several times before returning un-mutated data. @@ -506,7 +523,8 @@ size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size, return NewSize; } } - return std::min(Size, MaxSize); + *Data = ' '; + return 1; // Fallback, should not happen frequently. } void MutationDispatcher::AddWordToManualDictionary(const Word &W) { diff --git a/lib/Fuzzer/FuzzerMutate.h b/lib/Fuzzer/FuzzerMutate.h index d3c0b0012468..8c8fb3fd74c7 100644 --- a/lib/Fuzzer/FuzzerMutate.h +++ b/lib/Fuzzer/FuzzerMutate.h @@ -14,6 +14,7 @@ #include "FuzzerDefs.h" #include "FuzzerDictionary.h" +#include "FuzzerOptions.h" #include "FuzzerRandom.h" namespace fuzzer { @@ -113,9 +114,16 @@ private: template <class T> DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2, const uint8_t *Data, size_t Size); + DictionaryEntry MakeDictionaryEntryFromCMP(const Word &Arg1, const Word &Arg2, + const uint8_t *Data, size_t Size); + DictionaryEntry MakeDictionaryEntryFromCMP(const void *Arg1, const void *Arg2, + const void *Arg1Mutation, + const void *Arg2Mutation, + size_t ArgSize, + const uint8_t *Data, size_t Size); Random &Rand; - const FuzzingOptions &Options; + const FuzzingOptions Options; // Dictionary provided by the user via -dict=DICT_FILE. Dictionary ManualDictionary; @@ -135,6 +143,9 @@ private: const InputCorpus *Corpus = nullptr; std::vector<uint8_t> MutateInPlaceHere; + // CustomCrossOver needs its own buffer as a custom implementation may call + // LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere. + std::vector<uint8_t> CustomCrossOverInPlaceHere; std::vector<Mutator> Mutators; std::vector<Mutator> DefaultMutators; diff --git a/lib/Fuzzer/FuzzerOptions.h b/lib/Fuzzer/FuzzerOptions.h index 6f72205600b9..872def0326f0 100644 --- a/lib/Fuzzer/FuzzerOptions.h +++ b/lib/Fuzzer/FuzzerOptions.h @@ -1,4 +1,3 @@ -//===- FuzzerOptions.h - Internal header for the Fuzzer ---------*- C++ -* ===// // // The LLVM Compiler Infrastructure // @@ -29,7 +28,6 @@ struct FuzzingOptions { int MutateDepth = 5; bool UseCounters = false; bool UseIndirCalls = true; - bool UseMemcmp = true; bool UseMemmem = true; bool UseCmp = false; bool UseValueProfile = false; diff --git a/lib/Fuzzer/FuzzerRandom.h b/lib/Fuzzer/FuzzerRandom.h index b1be0bb935fa..8a1aa3ef5fdc 100644 --- a/lib/Fuzzer/FuzzerRandom.h +++ b/lib/Fuzzer/FuzzerRandom.h @@ -15,10 +15,11 @@ #include <random> namespace fuzzer { -class Random { +class Random : public std::mt19937 { public: - Random(unsigned int seed) : R(seed) {} - size_t Rand() { return R(); } + Random(unsigned int seed) : std::mt19937(seed) {} + result_type operator()() { return this->std::mt19937::operator()(); } + size_t Rand() { return this->operator()(); } size_t RandBool() { return Rand() % 2; } size_t operator()(size_t n) { return n ? Rand() % n : 0; } intptr_t operator()(intptr_t From, intptr_t To) { @@ -26,9 +27,6 @@ class Random { intptr_t RangeSize = To - From + 1; return operator()(RangeSize) + From; } - std::mt19937 &Get_mt19937() { return R; } - private: - std::mt19937 R; }; } // namespace fuzzer diff --git a/lib/Fuzzer/FuzzerShmem.h b/lib/Fuzzer/FuzzerShmem.h new file mode 100644 index 000000000000..53568e0acb69 --- /dev/null +++ b/lib/Fuzzer/FuzzerShmem.h @@ -0,0 +1,69 @@ +//===- FuzzerShmem.h - shared memory interface ------------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// SharedMemoryRegion +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_SHMEM_H +#define LLVM_FUZZER_SHMEM_H + +#include <algorithm> +#include <cstring> +#include <string> + +#include "FuzzerDefs.h" + +namespace fuzzer { + +class SharedMemoryRegion { + public: + bool Create(const char *Name); + bool Open(const char *Name); + bool Destroy(const char *Name); + uint8_t *GetData() { return Data; } + void PostServer() {Post(0);} + void WaitServer() {Wait(0);} + void PostClient() {Post(1);} + void WaitClient() {Wait(1);} + + size_t WriteByteArray(const uint8_t *Bytes, size_t N) { + assert(N <= kShmemSize - sizeof(N)); + memcpy(GetData(), &N, sizeof(N)); + memcpy(GetData() + sizeof(N), Bytes, N); + assert(N == ReadByteArraySize()); + return N; + } + size_t ReadByteArraySize() { + size_t Res; + memcpy(&Res, GetData(), sizeof(Res)); + return Res; + } + uint8_t *GetByteArray() { return GetData() + sizeof(size_t); } + + bool IsServer() const { return Data && IAmServer; } + bool IsClient() const { return Data && !IAmServer; } + +private: + + static const size_t kShmemSize = 1 << 22; + bool IAmServer; + std::string Path(const char *Name); + std::string SemName(const char *Name, int Idx); + void Post(int Idx); + void Wait(int Idx); + + bool Map(int fd); + uint8_t *Data = nullptr; + void *Semaphore[2]; +}; + +extern SharedMemoryRegion SMR; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_SHMEM_H diff --git a/lib/Fuzzer/FuzzerShmemPosix.cpp b/lib/Fuzzer/FuzzerShmemPosix.cpp new file mode 100644 index 000000000000..2723bdd86f48 --- /dev/null +++ b/lib/Fuzzer/FuzzerShmemPosix.cpp @@ -0,0 +1,103 @@ +//===- FuzzerShmemPosix.cpp - Posix shared memory ---------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// SharedMemoryRegion +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_POSIX + +#include "FuzzerIO.h" +#include "FuzzerShmem.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <semaphore.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +namespace fuzzer { + +std::string SharedMemoryRegion::Path(const char *Name) { + return DirPlusFile(TmpDir(), Name); +} + +std::string SharedMemoryRegion::SemName(const char *Name, int Idx) { + std::string Res(Name); + return Res + (char)('0' + Idx); +} + +bool SharedMemoryRegion::Map(int fd) { + Data = + (uint8_t *)mmap(0, kShmemSize, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); + if (Data == (uint8_t*)-1) + return false; + return true; +} + +bool SharedMemoryRegion::Create(const char *Name) { + int fd = open(Path(Name).c_str(), O_CREAT | O_RDWR, 0777); + if (fd < 0) return false; + if (ftruncate(fd, kShmemSize) < 0) return false; + if (!Map(fd)) + return false; + 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) + return false; + } + IAmServer = true; + return true; +} + +bool SharedMemoryRegion::Open(const char *Name) { + int fd = open(Path(Name).c_str(), O_RDWR); + if (fd < 0) return false; + struct stat stat_res; + if (0 != fstat(fd, &stat_res)) + return false; + assert(stat_res.st_size == kShmemSize); + if (!Map(fd)) + return false; + for (int i = 0; i < 2; i++) { + Semaphore[i] = sem_open(SemName(Name, i).c_str(), 0); + if (Semaphore[i] == (void *)-1) + return false; + } + IAmServer = false; + return true; +} + +bool SharedMemoryRegion::Destroy(const char *Name) { + return 0 == unlink(Path(Name).c_str()); +} + +void SharedMemoryRegion::Post(int Idx) { + assert(Idx == 0 || Idx == 1); + sem_post((sem_t*)Semaphore[Idx]); +} + +void SharedMemoryRegion::Wait(int Idx) { + assert(Idx == 0 || Idx == 1); + for (int i = 0; i < 10 && sem_wait((sem_t*)Semaphore[Idx]); i++) { + // sem_wait may fail if interrupted by a signal. + sleep(i); + if (i) + Printf("%s: sem_wait[%d] failed %s\n", i < 9 ? "WARNING" : "ERROR", i, + strerror(errno)); + if (i == 9) abort(); + } +} + +} // namespace fuzzer + +#endif // LIBFUZZER_POSIX diff --git a/lib/Fuzzer/FuzzerShmemWindows.cpp b/lib/Fuzzer/FuzzerShmemWindows.cpp new file mode 100644 index 000000000000..6325b4b8e5b4 --- /dev/null +++ b/lib/Fuzzer/FuzzerShmemWindows.cpp @@ -0,0 +1,64 @@ +//===- FuzzerShmemWindows.cpp - Posix shared memory -------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// SharedMemoryRegion +//===----------------------------------------------------------------------===// +#include "FuzzerDefs.h" +#if LIBFUZZER_WINDOWS + +#include "FuzzerIO.h" +#include "FuzzerShmem.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> + +namespace fuzzer { + +std::string SharedMemoryRegion::Path(const char *Name) { + return DirPlusFile(TmpDir(), Name); +} + +std::string SharedMemoryRegion::SemName(const char *Name, int Idx) { + std::string Res(Name); + return Res + (char)('0' + Idx); +} + +bool SharedMemoryRegion::Map(int fd) { + assert(0 && "UNIMPLEMENTED"); + return false; +} + +bool SharedMemoryRegion::Create(const char *Name) { + assert(0 && "UNIMPLEMENTED"); + return false; +} + +bool SharedMemoryRegion::Open(const char *Name) { + assert(0 && "UNIMPLEMENTED"); + return false; +} + +bool SharedMemoryRegion::Destroy(const char *Name) { + assert(0 && "UNIMPLEMENTED"); + return false; +} + +void SharedMemoryRegion::Post(int Idx) { + assert(0 && "UNIMPLEMENTED"); +} + +void SharedMemoryRegion::Wait(int Idx) { + Semaphore[1] = nullptr; + assert(0 && "UNIMPLEMENTED"); +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS diff --git a/lib/Fuzzer/FuzzerTracePC.cpp b/lib/Fuzzer/FuzzerTracePC.cpp index 39d6e6026210..ce0f7a47eee6 100644 --- a/lib/Fuzzer/FuzzerTracePC.cpp +++ b/lib/Fuzzer/FuzzerTracePC.cpp @@ -18,27 +18,37 @@ #include "FuzzerExtFunctions.h" #include "FuzzerIO.h" #include "FuzzerTracePC.h" +#include "FuzzerUtil.h" #include "FuzzerValueBitMap.h" #include <map> -#include <sanitizer/coverage_interface.h> #include <set> #include <sstream> +// The coverage counters and PCs. +// These are declared as global variables named "__sancov_*" to simplify +// experiments with inlined instrumentation. +alignas(64) ATTRIBUTE_INTERFACE +uint8_t __sancov_trace_pc_guard_8bit_counters[fuzzer::TracePC::kNumPCs]; + +ATTRIBUTE_INTERFACE +uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs]; + namespace fuzzer { TracePC TPC; -void TracePC::HandleTrace(uint32_t *Guard, uintptr_t PC) { - uint32_t Idx = *Guard; - if (!Idx) return; - PCs[Idx % kNumPCs] = PC; - Counters[Idx % kNumCounters]++; +uint8_t *TracePC::Counters() const { + return __sancov_trace_pc_guard_8bit_counters; +} + +uintptr_t *TracePC::PCs() const { + return __sancov_trace_pc_pcs; } size_t TracePC::GetTotalPCCoverage() { size_t Res = 0; - for (size_t i = 1; i < GetNumPCs(); i++) - if (PCs[i]) + for (size_t i = 1, N = GetNumPCs(); i < N; i++) + if (PCs()[i]) Res++; return Res; } @@ -46,8 +56,16 @@ size_t TracePC::GetTotalPCCoverage() { void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) { if (Start == Stop || *Start) return; assert(NumModules < sizeof(Modules) / sizeof(Modules[0])); - for (uint32_t *P = Start; P < Stop; P++) - *P = ++NumGuards; + for (uint32_t *P = Start; P < Stop; P++) { + NumGuards++; + if (NumGuards == kNumPCs) { + RawPrint( + "WARNING: The binary has too many instrumented PCs.\n" + " You may want to reduce the size of the binary\n" + " for more efficient fuzzing and precise coverage data\n"); + } + *P = NumGuards % kNumPCs; + } Modules[NumModules].Start = Start; Modules[NumModules].Stop = Stop; NumModules++; @@ -60,23 +78,12 @@ void TracePC::PrintModuleInfo() { Printf("\n"); } +ATTRIBUTE_NO_SANITIZE_ALL void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) { const uintptr_t kBits = 12; const uintptr_t kMask = (1 << kBits) - 1; uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits); - HandleValueProfile(Idx); -} - -static bool IsInterestingCoverageFile(std::string &File) { - if (File.find("compiler-rt/lib/") != std::string::npos) - return false; // sanitizer internal. - if (File.find("/usr/lib/") != std::string::npos) - return false; - if (File.find("/usr/include/") != std::string::npos) - return false; - if (File == "<null>") - return false; - return true; + ValueProfileMap.AddValueModPrime(Idx); } void TracePC::InitializePrintNewPCs() { @@ -84,16 +91,16 @@ void TracePC::InitializePrintNewPCs() { assert(!PrintedPCs); PrintedPCs = new std::set<uintptr_t>; for (size_t i = 1; i < GetNumPCs(); i++) - if (PCs[i]) - PrintedPCs->insert(PCs[i]); + if (PCs()[i]) + PrintedPCs->insert(PCs()[i]); } void TracePC::PrintNewPCs() { if (!DoPrintNewPCs) return; assert(PrintedPCs); for (size_t i = 1; i < GetNumPCs(); i++) - if (PCs[i] && PrintedPCs->insert(PCs[i]).second) - PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PCs[i]); + if (PCs()[i] && PrintedPCs->insert(PCs()[i]).second) + PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PCs()[i]); } void TracePC::PrintCoverage() { @@ -110,20 +117,21 @@ void TracePC::PrintCoverage() { CoveredLines; Printf("COVERAGE:\n"); for (size_t i = 1; i < GetNumPCs(); i++) { - if (!PCs[i]) continue; - std::string FileStr = DescribePC("%s", PCs[i]); + uintptr_t PC = PCs()[i]; + if (!PC) continue; + std::string FileStr = DescribePC("%s", PC); if (!IsInterestingCoverageFile(FileStr)) continue; - std::string FixedPCStr = DescribePC("%p", PCs[i]); - std::string FunctionStr = DescribePC("%F", PCs[i]); - std::string LineStr = DescribePC("%l", PCs[i]); + std::string FixedPCStr = DescribePC("%p", PC); + std::string FunctionStr = DescribePC("%F", PC); + std::string LineStr = DescribePC("%l", PC); char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++? void *OffsetRaw = nullptr; if (!EF->__sanitizer_get_module_and_offset_for_pc( - reinterpret_cast<void *>(PCs[i]), ModulePathRaw, + reinterpret_cast<void *>(PC), ModulePathRaw, sizeof(ModulePathRaw), &OffsetRaw)) continue; std::string Module = ModulePathRaw; - uintptr_t FixedPC = std::stol(FixedPCStr, 0, 16); + uintptr_t FixedPC = std::stoull(FixedPCStr, 0, 16); uintptr_t PcOffset = reinterpret_cast<uintptr_t>(OffsetRaw); ModuleOffsets[Module] = FixedPC - PcOffset; CoveredPCsPerModule[Module].push_back(PcOffset); @@ -154,8 +162,8 @@ void TracePC::PrintCoverage() { Printf("MODULE_WITH_COVERAGE: %s\n", ModuleName.c_str()); // sancov does not yet fully support DSOs. // std::string Cmd = "sancov -print-coverage-pcs " + ModuleName; - std::string Cmd = "objdump -d " + ModuleName + - " | grep 'call.*__sanitizer_cov_trace_pc_guard' | awk -F: '{print $1}'"; + std::string Cmd = DisassembleCmd(ModuleName) + " | " + + SearchRegexCmd("call.*__sanitizer_cov_trace_pc_guard"); std::string SanCovOutput; if (!ExecuteCommandAndReadOutput(Cmd, &SanCovOutput)) { Printf("INFO: Command failed: %s\n", Cmd.c_str()); @@ -164,7 +172,11 @@ void TracePC::PrintCoverage() { std::istringstream ISS(SanCovOutput); std::string S; while (std::getline(ISS, S, '\n')) { - uintptr_t PcOffset = std::stol(S, 0, 16); + size_t PcOffsetEnd = S.find(':'); + if (PcOffsetEnd == std::string::npos) + continue; + S.resize(PcOffsetEnd); + uintptr_t PcOffset = std::stoull(S, 0, 16); if (!std::binary_search(CoveredOffsets.begin(), CoveredOffsets.end(), PcOffset)) { uintptr_t PC = ModuleOffset + PcOffset; @@ -196,8 +208,19 @@ void TracePC::PrintCoverage() { } } +inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) { + // TODO: this implementation is x86 only. + // see sanitizer_common GetPreviousInstructionPc for full implementation. + return PC - 1; +} + void TracePC::DumpCoverage() { - __sanitizer_dump_coverage(PCs, GetNumPCs()); + if (EF->__sanitizer_dump_coverage) { + std::vector<uintptr_t> PCsCopy(GetNumPCs()); + for (size_t i = 0; i < GetNumPCs(); i++) + PCsCopy[i] = PCs()[i] ? GetPreviousInstructionPc(PCs()[i]) : 0; + EF->__sanitizer_dump_coverage(PCsCopy.data(), PCsCopy.size()); + } } // Value profile. @@ -210,97 +233,118 @@ void TracePC::DumpCoverage() { // For cmp instructions the interesting value is a XOR of the parameters. // The interesting value is mixed up with the PC and is then added to the map. -ATTRIBUTE_NO_SANITIZE_MEMORY +ATTRIBUTE_NO_SANITIZE_ALL void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, - size_t n) { + size_t n, bool StopAtZero) { if (!n) return; - size_t Len = std::min(n, (size_t)32); - const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1); - const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2); - size_t I = 0; - for (; I < Len; I++) - if (A1[I] != A2[I]) - break; - size_t PC = reinterpret_cast<size_t>(caller_pc); - size_t Idx = I; - // if (I < Len) - // Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1; - TPC.HandleValueProfile((PC & 4095) | (Idx << 12)); -} - -ATTRIBUTE_NO_SANITIZE_MEMORY -void TracePC::AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2, - size_t n) { - if (!n) return; - size_t Len = std::min(n, (size_t)32); + size_t Len = std::min(n, Word::GetMaxSize()); const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1); const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2); + uint8_t B1[Word::kMaxSize]; + uint8_t B2[Word::kMaxSize]; + // Copy the data into locals in this non-msan-instrumented function + // to avoid msan complaining further. + size_t Hash = 0; // Compute some simple hash of both strings. + for (size_t i = 0; i < Len; i++) { + B1[i] = A1[i]; + B2[i] = A2[i]; + size_t T = B1[i]; + Hash ^= (T << 8) | B2[i]; + } size_t I = 0; for (; I < Len; I++) - if (A1[I] != A2[I] || A1[I] == 0) + if (B1[I] != B2[I] || (StopAtZero && B1[I] == 0)) break; size_t PC = reinterpret_cast<size_t>(caller_pc); - size_t Idx = I; - // if (I < Len && A1[I]) - // Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1; - TPC.HandleValueProfile((PC & 4095) | (Idx << 12)); + size_t Idx = (PC & 4095) | (I << 12); + ValueProfileMap.AddValue(Idx); + TORCW.Insert(Idx ^ Hash, Word(B1, Len), Word(B2, Len)); } template <class T> -ATTRIBUTE_TARGET_POPCNT -#ifdef __clang__ // g++ can't handle this __attribute__ here :( -__attribute__((always_inline)) -#endif // __clang__ -void TracePC::HandleCmp(void *PC, T Arg1, T Arg2) { - uintptr_t PCuint = reinterpret_cast<uintptr_t>(PC); +ATTRIBUTE_TARGET_POPCNT ALWAYS_INLINE +ATTRIBUTE_NO_SANITIZE_ALL +void TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) { uint64_t ArgXor = Arg1 ^ Arg2; - uint64_t ArgDistance = __builtin_popcountl(ArgXor) + 1; // [1,65] - uintptr_t Idx = ((PCuint & 4095) + 1) * ArgDistance; + uint64_t ArgDistance = __builtin_popcountll(ArgXor) + 1; // [1,65] + uintptr_t Idx = ((PC & 4095) + 1) * ArgDistance; if (sizeof(T) == 4) TORC4.Insert(ArgXor, Arg1, Arg2); else if (sizeof(T) == 8) TORC8.Insert(ArgXor, Arg1, Arg2); - HandleValueProfile(Idx); + ValueProfileMap.AddValue(Idx); } } // namespace fuzzer extern "C" { -__attribute__((visibility("default"))) +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) { - uintptr_t PC = (uintptr_t)__builtin_return_address(0); - fuzzer::TPC.HandleTrace(Guard, PC); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + uint32_t Idx = *Guard; + __sancov_trace_pc_pcs[Idx] = PC; + __sancov_trace_pc_guard_8bit_counters[Idx]++; +} + +// Best-effort support for -fsanitize-coverage=trace-pc, which is available +// in both Clang and GCC. +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +void __sanitizer_cov_trace_pc() { + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + uintptr_t Idx = PC & (((uintptr_t)1 << fuzzer::TracePC::kTracePcBits) - 1); + __sancov_trace_pc_pcs[Idx] = PC; + __sancov_trace_pc_guard_8bit_counters[Idx]++; } -__attribute__((visibility("default"))) +ATTRIBUTE_INTERFACE void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) { fuzzer::TPC.HandleInit(Start, Stop); } -__attribute__((visibility("default"))) +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) { - uintptr_t PC = (uintptr_t)__builtin_return_address(0); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); fuzzer::TPC.HandleCallerCallee(PC, Callee); } -__attribute__((visibility("default"))) +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); } -__attribute__((visibility("default"))) + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); } -__attribute__((visibility("default"))) + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); } -__attribute__((visibility("default"))) + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); } -__attribute__((visibility("default"))) +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { uint64_t N = Cases[0]; uint64_t ValSizeInBits = Cases[1]; @@ -308,7 +352,7 @@ void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { // Skip the most common and the most boring case. if (Vals[N - 1] < 256 && Val < 256) return; - char *PC = (char*)__builtin_return_address(0); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); size_t i; uint64_t Token = 0; for (i = 0; i < N; i++) { @@ -325,17 +369,27 @@ void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { fuzzer::TPC.HandleCmp(PC + i, Token, (uint64_t)(0)); } -__attribute__((visibility("default"))) +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_div4(uint32_t Val) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint32_t)0); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Val, (uint32_t)0); } -__attribute__((visibility("default"))) + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_div8(uint64_t Val) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint64_t)0); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Val, (uint64_t)0); } -__attribute__((visibility("default"))) + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT void __sanitizer_cov_trace_gep(uintptr_t Idx) { - fuzzer::TPC.HandleCmp(__builtin_return_address(0), Idx, (uintptr_t)0); + uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); + fuzzer::TPC.HandleCmp(PC, Idx, (uintptr_t)0); } - } // extern "C" diff --git a/lib/Fuzzer/FuzzerTracePC.h b/lib/Fuzzer/FuzzerTracePC.h index b6b26b6c9af8..6523fa06005c 100644 --- a/lib/Fuzzer/FuzzerTracePC.h +++ b/lib/Fuzzer/FuzzerTracePC.h @@ -13,7 +13,9 @@ #define LLVM_FUZZER_TRACE_PC #include "FuzzerDefs.h" +#include "FuzzerDictionary.h" #include "FuzzerValueBitMap.h" + #include <set> namespace fuzzer { @@ -31,7 +33,8 @@ struct TableOfRecentCompares { struct Pair { T A, B; }; - void Insert(size_t Idx, T Arg1, T Arg2) { + ATTRIBUTE_NO_SANITIZE_ALL + void Insert(size_t Idx, const T &Arg1, const T &Arg2) { Idx = Idx % kSize; Table[Idx].A = Arg1; Table[Idx].B = Arg2; @@ -44,25 +47,23 @@ struct TableOfRecentCompares { class TracePC { public: - static const size_t kFeatureSetSize = ValueBitMap::kNumberOfItems; + static const size_t kNumPCs = 1 << 21; + // How many bits of PC are used from __sanitizer_cov_trace_pc. + static const size_t kTracePcBits = 18; - void HandleTrace(uint32_t *guard, uintptr_t PC); void HandleInit(uint32_t *start, uint32_t *stop); void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee); - void HandleValueProfile(size_t Value) { ValueProfileMap.AddValue(Value); } - template <class T> void HandleCmp(void *PC, T Arg1, T Arg2); + template <class T> void HandleCmp(uintptr_t PC, T Arg1, T Arg2); size_t GetTotalPCCoverage(); void SetUseCounters(bool UC) { UseCounters = UC; } void SetUseValueProfile(bool VP) { UseValueProfile = VP; } void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; } - template <class Callback> size_t CollectFeatures(Callback CB); - bool UpdateValueProfileMap(ValueBitMap *MaxValueProfileMap) { - return UseValueProfile && MaxValueProfileMap->MergeFrom(ValueProfileMap); - } + template <class Callback> void CollectFeatures(Callback CB) const; void ResetMaps() { ValueProfileMap.Reset(); - memset(Counters, 0, sizeof(Counters)); + memset(Counters(), 0, GetNumPCs()); + ClearExtraCounters(); } void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize); @@ -74,22 +75,20 @@ class TracePC { void DumpCoverage(); void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, - size_t n); - void AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2, - size_t n); - - bool UsingTracePcGuard() const {return NumModules; } + size_t n, bool StopAtZero); - static const size_t kTORCSize = 1 << 5; - TableOfRecentCompares<uint32_t, kTORCSize> TORC4; - TableOfRecentCompares<uint64_t, kTORCSize> TORC8; + TableOfRecentCompares<uint32_t, 32> TORC4; + TableOfRecentCompares<uint64_t, 32> TORC8; + TableOfRecentCompares<Word, 32> TORCW; void PrintNewPCs(); void InitializePrintNewPCs(); - size_t GetNumPCs() const { return Min(kNumPCs, NumGuards + 1); } + size_t GetNumPCs() const { + return NumGuards == 0 ? (1 << kTracePcBits) : Min(kNumPCs, NumGuards + 1); + } uintptr_t GetPC(size_t Idx) { assert(Idx < GetNumPCs()); - return PCs[Idx]; + return PCs()[Idx]; } private: @@ -105,51 +104,55 @@ private: size_t NumModules; // linker-initialized. size_t NumGuards; // linker-initialized. - static const size_t kNumCounters = 1 << 14; - alignas(8) uint8_t Counters[kNumCounters]; - - static const size_t kNumPCs = 1 << 24; - uintptr_t PCs[kNumPCs]; + uint8_t *Counters() const; + uintptr_t *PCs() const; std::set<uintptr_t> *PrintedPCs; ValueBitMap ValueProfileMap; }; -template <class Callback> -size_t TracePC::CollectFeatures(Callback CB) { - if (!UsingTracePcGuard()) return 0; - size_t Res = 0; - const size_t Step = 8; - assert(reinterpret_cast<uintptr_t>(Counters) % Step == 0); - size_t N = Min(kNumCounters, NumGuards + 1); - N = (N + Step - 1) & ~(Step - 1); // Round up. - for (size_t Idx = 0; Idx < N; Idx += Step) { - uint64_t Bundle = *reinterpret_cast<uint64_t*>(&Counters[Idx]); - if (!Bundle) continue; - for (size_t i = Idx; i < Idx + Step; i++) { - uint8_t Counter = (Bundle >> ((i - Idx) * 8)) & 0xff; - if (!Counter) continue; - Counters[i] = 0; - unsigned Bit = 0; - /**/ if (Counter >= 128) Bit = 7; - else if (Counter >= 32) Bit = 6; - else if (Counter >= 16) Bit = 5; - else if (Counter >= 8) Bit = 4; - else if (Counter >= 4) Bit = 3; - else if (Counter >= 3) Bit = 2; - else if (Counter >= 2) Bit = 1; - size_t Feature = (i * 8 + Bit); - if (CB(Feature)) - Res++; - } - } +template <class Callback> // void Callback(size_t Idx, uint8_t Value); +ATTRIBUTE_NO_SANITIZE_ALL +void ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End, + size_t FirstFeature, Callback Handle8bitCounter) { + typedef uintptr_t LargeType; + const size_t Step = sizeof(LargeType) / sizeof(uint8_t); + assert(!(reinterpret_cast<uintptr_t>(Begin) % 64)); + for (auto P = Begin; P < End; P += Step) + if (LargeType Bundle = *reinterpret_cast<const LargeType *>(P)) + for (size_t I = 0; I < Step; I++, Bundle >>= 8) + if (uint8_t V = Bundle & 0xff) + Handle8bitCounter(FirstFeature + P - Begin + I, V); +} + +template <class Callback> // bool Callback(size_t Feature) +ATTRIBUTE_NO_SANITIZE_ALL +__attribute__((noinline)) +void TracePC::CollectFeatures(Callback HandleFeature) const { + uint8_t *Counters = this->Counters(); + size_t N = GetNumPCs(); + auto Handle8bitCounter = [&](size_t Idx, uint8_t Counter) { + assert(Counter); + unsigned Bit = 0; + /**/ if (Counter >= 128) Bit = 7; + else if (Counter >= 32) Bit = 6; + else if (Counter >= 16) Bit = 5; + else if (Counter >= 8) Bit = 4; + else if (Counter >= 4) Bit = 3; + else if (Counter >= 3) Bit = 2; + else if (Counter >= 2) Bit = 1; + HandleFeature(Idx * 8 + Bit); + }; + + ForEachNonZeroByte(Counters, Counters + N, 0, Handle8bitCounter); + ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), N * 8, + Handle8bitCounter); + if (UseValueProfile) ValueProfileMap.ForEach([&](size_t Idx) { - if (CB(NumGuards * 8 + Idx)) - Res++; + HandleFeature(N * 8 + Idx); }); - return Res; } extern TracePC TPC; diff --git a/lib/Fuzzer/FuzzerTraceState.cpp b/lib/Fuzzer/FuzzerTraceState.cpp index 2ad9702fab0e..a486223d650c 100644 --- a/lib/Fuzzer/FuzzerTraceState.cpp +++ b/lib/Fuzzer/FuzzerTraceState.cpp @@ -13,7 +13,6 @@ #include "FuzzerInternal.h" #include "FuzzerIO.h" #include "FuzzerMutate.h" -#include "FuzzerRandom.h" #include "FuzzerTracePC.h" #include <algorithm> #include <cstring> @@ -23,19 +22,10 @@ namespace fuzzer { -// For now, very simple: put Size bytes of Data at position Pos. -struct TraceBasedMutation { - uint32_t Pos; - Word W; -}; - // Declared as static globals for faster checks inside the hooks. -static bool RecordingMemcmp = false; static bool RecordingMemmem = false; -static bool DoingMyOwnMemmem = false; -ScopedDoingMyOwnMemmem::ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = true; } -ScopedDoingMyOwnMemmem::~ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = false; } +int ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr; class TraceState { public: @@ -43,64 +33,21 @@ public: const Fuzzer *F) : MD(MD), Options(Options), F(F) {} - void TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1, - const uint8_t *Data2); - - int TryToAddDesiredData(const uint8_t *PresentData, - const uint8_t *DesiredData, size_t DataSize); - void StartTraceRecording() { - if (!Options.UseMemcmp) + if (!Options.UseMemmem) return; - RecordingMemcmp = Options.UseMemcmp; - RecordingMemmem = Options.UseMemmem; - NumMutations = 0; + RecordingMemmem = true; InterestingWords.clear(); MD.ClearAutoDictionary(); } void StopTraceRecording() { - if (!RecordingMemcmp) + if (!RecordingMemmem) return; - RecordingMemcmp = false; - for (size_t i = 0; i < NumMutations; i++) { - auto &M = Mutations[i]; - if (Options.Verbosity >= 2) { - AutoDictUnitCounts[M.W]++; - AutoDictAdds++; - if ((AutoDictAdds & (AutoDictAdds - 1)) == 0) { - typedef std::pair<size_t, Word> CU; - std::vector<CU> CountedUnits; - for (auto &I : AutoDictUnitCounts) - CountedUnits.push_back(std::make_pair(I.second, I.first)); - std::sort(CountedUnits.begin(), CountedUnits.end(), - [](const CU &a, const CU &b) { return a.first > b.first; }); - Printf("AutoDict:\n"); - for (auto &I : CountedUnits) { - Printf(" %zd ", I.first); - PrintASCII(I.second.data(), I.second.size()); - Printf("\n"); - } - } - } - MD.AddWordToAutoDictionary({M.W, M.Pos}); - } for (auto &W : InterestingWords) MD.AddWordToAutoDictionary({W}); } - void AddMutation(uint32_t Pos, uint32_t Size, const uint8_t *Data) { - if (NumMutations >= kMaxMutations) return; - auto &M = Mutations[NumMutations++]; - M.Pos = Pos; - M.W.Set(Data, Size); - } - - void AddMutation(uint32_t Pos, uint32_t Size, uint64_t Data) { - assert(Size <= sizeof(Data)); - AddMutation(Pos, Size, reinterpret_cast<uint8_t*>(&Data)); - } - void AddInterestingWord(const uint8_t *Data, size_t Size) { if (!RecordingMemmem || !F->InFuzzingThread()) return; if (Size <= 1) return; @@ -110,75 +57,14 @@ public: } private: - bool IsTwoByteData(uint64_t Data) { - int64_t Signed = static_cast<int64_t>(Data); - Signed >>= 16; - return Signed == 0 || Signed == -1L; - } - - // We don't want to create too many trace-based mutations as it is both - // expensive and useless. So after some number of mutations is collected, - // start rejecting some of them. The more there are mutations the more we - // reject. - bool WantToHandleOneMoreMutation() { - const size_t FirstN = 64; - // Gladly handle first N mutations. - if (NumMutations <= FirstN) return true; - size_t Diff = NumMutations - FirstN; - size_t DiffLog = sizeof(long) * 8 - __builtin_clzl((long)Diff); - assert(DiffLog > 0 && DiffLog < 64); - bool WantThisOne = MD.GetRand()(1 << DiffLog) == 0; // 1 out of DiffLog. - return WantThisOne; - } - static const size_t kMaxMutations = 1 << 16; - size_t NumMutations; - TraceBasedMutation Mutations[kMaxMutations]; // TODO: std::set is too inefficient, need to have a custom DS here. std::set<Word> InterestingWords; MutationDispatcher &MD; const FuzzingOptions Options; const Fuzzer *F; - std::map<Word, size_t> AutoDictUnitCounts; - size_t AutoDictAdds = 0; }; -int TraceState::TryToAddDesiredData(const uint8_t *PresentData, - const uint8_t *DesiredData, - size_t DataSize) { - if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0; - ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem; - const uint8_t *UnitData; - auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData); - int Res = 0; - const uint8_t *Beg = UnitData; - const uint8_t *End = Beg + UnitSize; - for (const uint8_t *Cur = Beg; Cur < End; Cur++) { - Cur = (uint8_t *)SearchMemory(Cur, End - Cur, PresentData, DataSize); - if (!Cur) - break; - size_t Pos = Cur - Beg; - assert(Pos < UnitSize); - AddMutation(Pos, DataSize, DesiredData); - Res++; - } - return Res; -} - -void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1, - const uint8_t *Data2) { - if (!RecordingMemcmp || !F->InFuzzingThread()) return; - CmpSize = std::min(CmpSize, Word::GetMaxSize()); - int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize); - int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize); - if ((Added1 || Added2) && Options.Verbosity >= 3) { - Printf("MemCmp Added %d%d: ", Added1, Added2); - if (Added1) PrintASCII(Data1, CmpSize); - if (Added2) PrintASCII(Data2, CmpSize); - Printf("\n"); - } -} - static TraceState *TS; void Fuzzer::StartTraceRecording() { @@ -192,7 +78,7 @@ void Fuzzer::StopTraceRecording() { } void Fuzzer::InitializeTraceState() { - if (!Options.UseMemcmp) return; + if (!Options.UseMemmem) return; TS = new TraceState(MD, Options, this); } @@ -202,10 +88,17 @@ static size_t InternalStrnlen(const char *S, size_t MaxLen) { return Len; } +// Finds min of (strlen(S1), strlen(S2)). +// Needed bacause one of these strings may actually be non-zero terminated. +static size_t InternalStrnlen2(const char *S1, const char *S2) { + size_t Len = 0; + for (; S1[Len] && S2[Len]; Len++) {} + return Len; +} + } // namespace fuzzer using fuzzer::TS; -using fuzzer::RecordingMemcmp; extern "C" { @@ -215,62 +108,72 @@ extern "C" { #endif #if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS + +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) { - fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n); - if (!RecordingMemcmp) return; + if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return; if (result == 0) return; // No reason to mutate. if (n <= 1) return; // Not interesting. - TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1), - reinterpret_cast<const uint8_t *>(s2)); + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/false); } +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) { - fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, n); - if (!RecordingMemcmp) return; + if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return; if (result == 0) return; // No reason to mutate. size_t Len1 = fuzzer::InternalStrnlen(s1, n); size_t Len2 = fuzzer::InternalStrnlen(s2, n); n = std::min(n, Len1); n = std::min(n, Len2); if (n <= 1) return; // Not interesting. - TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1), - reinterpret_cast<const uint8_t *>(s2)); + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/true); } + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2, int result) { - fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, 64); - if (!RecordingMemcmp) return; + if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return; if (result == 0) return; // No reason to mutate. - size_t Len1 = strlen(s1); - size_t Len2 = strlen(s2); - size_t N = std::min(Len1, Len2); + size_t N = fuzzer::InternalStrnlen2(s1, s2); if (N <= 1) return; // Not interesting. - TS->TraceMemcmpCallback(N, reinterpret_cast<const uint8_t *>(s1), - reinterpret_cast<const uint8_t *>(s2)); + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, N, /*StopAtZero*/true); } +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; 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; 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; TS->AddInterestingWord(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; TS->AddInterestingWord(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::DoingMyOwnMemmem) return; + if (fuzzer::ScopedDoingMyOwnMemOrStr::DoingMyOwnMemOrStr) return; TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), len2); } diff --git a/lib/Fuzzer/FuzzerUtil.h b/lib/Fuzzer/FuzzerUtil.h index 08058c56e4c5..f84fd9ef0fce 100644 --- a/lib/Fuzzer/FuzzerUtil.h +++ b/lib/Fuzzer/FuzzerUtil.h @@ -67,6 +67,10 @@ inline std::string CloneArgsWithoutX(const std::vector<std::string> &Args, return CloneArgsWithoutX(Args, X, X); } +std::string DisassembleCmd(const std::string &FileName); + +std::string SearchRegexCmd(const std::string &Regex); + } // namespace fuzzer #endif // LLVM_FUZZER_UTIL_H diff --git a/lib/Fuzzer/FuzzerUtilPosix.cpp b/lib/Fuzzer/FuzzerUtilPosix.cpp index e8d48dc81a3b..0161309fbf86 100644 --- a/lib/Fuzzer/FuzzerUtilPosix.cpp +++ b/lib/Fuzzer/FuzzerUtilPosix.cpp @@ -118,6 +118,14 @@ const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, return memmem(Data, DataLen, Patt, PattLen); } +std::string DisassembleCmd(const std::string &FileName) { + return "objdump -d " + FileName; +} + +std::string SearchRegexCmd(const std::string &Regex) { + return "grep '" + Regex + "'"; +} + } // namespace fuzzer #endif // LIBFUZZER_POSIX diff --git a/lib/Fuzzer/FuzzerUtilWindows.cpp b/lib/Fuzzer/FuzzerUtilWindows.cpp index 3ca1f2c8f562..08bb3cf3be15 100644 --- a/lib/Fuzzer/FuzzerUtilWindows.cpp +++ b/lib/Fuzzer/FuzzerUtilWindows.cpp @@ -28,7 +28,7 @@ namespace fuzzer { static const FuzzingOptions* HandlerOpt = nullptr; -LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { +static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: @@ -126,10 +126,7 @@ void SetSignalHandler(const FuzzingOptions& Options) { if (Options.HandleSegv || Options.HandleBus || Options.HandleIll || Options.HandleFpe) - if (!AddVectoredExceptionHandler(1, ExceptionHandler)) { - Printf("libFuzzer: AddVectoredExceptionHandler failed.\n"); - exit(1); - } + SetUnhandledExceptionFilter(ExceptionHandler); if (Options.HandleAbrt) if (SIG_ERR == signal(SIGABRT, CrashHandler)) { @@ -178,6 +175,17 @@ const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, return NULL; } +std::string DisassembleCmd(const std::string &FileName) { + if (ExecuteCommand("dumpbin /summary > nul") == 0) + return "dumpbin /disasm " + FileName; + Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n"); + exit(1); +} + +std::string SearchRegexCmd(const std::string &Regex) { + return "findstr /r \"" + Regex + "\""; +} + } // namespace fuzzer #endif // LIBFUZZER_WINDOWS diff --git a/lib/Fuzzer/FuzzerValueBitMap.h b/lib/Fuzzer/FuzzerValueBitMap.h index 0692acd13ee3..8f7ff74300f4 100644 --- a/lib/Fuzzer/FuzzerValueBitMap.h +++ b/lib/Fuzzer/FuzzerValueBitMap.h @@ -18,19 +18,20 @@ namespace fuzzer { // A bit map containing kMapSizeInWords bits. struct ValueBitMap { - static const size_t kMapSizeInBits = 65371; // Prime. - static const size_t kMapSizeInBitsAligned = 65536; // 2^16 + static const size_t kMapSizeInBits = 1 << 16; + static const size_t kMapPrimeMod = 65371; // Largest Prime < kMapSizeInBits; static const size_t kBitsInWord = (sizeof(uintptr_t) * 8); - static const size_t kMapSizeInWords = kMapSizeInBitsAligned / kBitsInWord; + static const size_t kMapSizeInWords = kMapSizeInBits / kBitsInWord; public: - static const size_t kNumberOfItems = kMapSizeInBits; + // Clears all bits. void Reset() { memset(Map, 0, sizeof(Map)); } // Computes a hash function of Value and sets the corresponding bit. // Returns true if the bit was changed from 0 to 1. + ATTRIBUTE_NO_SANITIZE_ALL inline bool AddValue(uintptr_t Value) { - uintptr_t Idx = Value < kMapSizeInBits ? Value : Value % kMapSizeInBits; + uintptr_t Idx = Value % kMapSizeInBits; uintptr_t WordIdx = Idx / kBitsInWord; uintptr_t BitIdx = Idx % kBitsInWord; uintptr_t Old = Map[WordIdx]; @@ -39,6 +40,11 @@ struct ValueBitMap { return New != Old; } + ATTRIBUTE_NO_SANITIZE_ALL + inline bool AddValueModPrime(uintptr_t Value) { + return AddValue(Value % kMapPrimeMod); + } + inline bool Get(uintptr_t Idx) { assert(Idx < kMapSizeInBits); uintptr_t WordIdx = Idx / kBitsInWord; @@ -62,14 +68,15 @@ struct ValueBitMap { Other.Map[i] = 0; } if (M) - Res += __builtin_popcountl(M); + Res += __builtin_popcountll(M); } NumBits = Res; return OldNumBits < NumBits; } template <class Callback> - void ForEach(Callback CB) { + ATTRIBUTE_NO_SANITIZE_ALL + void ForEach(Callback CB) const { for (size_t i = 0; i < kMapSizeInWords; i++) if (uintptr_t M = Map[i]) for (size_t j = 0; j < sizeof(M) * 8; j++) diff --git a/lib/Fuzzer/afl/afl_driver.cpp b/lib/Fuzzer/afl/afl_driver.cpp index fc9589552ba3..b3a54e57fceb 100644 --- a/lib/Fuzzer/afl/afl_driver.cpp +++ b/lib/Fuzzer/afl/afl_driver.cpp @@ -238,6 +238,13 @@ static void maybe_duplicate_stderr() { } } +// Define LLVMFuzzerMutate to avoid link failures for targets that use it +// with libFuzzer's LLVMFuzzerCustomMutator. +extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { + assert(false && "LLVMFuzzerMutate should not be called from afl_driver"); + return 0; +} + int main(int argc, char **argv) { fprintf(stderr, "======================= INFO =========================\n" "This binary is built for AFL-fuzz.\n" diff --git a/lib/Fuzzer/build.sh b/lib/Fuzzer/build.sh index 27c148ad43db..4556af5daf7d 100755 --- a/lib/Fuzzer/build.sh +++ b/lib/Fuzzer/build.sh @@ -1,7 +1,8 @@ #!/bin/bash LIBFUZZER_SRC_DIR=$(dirname $0) +CXX="${CXX:-clang}" for f in $LIBFUZZER_SRC_DIR/*.cpp; do - clang -g -O2 -fno-omit-frame-pointer -std=c++11 $f -c & + $CXX -g -O2 -fno-omit-frame-pointer -std=c++11 $f -c & done wait rm -f libFuzzer.a diff --git a/lib/Fuzzer/test/AbsNegAndConstant64Test.cpp b/lib/Fuzzer/test/AbsNegAndConstant64Test.cpp index 577481431ae2..69b0d59fb8ef 100644 --- a/lib/Fuzzer/test/AbsNegAndConstant64Test.cpp +++ b/lib/Fuzzer/test/AbsNegAndConstant64Test.cpp @@ -14,7 +14,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { uint64_t y; memcpy(&x, Data, sizeof(x)); memcpy(&y, Data + sizeof(x), sizeof(y)); - if (labs(x) < 0 && y == 0xbaddcafedeadbeefUL) { + if (llabs(x) < 0 && y == 0xbaddcafedeadbeefULL) { printf("BINGO; Found the target, exiting; x = 0x%lx y 0x%lx\n", x, y); exit(1); } diff --git a/lib/Fuzzer/test/BadStrcmpTest.cpp b/lib/Fuzzer/test/BadStrcmpTest.cpp new file mode 100644 index 000000000000..159cd7ea5f70 --- /dev/null +++ b/lib/Fuzzer/test/BadStrcmpTest.cpp @@ -0,0 +1,19 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test that we don't creash in case of bad strcmp params. +#include <cstdint> +#include <cstring> +#include <cstddef> + +static volatile int Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size != 10) return 0; + // Data is not zero-terminated, so this call is bad. + // Still, there are cases when such calles appear, see e.g. + // https://bugs.llvm.org/show_bug.cgi?id=32357 + Sink = strcmp(reinterpret_cast<const char*>(Data), "123456789"); + return 0; +} + diff --git a/lib/Fuzzer/test/BogusInitializeTest.cpp b/lib/Fuzzer/test/BogusInitializeTest.cpp new file mode 100644 index 000000000000..c7e81a5478b2 --- /dev/null +++ b/lib/Fuzzer/test/BogusInitializeTest.cpp @@ -0,0 +1,15 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Make sure LLVMFuzzerInitialize does not change argv[0]. +#include <stddef.h> +#include <stdint.h> + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + ***argv = 'X'; + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + return 0; +} diff --git a/lib/Fuzzer/test/CMakeLists.txt b/lib/Fuzzer/test/CMakeLists.txt index c0457746a0e7..f72bc3909a3c 100644 --- a/lib/Fuzzer/test/CMakeLists.txt +++ b/lib/Fuzzer/test/CMakeLists.txt @@ -11,21 +11,35 @@ set(variables_to_filter LIBFUZZER_FLAGS_BASE ) foreach (VARNAME ${variables_to_filter}) - string(REPLACE " " ";" BUILD_FLAGS_AS_LIST "${${VARNAME}}") - set(new_flags "") - foreach (flag ${BUILD_FLAGS_AS_LIST}) - # NOTE: Use of XX here is to avoid a CMake warning due to CMP0054 - if (NOT ("XX${flag}" MATCHES "XX-O[0123s]")) - set(new_flags "${new_flags} ${flag}") - else() - set(new_flags "${new_flags} -O0") - endif() - endforeach() - set(${VARNAME} "${new_flags}") + string(REGEX REPLACE "([-/]O)[123s]" "\\10" ${VARNAME} "${${VARNAME}}") endforeach() # Enable the coverage instrumentation (it is disabled for the Fuzzer lib). -set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp,trace-div,trace-gep -g") +set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp,trace-div,trace-gep -gline-tables-only") + +if(MSVC) + # For tests use the CRT specified for release build + # (asan doesn't support MDd and MTd) + if ("${LLVM_USE_CRT_RELEASE}" STREQUAL "") + set(CRT_FLAG " /MD ") + else() + set(CRT_FLAG " /${LLVM_USE_CRT_RELEASE} ") + endif() + # In order to use the sanitizers in Windows, we need to link against many + # runtime libraries which will depend on the target being created + # (executable or dll) and the c runtime library used (MT/MD). + # By default, cmake uses link.exe for linking, which fails because we don't + # specify the appropiate dependencies. + # As we don't want to consider all of that possible situations which depends + # on the implementation of the compiler-rt, the simplest option is to change + # the rules for linking executables and shared libraries, using the compiler + # instead of link.exe. Clang will consider the sanitizer flags, and + # automatically provide the required libraries to the linker. + set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> ${CMAKE_CXX_FLAGS} ${CRT_FLAG} <OBJECTS> -o <TARGET> <LINK_LIBRARIES> /link <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS>") + set(CMAKE_CXX_CREATE_SHARED_LIBRARY "<CMAKE_CXX_COMPILER> ${CMAKE_CXX_FLAGS} ${CRT_FLAG} /LD <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG> <TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES> /link <LINK_FLAGS>") +endif() + +add_custom_target(TestBinaries) # add_libfuzzer_test(<name> # SOURCES source0.cpp [source1.cpp ...] @@ -51,12 +65,9 @@ function(add_libfuzzer_test name) PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/Fuzzer/test" ) - set(TestBinaries ${TestBinaries} LLVMFuzzer-${name} PARENT_SCOPE) + add_dependencies(TestBinaries LLVMFuzzer-${name}) endfunction() -# Variable to keep track of all test targets -set(TestBinaries) - ############################################################################### # Basic tests ############################################################################### @@ -65,16 +76,23 @@ set(Tests AbsNegAndConstantTest AbsNegAndConstant64Test AccumulateAllocationsTest + BadStrcmpTest + BogusInitializeTest BufferOverflowOnInput CallerCalleeTest CounterTest + CustomCrossOverAndMutateTest CustomCrossOverTest CustomMutatorTest + CxxStringEqTest DivTest EmptyTest + EquivalenceATest + EquivalenceBTest FourIndependentBranchesTest FullCoverageSetTest InitializeTest + Memcmp64BytesTest MemcmpTest LeakTest LeakTimeoutTest @@ -92,6 +110,7 @@ set(Tests SimpleHashTest SimpleTest SimpleThreadedTest + SingleByteInputTest SingleMemcmpTest SingleStrcmpTest SingleStrncmpTest @@ -105,17 +124,19 @@ set(Tests SwapCmpTest SwitchTest Switch2Test + TableLookupTest ThreadedLeakTest ThreadedTest TimeoutTest TimeoutEmptyTest TraceMallocTest + TwoDifferentBugsTest ) -if(APPLE) - # LeakSanitizer is not supported on OSX right now +if(APPLE OR MSVC) + # LeakSanitizer is not supported on OSX and Windows right now set(HAS_LSAN 0) - message(WARNING "LeakSanitizer is not supported on Apple platforms." + message(WARNING "LeakSanitizer is not supported." " Building and running LibFuzzer LeakSanitizer tests is disabled." ) else() @@ -126,6 +147,17 @@ foreach(Test ${Tests}) add_libfuzzer_test(${Test} SOURCES ${Test}.cpp) endforeach() +function(test_export_symbol target symbol) + if(MSVC) + set_target_properties(LLVMFuzzer-${target} PROPERTIES LINK_FLAGS + "-export:${symbol}") + endif() +endfunction() + +test_export_symbol(InitializeTest "LLVMFuzzerInitialize") +test_export_symbol(BogusInitializeTest "LLVMFuzzerInitialize") +test_export_symbol(CustomCrossOverTest "LLVMFuzzerCustomCrossOver") +test_export_symbol(CustomMutatorTest "LLVMFuzzerCustomMutator") ############################################################################### # Unit tests @@ -150,13 +182,13 @@ target_include_directories(LLVMFuzzer-Unittest PRIVATE "${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include" ) -set(TestBinaries ${TestBinaries} LLVMFuzzer-Unittest) +add_dependencies(TestBinaries LLVMFuzzer-Unittest) set_target_properties(LLVMFuzzer-Unittest PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) -set(TestBinaries ${TestBinaries} LLVMFuzzer-StandaloneInitializeTest) +add_dependencies(TestBinaries LLVMFuzzer-StandaloneInitializeTest) set_target_properties(LLVMFuzzer-StandaloneInitializeTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" @@ -170,6 +202,7 @@ include_directories(..) # add_subdirectory(uninstrumented) add_subdirectory(no-coverage) +add_subdirectory(trace-pc) add_subdirectory(ubsan) add_library(LLVMFuzzer-DSO1 SHARED DSO1.cpp) @@ -187,12 +220,22 @@ target_link_libraries(LLVMFuzzer-DSOTest set_target_properties(LLVMFuzzer-DSOTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/Fuzzer/test") -set_target_properties(LLVMFuzzer-DSO1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib") -set_target_properties(LLVMFuzzer-DSO2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib") -set(TestBinaries ${TestBinaries} LLVMFuzzer-DSOTest) +if(MSVC) + set_output_directory(LLVMFuzzer-DSO1 + BINARY_DIR "${CMAKE_BINARY_DIR}/lib/Fuzzer/test" + LIBRARY_DIR "${CMAKE_BINARY_DIR}/lib/Fuzzer/test") + set_output_directory(LLVMFuzzer-DSO2 + BINARY_DIR "${CMAKE_BINARY_DIR}/lib/Fuzzer/test" + LIBRARY_DIR "${CMAKE_BINARY_DIR}/lib/Fuzzer/test") +else(MSVC) + set_output_directory(LLVMFuzzer-DSO1 + LIBRARY_DIR "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib") + set_output_directory(LLVMFuzzer-DSO2 + LIBRARY_DIR "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib") +endif() + +add_dependencies(TestBinaries LLVMFuzzer-DSOTest) ############################################################################### # Configure lit to run the tests @@ -200,6 +243,10 @@ set(TestBinaries ${TestBinaries} LLVMFuzzer-DSOTest) # Note this is done after declaring all tests so we can inform lit if any tests # need to be disabled. ############################################################################### +set(LIBFUZZER_POSIX 1) +if (MSVC) + set(LIBFUZZER_POSIX 0) +endif() configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in @@ -213,5 +260,11 @@ configure_lit_site_cfg( add_lit_testsuite(check-fuzzer "Running Fuzzer tests" ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ${TestBinaries} FileCheck not + DEPENDS TestBinaries ) + +# Don't add dependencies on Windows. The linker step would fail on Windows, +# since cmake will use link.exe for linking and won't include compiler-rt libs. +if(NOT MSVC) + add_dependencies(check-fuzzer FileCheck sancov not) +endif() diff --git a/lib/Fuzzer/test/CustomCrossOverAndMutateTest.cpp b/lib/Fuzzer/test/CustomCrossOverAndMutateTest.cpp new file mode 100644 index 000000000000..74fc939534ca --- /dev/null +++ b/lib/Fuzzer/test/CustomCrossOverAndMutateTest.cpp @@ -0,0 +1,34 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test that libFuzzer does not crash when LLVMFuzzerMutate called from +// LLVMFuzzerCustomCrossOver. +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <string.h> +#include <string> +#include <vector> + +#include "FuzzerInterface.h" + +static volatile int sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + std::string Str(reinterpret_cast<const char *>(Data), Size); + if (Size && Data[0] == '0') + sink++; + return 0; +} + +extern "C" 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) { + std::vector<uint8_t> Buffer(MaxOutSize * 10); + LLVMFuzzerMutate(Buffer.data(), Buffer.size(), Buffer.size()); + size_t Size = std::min(Size1, MaxOutSize); + memcpy(Out, Data1, Size); + return Size; +} diff --git a/lib/Fuzzer/test/CxxStringEqTest.cpp b/lib/Fuzzer/test/CxxStringEqTest.cpp new file mode 100644 index 000000000000..e0e23c972ccb --- /dev/null +++ b/lib/Fuzzer/test/CxxStringEqTest.cpp @@ -0,0 +1,25 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. Must find a specific string +// used in std::string operator ==. +#include <cstdint> +#include <cstdlib> +#include <cstddef> +#include <string> +#include <iostream> + +static volatile int Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + std::string Str((const char*)Data, Size); + bool Eq = Str == "FooBar"; + Sink = Str == "123456"; // Try to confuse the fuzzer + if (Eq) { + std::cout << "BINGO; Found the target, exiting\n"; + std::cout.flush(); + abort(); + } + return 0; +} + diff --git a/lib/Fuzzer/test/DSO1.cpp b/lib/Fuzzer/test/DSO1.cpp index 4a293890f4b0..72a5ec4a0cde 100644 --- a/lib/Fuzzer/test/DSO1.cpp +++ b/lib/Fuzzer/test/DSO1.cpp @@ -2,7 +2,9 @@ // License. See LICENSE.TXT for details. // Source code for a simple DSO. - +#ifdef _WIN32 +__declspec( dllexport ) +#endif int DSO1(int a) { if (a < 123456) return 0; diff --git a/lib/Fuzzer/test/DSO2.cpp b/lib/Fuzzer/test/DSO2.cpp index 04b308d193ac..2967055dc227 100644 --- a/lib/Fuzzer/test/DSO2.cpp +++ b/lib/Fuzzer/test/DSO2.cpp @@ -2,7 +2,9 @@ // License. See LICENSE.TXT for details. // Source code for a simple DSO. - +#ifdef _WIN32 +__declspec( dllexport ) +#endif int DSO2(int a) { if (a < 3598235) return 0; diff --git a/lib/Fuzzer/test/EquivalenceATest.cpp b/lib/Fuzzer/test/EquivalenceATest.cpp new file mode 100644 index 000000000000..7d1ebb0f6a4a --- /dev/null +++ b/lib/Fuzzer/test/EquivalenceATest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> + +// Test for libFuzzer's "equivalence" fuzzing, part A. +extern "C" void LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + // fprintf(stderr, "A %zd\n", Size); + uint8_t Result[50]; + if (Size > 50) Size = 50; + for (size_t i = 0; i < Size; i++) + Result[Size - i - 1] = Data[i]; + LLVMFuzzerAnnounceOutput(Result, Size); + return 0; +} diff --git a/lib/Fuzzer/test/EquivalenceBTest.cpp b/lib/Fuzzer/test/EquivalenceBTest.cpp new file mode 100644 index 000000000000..b1de208b57f6 --- /dev/null +++ b/lib/Fuzzer/test/EquivalenceBTest.cpp @@ -0,0 +1,27 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> + +// Test for libFuzzer's "equivalence" fuzzing, part B. +extern "C" void LLVMFuzzerAnnounceOutput(const uint8_t *Data, size_t Size); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + // fprintf(stderr, "B %zd\n", Size); + uint8_t Result[50]; + if (Size > 50) Size = 50; + for (size_t i = 0; i < Size; i++) + Result[Size - i - 1] = Data[i]; + + // Be a bit different from EquivalenceATest + if (Size > 10 && Data[5] == 'B' && Data[6] == 'C' && Data[7] == 'D') { + static int c; + if (!c) + fprintf(stderr, "ZZZZZZZ\n"); + c = 1; + Result[2]++; + } + + LLVMFuzzerAnnounceOutput(Result, Size); + return 0; +} diff --git a/lib/Fuzzer/test/FuzzerUnittest.cpp b/lib/Fuzzer/test/FuzzerUnittest.cpp index 4992ef57b6ca..78ea874f2ce2 100644 --- a/lib/Fuzzer/test/FuzzerUnittest.cpp +++ b/lib/Fuzzer/test/FuzzerUnittest.cpp @@ -10,10 +10,12 @@ #include "FuzzerDictionary.h" #include "FuzzerMerge.h" #include "FuzzerMutate.h" +#include "FuzzerTracePC.h" #include "FuzzerRandom.h" #include "gtest/gtest.h" #include <memory> #include <set> +#include <sstream> using namespace fuzzer; @@ -584,15 +586,15 @@ TEST(FuzzerUtil, Base64) { TEST(Corpus, Distribution) { Random Rand(0); - InputCorpus C(""); + 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) }, 0); + C->AddToCorpus(Unit{ static_cast<uint8_t>(i) }, 0); std::vector<size_t> Hist(N); for (size_t i = 0; i < N * TriesPerUnit; i++) { - Hist[C.ChooseUnitIdxToMutate(Rand)]++; + Hist[C->ChooseUnitIdxToMutate(Rand)]++; } for (size_t i = 0; i < N; i++) { // A weak sanity check that every unit gets invoked. @@ -636,7 +638,10 @@ static void Merge(const std::string &Input, Merger M; std::vector<std::string> NewFiles; EXPECT_TRUE(M.Parse(Input, true)); + std::stringstream SS; + M.PrintSummary(SS); EXPECT_EQ(NumNewFeatures, M.Merge(&NewFiles)); + EXPECT_EQ(M.AllFeatures(), M.ParseSummary(SS)); EQ(NewFiles, Result); } @@ -706,6 +711,16 @@ TEST(Merge, Good) { EQ(M.Files[2].Features, {1, 3, 6}); EXPECT_EQ(3U, M.Merge(&NewFiles)); EQ(NewFiles, {"B"}); + + // Same as the above, but with InitialFeatures. + EXPECT_TRUE(M.Parse("2\n0\nB\nC\n" + "STARTED 0 1001\nDONE 0 4 5 6 \n" + "STARTED 1 1002\nDONE 1 6 1 3\n" + "", true)); + EQ(M.Files[0].Features, {4, 5, 6}); + EQ(M.Files[1].Features, {1, 3, 6}); + EXPECT_EQ(3U, M.Merge({1, 2, 3}, &NewFiles)); + EQ(NewFiles, {"B"}); } TEST(Merge, Merge) { @@ -736,3 +751,25 @@ TEST(Merge, Merge) { "STARTED 3 1000\nDONE 3 1 \n", {"B", "D"}, 3); } + +TEST(Fuzzer, ForEachNonZeroByte) { + const size_t N = 64; + alignas(64) uint8_t Ar[N + 8] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 0, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 7, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 8, + 9, 9, 9, 9, 9, 9, 9, 9, + }; + typedef std::vector<std::pair<size_t, uint8_t> > Vec; + Vec Res, Expected; + auto CB = [&](size_t Idx, uint8_t V) { Res.push_back({Idx, V}); }; + ForEachNonZeroByte(Ar, Ar + N, 100, CB); + Expected = {{108, 1}, {109, 2}, {118, 3}, {120, 4}, + {135, 5}, {137, 6}, {146, 7}, {163, 8}}; + EXPECT_EQ(Res, Expected); +} diff --git a/lib/Fuzzer/test/LargeTest.cpp b/lib/Fuzzer/test/LargeTest.cpp new file mode 100644 index 000000000000..83ed61971801 --- /dev/null +++ b/lib/Fuzzer/test/LargeTest.cpp @@ -0,0 +1,37 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// A fuzz target with lots of edges. +#include <cstdint> +#include <cstdlib> + +static inline void break_optimization(const void *arg) { + __asm__ __volatile__("" : : "r" (arg) : "memory"); +} + +#define A \ + do { \ + i++; \ + c++; \ + if (Data[(i + __LINE__) % Size] == (c % 256)) \ + break_optimization(Data); \ + else \ + break_optimization(0); \ + } while (0) + +// for (int i = 0, n = Data[(__LINE__ - 1) % Size] % 16; i < n; i++) + +#define B do{A; A; A; A; A; A; A; A; A; A; A; A; A; A; A; A; A; A; }while(0) +#define C do{B; B; B; B; B; B; B; B; B; B; B; B; B; B; B; B; B; B; }while(0) +#define D do{C; C; C; C; C; C; C; C; C; C; C; C; C; C; C; C; C; C; }while(0) +#define E do{D; D; D; D; D; D; D; D; D; D; D; D; D; D; D; D; D; D; }while(0) + +volatile int sink; +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (!Size) return 0; + int c = 0; + int i = 0; + D; + return 0; +} + diff --git a/lib/Fuzzer/test/LoadTest.cpp b/lib/Fuzzer/test/LoadTest.cpp index c1780d5c7bd9..eef16c7be51e 100644 --- a/lib/Fuzzer/test/LoadTest.cpp +++ b/lib/Fuzzer/test/LoadTest.cpp @@ -14,7 +14,7 @@ int array[kArraySize]; extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { if (Size < 8) return 0; - size_t a = 0; + uint64_t a = 0; memcpy(&a, Data, 8); Sink = array[a % (kArraySize + 1)]; return 0; diff --git a/lib/Fuzzer/test/Memcmp64BytesTest.cpp b/lib/Fuzzer/test/Memcmp64BytesTest.cpp new file mode 100644 index 000000000000..e81526b578a3 --- /dev/null +++ b/lib/Fuzzer/test/Memcmp64BytesTest.cpp @@ -0,0 +1,20 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. The fuzzer must find a particular string. +#include <cassert> +#include <cstring> +#include <cstdint> +#include <cstdio> +#include <cstdlib> + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + const char kString64Bytes[] = + "123456789 123456789 123456789 123456789 123456789 123456789 1234"; + assert(sizeof(kString64Bytes) == 65); + if (Size >= 64 && memcmp(Data, kString64Bytes, 64) == 0) { + fprintf(stderr, "BINGO\n"); + exit(1); + } + return 0; +} diff --git a/lib/Fuzzer/test/UninstrumentedTest.cpp b/lib/Fuzzer/test/NotinstrumentedTest.cpp index ffe952c749d2..ffe952c749d2 100644 --- a/lib/Fuzzer/test/UninstrumentedTest.cpp +++ b/lib/Fuzzer/test/NotinstrumentedTest.cpp diff --git a/lib/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp b/lib/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp index ea23a601aa23..316b7682b8e6 100644 --- a/lib/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp +++ b/lib/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp @@ -15,7 +15,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { if (Size > 0 && Data[0] == 'H') { if (Size > 1 && Data[1] == 'i') { if (Size > 2 && Data[2] == '!') { - size_t kSize = 0xff000000U; + size_t kSize = 0x20000000U; char *p = new char[kSize]; SinkPtr = p; delete [] p; diff --git a/lib/Fuzzer/test/RepeatedMemcmp.cpp b/lib/Fuzzer/test/RepeatedMemcmp.cpp index a327bbee7815..7377f65ed76d 100644 --- a/lib/Fuzzer/test/RepeatedMemcmp.cpp +++ b/lib/Fuzzer/test/RepeatedMemcmp.cpp @@ -8,13 +8,16 @@ #include <cstdlib> extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - int Matches = 0; - for (size_t i = 0; i + 2 < Size; i += 3) { - const char *Pat = i % 2 ? "foo" : "bar"; - if (!memcmp(Data + i, Pat, 3)) - Matches++; - } - if (Matches > 20) { + int Matches1 = 0; + for (size_t i = 0; i + 2 < Size; i += 3) + if (!memcmp(Data + i, "foo", 3)) + Matches1++; + int Matches2 = 0; + for (size_t i = 0; i + 2 < Size; i += 3) + if (!memcmp(Data + i, "bar", 3)) + Matches2++; + + if (Matches1 > 10 && Matches2 > 10) { fprintf(stderr, "BINGO!\n"); exit(1); } diff --git a/lib/Fuzzer/test/SimpleCmpTest.cpp b/lib/Fuzzer/test/SimpleCmpTest.cpp index 0220c30f9a6b..12b5cdda0660 100644 --- a/lib/Fuzzer/test/SimpleCmpTest.cpp +++ b/lib/Fuzzer/test/SimpleCmpTest.cpp @@ -26,12 +26,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { memcpy(&y, Data + 8, 8); // 16 memcpy(&z, Data + 16, sizeof(z)); // 20 memcpy(&a, Data + 20, sizeof(a)); // 22 + const bool k32bit = sizeof(void*) == 4; - if (x > 1234567890 && PrintOnce(__LINE__) && - x < 1234567895 && PrintOnce(__LINE__) && + if ((k32bit || x > 1234567890) && PrintOnce(__LINE__) && + (k32bit || x < 1234567895) && PrintOnce(__LINE__) && a == 0x4242 && PrintOnce(__LINE__) && - y >= 987654321 && PrintOnce(__LINE__) && - y <= 987654325 && PrintOnce(__LINE__) && + (k32bit || y >= 987654321) && PrintOnce(__LINE__) && + (k32bit || y <= 987654325) && PrintOnce(__LINE__) && z < -10000 && PrintOnce(__LINE__) && z >= -10005 && PrintOnce(__LINE__) && z != -10003 && PrintOnce(__LINE__) && diff --git a/lib/Fuzzer/test/SingleByteInputTest.cpp b/lib/Fuzzer/test/SingleByteInputTest.cpp new file mode 100644 index 000000000000..4ce819d230ce --- /dev/null +++ b/lib/Fuzzer/test/SingleByteInputTest.cpp @@ -0,0 +1,17 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer, need just one byte to crash. +#include <cstdint> +#include <cstdlib> +#include <cstddef> +#include <cstdio> + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size > 0 && Data[Size/2] == 42) { + fprintf(stderr, "BINGO\n"); + abort(); + } + return 0; +} + diff --git a/lib/Fuzzer/test/SingleStrcmpTest.cpp b/lib/Fuzzer/test/SingleStrcmpTest.cpp index 73470b527eeb..48f481dfc51a 100644 --- a/lib/Fuzzer/test/SingleStrcmpTest.cpp +++ b/lib/Fuzzer/test/SingleStrcmpTest.cpp @@ -8,10 +8,14 @@ #include <cstdlib> extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - char *S = (char*)Data; - if (Size >= 7 && !strcmp(S, "qwerty")) { - fprintf(stderr, "BINGO\n"); - exit(1); + if (Size >= 7) { + char Copy[7]; + memcpy(Copy, Data, 6); + Copy[6] = 0; + if (!strcmp(Copy, "qwerty")) { + fprintf(stderr, "BINGO\n"); + exit(1); + } } return 0; } diff --git a/lib/Fuzzer/test/SingleStrncmpTest.cpp b/lib/Fuzzer/test/SingleStrncmpTest.cpp index dbcc464b0a78..e5601da86329 100644 --- a/lib/Fuzzer/test/SingleStrncmpTest.cpp +++ b/lib/Fuzzer/test/SingleStrncmpTest.cpp @@ -9,7 +9,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { char *S = (char*)Data; - if (Size >= 6 && !strncmp(S, "qwerty", 6)) { + volatile auto Strncmp = &(strncmp); // Make sure strncmp is not inlined. + if (Size >= 6 && !Strncmp(S, "qwerty", 6)) { fprintf(stderr, "BINGO\n"); exit(1); } diff --git a/lib/Fuzzer/test/SwapCmpTest.cpp b/lib/Fuzzer/test/SwapCmpTest.cpp index f79db4ccf714..b90ac72c22c4 100644 --- a/lib/Fuzzer/test/SwapCmpTest.cpp +++ b/lib/Fuzzer/test/SwapCmpTest.cpp @@ -19,8 +19,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { x = __builtin_bswap64(x); y = __builtin_bswap32(y); z = __builtin_bswap16(z); + const bool k32bit = sizeof(void*) == 4; - if (x == 0x46555A5A5A5A5546ULL && + if ((k32bit || x == 0x46555A5A5A5A5546ULL) && z == 0x4F4B && y == 0x66757A7A && true diff --git a/lib/Fuzzer/test/TableLookupTest.cpp b/lib/Fuzzer/test/TableLookupTest.cpp new file mode 100644 index 000000000000..f9d5610820ff --- /dev/null +++ b/lib/Fuzzer/test/TableLookupTest.cpp @@ -0,0 +1,45 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Make sure the fuzzer eventually finds all possible values of a variable +// within a range. +#include <cstring> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <cassert> +#include <set> + +const size_t N = 1 << 12; + +// Define an array of counters that will be understood by libFuzzer +// as extra coverage signal. The array must be: +// * uint8_t +// * aligned by 64 +// * in the section named __libfuzzer_extra_counters. +// The target code may declare more than one such array. +// +// Use either `Counters[Idx] = 1` or `Counters[Idx]++;` +// depending on whether multiple occurrences of the event 'Idx' +// is important to distinguish from one occurrence. +#ifdef __linux__ +alignas(64) __attribute__((section("__libfuzzer_extra_counters"))) +#endif +static uint8_t Counters[N]; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + static std::set<uint16_t> SeenIdx; + if (Size != 4) return 0; + uint32_t Idx; + memcpy(&Idx, Data, 4); + Idx %= N; + assert(Counters[Idx] == 0); // libFuzzer should reset these between the runs. + // Or Counters[Idx]=1 if we don't care how many times this happened. + Counters[Idx]++; + SeenIdx.insert(Idx); + if (SeenIdx.size() == N) { + fprintf(stderr, "BINGO: found all values\n"); + abort(); + } + return 0; +} diff --git a/lib/Fuzzer/test/TwoDifferentBugsTest.cpp b/lib/Fuzzer/test/TwoDifferentBugsTest.cpp new file mode 100644 index 000000000000..42c0d192ba86 --- /dev/null +++ b/lib/Fuzzer/test/TwoDifferentBugsTest.cpp @@ -0,0 +1,22 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Simple test for a fuzzer. This test may trigger two different bugs. +#include <cstdint> +#include <cstdlib> +#include <cstddef> +#include <iostream> + +static volatile int *Null = 0; + +void Foo() { Null[1] = 0; } +void Bar() { Null[2] = 0; } + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 10 && Data[0] == 'H') + Foo(); + if (Size >= 10 && Data[0] == 'H') + Bar(); + return 0; +} + diff --git a/lib/Fuzzer/test/afl-driver-extra-stats.test b/lib/Fuzzer/test/afl-driver-extra-stats.test index 81e384e7dad2..1b0818e55ea5 100644 --- a/lib/Fuzzer/test/afl-driver-extra-stats.test +++ b/lib/Fuzzer/test/afl-driver-extra-stats.test @@ -1,3 +1,5 @@ +REQUIRES: posix + ; Test that not specifying an extra stats file isn't broken. RUN: unset AFL_DRIVER_EXTRA_STATS_FILENAME RUN: AFLDriverTest diff --git a/lib/Fuzzer/test/afl-driver-stderr.test b/lib/Fuzzer/test/afl-driver-stderr.test index c0f9c8398c2a..e835acd4275b 100644 --- a/lib/Fuzzer/test/afl-driver-stderr.test +++ b/lib/Fuzzer/test/afl-driver-stderr.test @@ -1,3 +1,5 @@ +REQUIRES: posix + ; Test that not specifying a stderr file isn't broken. RUN: unset AFL_DRIVER_STDERR_DUPLICATE_FILENAME RUN: AFLDriverTest diff --git a/lib/Fuzzer/test/bad-strcmp.test b/lib/Fuzzer/test/bad-strcmp.test new file mode 100644 index 000000000000..9a2f3742a5f4 --- /dev/null +++ b/lib/Fuzzer/test/bad-strcmp.test @@ -0,0 +1 @@ +RUN: LLVMFuzzer-BadStrcmpTest -runs=100000 diff --git a/lib/Fuzzer/test/coverage.test b/lib/Fuzzer/test/coverage.test index fa11be502ef9..ff3fdff57a3d 100644 --- a/lib/Fuzzer/test/coverage.test +++ b/lib/Fuzzer/test/coverage.test @@ -1,9 +1,11 @@ +XFAIL: darwin + CHECK: COVERAGE: CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:13 CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:14 CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:16 CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:19 -CHECK: COVERED_DIRS: {{.*}}lib/Fuzzer/test +CHECK: COVERED_DIRS: {{.*}}lib{{[/\\]}}Fuzzer{{[/\\]}}test RUN: not LLVMFuzzer-NullDerefTest -print_coverage=1 2>&1 | FileCheck %s RUN: LLVMFuzzer-DSOTest -print_coverage=1 -runs=0 2>&1 | FileCheck %s --check-prefix=DSO diff --git a/lib/Fuzzer/test/cxxstring.test b/lib/Fuzzer/test/cxxstring.test new file mode 100644 index 000000000000..c60d7aee9686 --- /dev/null +++ b/lib/Fuzzer/test/cxxstring.test @@ -0,0 +1,2 @@ +RUN: not LLVMFuzzer-CxxStringEqTest -seed=1 -runs=1000000 2>&1 | FileCheck %s +CHECK: BINGO diff --git a/lib/Fuzzer/test/disable-leaks.test b/lib/Fuzzer/test/disable-leaks.test new file mode 100644 index 000000000000..467b64ccc6f4 --- /dev/null +++ b/lib/Fuzzer/test/disable-leaks.test @@ -0,0 +1,4 @@ +REQUIRES: lsan +RUN: LLVMFuzzer-AccumulateAllocationsTest -detect_leaks=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=ACCUMULATE_ALLOCS +ACCUMULATE_ALLOCS: INFO: libFuzzer disabled leak detection after every mutation + diff --git a/lib/Fuzzer/test/dump_coverage.test b/lib/Fuzzer/test/dump_coverage.test index 9bd98daa3619..8acc8304fc60 100644 --- a/lib/Fuzzer/test/dump_coverage.test +++ b/lib/Fuzzer/test/dump_coverage.test @@ -1,16 +1,14 @@ -RUN: DIR=%t_workdir -RUN: BUILD_DIR=$(pwd) -RUN: rm -rf $DIR && mkdir -p $DIR && cd $DIR -RUN: not $BUILD_DIR/LLVMFuzzer-NullDerefTest -dump_coverage=1 2>&1 | FileCheck %s -RUN: $BUILD_DIR/LLVMFuzzer-DSOTest -dump_coverage=1 -runs=0 2>&1 | FileCheck %s --check-prefix=DSO -RUN: not $BUILD_DIR/LLVMFuzzer-NullDerefTest -dump_coverage=0 2>&1 | FileCheck %s --check-prefix=NOCOV -RUN: rm -rf $DIR - - -CHECK: SanitizerCoverage: ./LLVMFuzzer-NullDerefTest.{{.*}}.sancov {{.*}} PCs written - -DSO: SanitizerCoverage: ./LLVMFuzzer-DSOTest.{{.*}}.sancov {{.*}} PCs written -DSO-DAG: SanitizerCoverage: ./libLLVMFuzzer-DSO1.{{.*}}.sancov {{.*}} PCs written -DSO-DAG: SanitizerCoverage: ./libLLVMFuzzer-DSO2.{{.*}}.sancov {{.*}} PCs written +RUN: rm -rf %t_workdir && mkdir -p %t_workdir +RUN: env ASAN_OPTIONS=coverage_dir='"%t_workdir"' not LLVMFuzzer-NullDerefTest -dump_coverage=1 2>&1 | FileCheck %s +RUN: sancov -covered-functions LLVMFuzzer-NullDerefTest* %t_workdir/*.sancov | FileCheck %s --check-prefix=SANCOV +RUN: env ASAN_OPTIONS=coverage_dir='"%t_workdir"' LLVMFuzzer-DSOTest -dump_coverage=1 -runs=0 2>&1 | FileCheck %s --check-prefix=DSO +RUN: env ASAN_OPTIONS=coverage_dir='"%t_workdir"' not LLVMFuzzer-NullDerefTest -dump_coverage=0 2>&1 | FileCheck %s --check-prefix=NOCOV + +CHECK: SanitizerCoverage: {{.*}}LLVMFuzzer-NullDerefTest.{{.*}}.sancov {{.*}} PCs written +SANCOV: LLVMFuzzerTestOneInput + +DSO: SanitizerCoverage: {{.*}}LLVMFuzzer-DSOTest.{{.*}}.sancov {{.*}} PCs written +DSO-DAG: SanitizerCoverage: {{.*}}LLVMFuzzer-DSO1.{{.*}}.sancov {{.*}} PCs written +DSO-DAG: SanitizerCoverage: {{.*}}LLVMFuzzer-DSO2.{{.*}}.sancov {{.*}} PCs written NOCOV-NOT: SanitizerCoverage: {{.*}} PCs written diff --git a/lib/Fuzzer/test/equivalence-signals.test b/lib/Fuzzer/test/equivalence-signals.test new file mode 100644 index 000000000000..81a7f37602cc --- /dev/null +++ b/lib/Fuzzer/test/equivalence-signals.test @@ -0,0 +1,9 @@ +REQUIRES: posix +# Run EquivalenceATest against itself with a small timeout +# to stress the signal handling and ensure that shmem doesn't mind +# the signals. + +RUN: LLVMFuzzer-EquivalenceATest -timeout=1 -run_equivalence_server=EQUIV_SIG_TEST & export APID=$! +RUN: sleep 3 +RUN: LLVMFuzzer-EquivalenceATest -timeout=1 -use_equivalence_server=EQUIV_SIG_TEST -runs=500000 2>&1 +RUN: kill -9 $APID diff --git a/lib/Fuzzer/test/equivalence.test b/lib/Fuzzer/test/equivalence.test new file mode 100644 index 000000000000..015ba855c600 --- /dev/null +++ b/lib/Fuzzer/test/equivalence.test @@ -0,0 +1,8 @@ +REQUIRES: posix + +RUN: LLVMFuzzer-EquivalenceATest -run_equivalence_server=EQUIV_TEST & export APID=$! +RUN: sleep 3 +RUN: not LLVMFuzzer-EquivalenceBTest -use_equivalence_server=EQUIV_TEST -max_len=4096 2>&1 | FileCheck %s +CHECK: ERROR: libFuzzer: equivalence-mismatch. Sizes: {{.*}}; offset 2 +CHECK: SUMMARY: libFuzzer: equivalence-mismatch +RUN: kill -9 $APID diff --git a/lib/Fuzzer/test/extra-counters.test b/lib/Fuzzer/test/extra-counters.test new file mode 100644 index 000000000000..61fce44784b7 --- /dev/null +++ b/lib/Fuzzer/test/extra-counters.test @@ -0,0 +1,6 @@ +REQUIRES: linux + +RUN: not LLVMFuzzer-TableLookupTest -print_final_stats=1 2>&1 | FileCheck %s +CHECK: BINGO +// Expecting >= 4096 new_units_added +CHECK: stat::new_units_added:{{.*[4][0-9][0-9][0-9]}} diff --git a/lib/Fuzzer/test/fuzzer-customcrossover.test b/lib/Fuzzer/test/fuzzer-customcrossover.test index 28d39ce31dec..ccf8261af8ad 100644 --- a/lib/Fuzzer/test/fuzzer-customcrossover.test +++ b/lib/Fuzzer/test/fuzzer-customcrossover.test @@ -2,7 +2,7 @@ RUN: rm -rf %t/CustomCrossover RUN: mkdir -p %t/CustomCrossover RUN: echo "0123456789" > %t/CustomCrossover/digits RUN: echo "abcdefghij" > %t/CustomCrossover/chars -RUN: not LLVMFuzzer-CustomCrossOverTest -seed=1 -use_memcmp=0 -runs=100000 %t/CustomCrossover 2>&1 | FileCheck %s --check-prefix=LLVMFuzzerCustomCrossover +RUN: not LLVMFuzzer-CustomCrossOverTest -seed=1 -runs=100000 %t/CustomCrossover 2>&1 | FileCheck %s --check-prefix=LLVMFuzzerCustomCrossover RUN: rm -rf %t/CustomCrossover LLVMFuzzerCustomCrossover: In LLVMFuzzerCustomCrossover diff --git a/lib/Fuzzer/test/fuzzer-customcrossoverandmutate.test b/lib/Fuzzer/test/fuzzer-customcrossoverandmutate.test new file mode 100644 index 000000000000..1e322ec0da63 --- /dev/null +++ b/lib/Fuzzer/test/fuzzer-customcrossoverandmutate.test @@ -0,0 +1 @@ +RUN: LLVMFuzzer-CustomCrossOverAndMutateTest -seed=1 -runs=100000 diff --git a/lib/Fuzzer/test/fuzzer-dirs.test b/lib/Fuzzer/test/fuzzer-dirs.test index 63afe8dfcf9c..3de64f278f5d 100644 --- a/lib/Fuzzer/test/fuzzer-dirs.test +++ b/lib/Fuzzer/test/fuzzer-dirs.test @@ -5,9 +5,9 @@ RUN: echo b > %t/SUB1/SUB2/b RUN: echo c > %t/SUB1/SUB2/SUB3/c RUN: LLVMFuzzer-SimpleTest %t/SUB1 -runs=0 2>&1 | FileCheck %s --check-prefix=SUBDIRS SUBDIRS: READ units: 3 -RUN: echo zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz > %t/SUB1/long +RUN: echo -n zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz > %t/SUB1/long RUN: LLVMFuzzer-SimpleTest %t/SUB1 -runs=0 2>&1 | FileCheck %s --check-prefix=LONG -LONG: INFO: -max_len is not provided, using 94 +LONG: INFO: -max_len is not provided, using 93 RUN: rm -rf %t/SUB1 RUN: not LLVMFuzzer-SimpleTest NONEXISTENT_DIR 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR diff --git a/lib/Fuzzer/test/fuzzer-jobs.test b/lib/Fuzzer/test/fuzzer-jobs.test deleted file mode 100644 index 5bf8cfadfb75..000000000000 --- a/lib/Fuzzer/test/fuzzer-jobs.test +++ /dev/null @@ -1,29 +0,0 @@ -RUN: rm -rf %tmp -RUN: mkdir %tmp && cd %tmp -# Create a shared corpus directory -RUN: rm -rf FuzzerJobsTestCORPUS -RUN: mkdir FuzzerJobsTestCORPUS -RUN: rm -f fuzz-{0,1}.log -# Start fuzzer and in parallel check that the output files -# that should be created exist. -RUN: LLVMFuzzer-EmptyTest -max_total_time=4 -jobs=2 -workers=2 FuzzerJobsTestCORPUS > %t-fuzzer-jobs-test.log 2>&1 & export FUZZER_PID=$! -# Wait a short while to give time for the child processes -# to start fuzzing -RUN: sleep 2 -# If the instances are running in parallel they should have created their log -# files by now. -RUN: ls fuzz-0.log -RUN: ls fuzz-1.log -# Wait for libfuzzer to finish. -# This probably isn't portable but we need a way to block until -# the fuzzer is done otherwise we might remove the files while -# they are being used. -RUN: while kill -0 ${FUZZER_PID}; do : ; done -RUN: rm -f fuzz-{0,1}.log -RUN: rm -rf FuzzerJobsTestCORPUS -RUN: FileCheck -input-file=%t-fuzzer-jobs-test.log %s -RUN: rm %t-fuzzer-jobs-test.log -RUN: cd ../ - -CHECK-DAG: Job 0 exited with exit code 0 -CHECK-DAG: Job 1 exited with exit code 0 diff --git a/lib/Fuzzer/test/fuzzer-leak.test b/lib/Fuzzer/test/fuzzer-leak.test index 9cf5c743fff5..13e3ad740e6d 100644 --- a/lib/Fuzzer/test/fuzzer-leak.test +++ b/lib/Fuzzer/test/fuzzer-leak.test @@ -29,7 +29,5 @@ RUN: not LLVMFuzzer-LeakTimeoutTest -timeout=1 2>&1 | FileCheck %s --check-prefi LEAK_TIMEOUT: ERROR: libFuzzer: timeout after LEAK_TIMEOUT-NOT: LeakSanitizer -RUN: LLVMFuzzer-AccumulateAllocationsTest -detect_leaks=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=ACCUMULATE_ALLOCS -ACCUMULATE_ALLOCS: INFO: libFuzzer disabled leak detection after every mutation RUN: LLVMFuzzer-LeakTest -error_exitcode=0 diff --git a/lib/Fuzzer/test/fuzzer-oom.test b/lib/Fuzzer/test/fuzzer-oom.test index 8caf649e9f04..e9d33552723e 100644 --- a/lib/Fuzzer/test/fuzzer-oom.test +++ b/lib/Fuzzer/test/fuzzer-oom.test @@ -1,10 +1,12 @@ +XFAIL: darwin RUN: not LLVMFuzzer-OutOfMemoryTest -rss_limit_mb=300 2>&1 | FileCheck %s + CHECK: ERROR: libFuzzer: out-of-memory (used: {{.*}}; limit: 300Mb) CHECK: Test unit written to ./oom- SUMMARY: libFuzzer: out-of-memory -RUN: not LLVMFuzzer-OutOfMemorySingleLargeMallocTest 2>&1 | FileCheck %s --check-prefix=SINGLE_LARGE_MALLOC -SINGLE_LARGE_MALLOC: libFuzzer: out-of-memory (malloc(42{{.*}})) +RUN: not LLVMFuzzer-OutOfMemorySingleLargeMallocTest -rss_limit_mb=300 2>&1 | FileCheck %s --check-prefix=SINGLE_LARGE_MALLOC +SINGLE_LARGE_MALLOC: libFuzzer: out-of-memory (malloc(53{{.*}})) SINGLE_LARGE_MALLOC: in LLVMFuzzerTestOneInput # Check that -rss_limit_mb=0 means no limit. diff --git a/lib/Fuzzer/test/fuzzer-segv.test b/lib/Fuzzer/test/fuzzer-segv.test index 330f03bcc494..b9a6a5ce44ca 100644 --- a/lib/Fuzzer/test/fuzzer-segv.test +++ b/lib/Fuzzer/test/fuzzer-segv.test @@ -1,4 +1,4 @@ -RUN: ASAN_OPTIONS=handle_segv=0 not LLVMFuzzer-NullDerefTest 2>&1 | FileCheck %s --check-prefix=LIBFUZZER_OWN_SEGV_HANDLER +RUN: env ASAN_OPTIONS=handle_segv=0 not LLVMFuzzer-NullDerefTest 2>&1 | FileCheck %s --check-prefix=LIBFUZZER_OWN_SEGV_HANDLER LIBFUZZER_OWN_SEGV_HANDLER: == ERROR: libFuzzer: deadly signal LIBFUZZER_OWN_SEGV_HANDLER: SUMMARY: libFuzzer: deadly signal LIBFUZZER_OWN_SEGV_HANDLER: Test unit written to ./crash- diff --git a/lib/Fuzzer/test/fuzzer-singleinputs.test b/lib/Fuzzer/test/fuzzer-singleinputs.test index ca8403bff81f..500e5da8faa9 100644 --- a/lib/Fuzzer/test/fuzzer-singleinputs.test +++ b/lib/Fuzzer/test/fuzzer-singleinputs.test @@ -8,7 +8,7 @@ RUN: echo bbb > %tmp/SINGLE_INPUTS/bbb RUN: LLVMFuzzer-SimpleTest %tmp/SINGLE_INPUTS/aaa %tmp/SINGLE_INPUTS/bbb 2>&1 | FileCheck %s --check-prefix=SINGLE_INPUTS RUN: LLVMFuzzer-SimpleTest -max_len=2 %tmp/SINGLE_INPUTS/aaa %tmp/SINGLE_INPUTS/bbb 2>&1 | FileCheck %s --check-prefix=SINGLE_INPUTS RUN: rm -rf %tmp/SINGLE_INPUTS -SINGLE_INPUTS: LLVMFuzzer-SimpleTest: Running 2 inputs 1 time(s) each. +SINGLE_INPUTS: LLVMFuzzer-SimpleTest{{.*}}: Running 2 inputs 1 time(s) each. SINGLE_INPUTS: aaa in SINGLE_INPUTS: bbb in SINGLE_INPUTS: NOTE: fuzzing was not performed, you have only diff --git a/lib/Fuzzer/test/fuzzer-traces-hooks.test b/lib/Fuzzer/test/fuzzer-traces-hooks.test index 71fe6f2daf11..f93a8b7199e2 100644 --- a/lib/Fuzzer/test/fuzzer-traces-hooks.test +++ b/lib/Fuzzer/test/fuzzer-traces-hooks.test @@ -1,25 +1,17 @@ -// FIXME: Support sanitizer hooks for memcmp and strcmp need -// to be implemented in the sanitizer runtime for platforms other -// than linux -REQUIRES: linux +// FIXME: Support for sanitizer hooks for memcmp and strcmp needs to +// be implemented in the sanitizer runtime for this test +UNSUPPORTED: windows CHECK: BINGO -Done1000000: Done 1000000 runs in -RUN: not LLVMFuzzer-MemcmpTest -seed=4294967295 -runs=100000 2>&1 | FileCheck %s -RUN: LLVMFuzzer-MemcmpTest -use_memcmp=0 -seed=4294967295 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 +RUN: not LLVMFuzzer-MemcmpTest -seed=1 -runs=2000000 2>&1 | FileCheck %s +RUN: not LLVMFuzzer-StrncmpTest -seed=1 -runs=2000000 2>&1 | FileCheck %s +RUN: not LLVMFuzzer-StrcmpTest -seed=1 -runs=2000000 2>&1 | FileCheck %s +RUN: not LLVMFuzzer-StrstrTest -seed=1 -runs=2000000 2>&1 | FileCheck %s -RUN: not LLVMFuzzer-StrncmpTest -seed=2 -runs=100000 2>&1 | FileCheck %s -RUN: LLVMFuzzer-StrncmpTest -use_memcmp=0 -seed=3 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 +RUN: not LLVMFuzzer-Memcmp64BytesTest -seed=1 -runs=1000000 2>&1 | FileCheck %s -RUN: not LLVMFuzzer-StrcmpTest -seed=4 -runs=200000 2>&1 | FileCheck %s -RUN: LLVMFuzzer-StrcmpTest -use_memcmp=0 -seed=5 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 - -RUN: not LLVMFuzzer-StrstrTest -seed=6 -runs=200000 2>&1 | FileCheck %s -RUN: LLVMFuzzer-StrstrTest -use_memmem=0 -seed=7 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 - -RUN: LLVMFuzzer-RepeatedMemcmp -seed=10 -runs=100000 2>&1 | FileCheck %s --check-prefix=RECOMMENDED_DICT +RUN: LLVMFuzzer-RepeatedMemcmp -seed=11 -runs=100000 2>&1 | FileCheck %s --check-prefix=RECOMMENDED_DICT RECOMMENDED_DICT:###### Recommended dictionary. ###### RECOMMENDED_DICT-DAG: "foo" RECOMMENDED_DICT-DAG: "bar" RECOMMENDED_DICT:###### End of recommended dictionary. ###### - diff --git a/lib/Fuzzer/test/fuzzer.test b/lib/Fuzzer/test/fuzzer.test index 2f91c2195ca9..ff46d32b387d 100644 --- a/lib/Fuzzer/test/fuzzer.test +++ b/lib/Fuzzer/test/fuzzer.test @@ -11,7 +11,7 @@ MaxTotalTime: Done {{.*}} runs in {{.}} second(s) RUN: not LLVMFuzzer-NullDerefTest 2>&1 | FileCheck %s --check-prefix=NullDerefTest RUN: not LLVMFuzzer-NullDerefTest -close_fd_mask=3 2>&1 | FileCheck %s --check-prefix=NullDerefTest -NullDerefTest: ERROR: AddressSanitizer: SEGV on unknown address +NullDerefTest: ERROR: AddressSanitizer: {{SEGV|access-violation}} on unknown address NullDerefTest: Test unit written to ./crash- RUN: not LLVMFuzzer-NullDerefTest -artifact_prefix=ZZZ 2>&1 | FileCheck %s --check-prefix=NullDerefTestPrefix NullDerefTestPrefix: Test unit written to ZZZcrash- @@ -34,7 +34,7 @@ COUNTERS: BINGO DISABLED: not LLVMFuzzer-UninstrumentedTest-Uninstrumented 2>&1 | FileCheck %s --check-prefix=UNINSTRUMENTED UNINSTRUMENTED: ERROR: __sanitizer_set_death_callback is not defined. Exiting. -RUN: not LLVMFuzzer-UninstrumentedTest-NoCoverage 2>&1 | FileCheck %s --check-prefix=NO_COVERAGE +RUN: not LLVMFuzzer-NotinstrumentedTest-NoCoverage 2>&1 | FileCheck %s --check-prefix=NO_COVERAGE NO_COVERAGE: ERROR: no interesting inputs were found. Is the code instrumented for coverage? Exiting RUN: not LLVMFuzzer-BufferOverflowOnInput 2>&1 | FileCheck %s --check-prefix=OOB @@ -51,7 +51,10 @@ RUN: LLVMFuzzer-SimpleTest -exit_on_src_pos=SimpleTest.cpp:17 2 RUN: LLVMFuzzer-ShrinkControlFlowTest -exit_on_src_pos=ShrinkControlFlowTest.cpp:23 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS EXIT_ON_SRC_POS: INFO: found line matching '{{.*}}', exiting. -RUN: ASAN_OPTIONS=strict_string_checks=1 not LLVMFuzzer-StrncmpOOBTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=STRNCMP +RUN: env ASAN_OPTIONS=strict_string_checks=1 not LLVMFuzzer-StrncmpOOBTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=STRNCMP STRNCMP: AddressSanitizer: heap-buffer-overflow STRNCMP-NOT: __sanitizer_weak_hook_strncmp STRNCMP: in LLVMFuzzerTestOneInput + +RUN: not LLVMFuzzer-BogusInitializeTest 2>&1 | FileCheck %s --check-prefix=BOGUS_INITIALIZE +BOGUS_INITIALIZE: argv[0] has been modified in LLVMFuzzerInitialize diff --git a/lib/Fuzzer/test/lit.cfg b/lib/Fuzzer/test/lit.cfg index 745af0c38245..85c95b42d1ea 100644 --- a/lib/Fuzzer/test/lit.cfg +++ b/lib/Fuzzer/test/lit.cfg @@ -6,6 +6,23 @@ config.test_format = lit.formats.ShTest(True) config.suffixes = ['.test'] config.test_source_root = os.path.dirname(__file__) +# Choose between lit's internal shell pipeline runner and a real shell. If +# LIT_USE_INTERNAL_SHELL is in the environment, we use that as an override. +use_lit_shell = os.environ.get("LIT_USE_INTERNAL_SHELL") +if use_lit_shell: + # 0 is external, "" is default, and everything else is internal. + execute_external = (use_lit_shell == "0") +else: + # Otherwise we default to internal on Windows and external elsewhere, as + # bash on Windows is usually very slow. + execute_external = (not sys.platform in ['win32']) + +# testFormat: The test format to use to interpret tests. +# +# For now we require '&&' between commands, until they get globally killed and +# the test runner updated. +config.test_format = lit.formats.ShTest(execute_external) + # Tweak PATH to include llvm tools dir and current exec dir. llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) if (not llvm_tools_dir) or (not os.path.exists(llvm_tools_dir)): @@ -20,6 +37,15 @@ if config.has_lsan: else: lit_config.note('lsan feature unavailable') +if sys.platform.startswith('win') or sys.platform.startswith('cygwin'): + config.available_features.add('windows') + +if sys.platform.startswith('darwin'): + config.available_features.add('darwin') + +if config.is_posix: + config.available_features.add('posix') + if sys.platform.startswith('linux'): # Note the value of ``sys.platform`` is not consistent # between python 2 and 3, hence the use of ``.startswith()``. diff --git a/lib/Fuzzer/test/lit.site.cfg.in b/lib/Fuzzer/test/lit.site.cfg.in index 03e86c487ca9..069f2b72c0d9 100644 --- a/lib/Fuzzer/test/lit.site.cfg.in +++ b/lib/Fuzzer/test/lit.site.cfg.in @@ -1,4 +1,5 @@ config.test_exec_root = "@CMAKE_CURRENT_BINARY_DIR@" config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.has_lsan = True if @HAS_LSAN@ == 1 else False +config.is_posix = @LIBFUZZER_POSIX@ lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/lib/Fuzzer/test/merge-posix.test b/lib/Fuzzer/test/merge-posix.test new file mode 100644 index 000000000000..47b90b986791 --- /dev/null +++ b/lib/Fuzzer/test/merge-posix.test @@ -0,0 +1,23 @@ +REQUIRES: posix + +RUN: rm -rf %tmp/T1 %tmp/T2 +RUN: mkdir -p %tmp/T1 %tmp/T2 + +RUN: echo F..... > %tmp/T1/1 +RUN: echo .U.... > %tmp/T1/2 +RUN: echo ..Z... > %tmp/T1/3 + +RUN: echo .....F > %tmp/T2/1 +RUN: echo ....U. > %tmp/T2/2 +RUN: echo ...Z.. > %tmp/T2/3 +RUN: echo ...Z.. > %tmp/T2/4 +RUN: echo ....E. > %tmp/T2/5 +RUN: echo .....R > %tmp/T2/6 + +# Check that we can report an error if file size exceeded +RUN: (ulimit -f 1; not LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=SIGXFSZ) +SIGXFSZ: ERROR: libFuzzer: file size exceeded + +# Check that we honor TMPDIR +RUN: TMPDIR=DIR_DOES_NOT_EXIST not LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=TMPDIR +TMPDIR: MERGE-OUTER: failed to write to the control file: DIR_DOES_NOT_EXIST/libFuzzerTemp diff --git a/lib/Fuzzer/test/merge-summary.test b/lib/Fuzzer/test/merge-summary.test new file mode 100644 index 000000000000..df9d62dec636 --- /dev/null +++ b/lib/Fuzzer/test/merge-summary.test @@ -0,0 +1,15 @@ +RUN: rm -rf %t/T1 %t/T2 +RUN: mkdir -p %t/T0 %t/T1 %t/T2 +RUN: echo ...Z.. > %t/T2/1 +RUN: echo ....E. > %t/T2/2 +RUN: echo .....R > %t/T2/3 +RUN: echo F..... > %t/T2/a +RUN: echo .U.... > %t/T2/b +RUN: echo ..Z... > %t/T2/c + +RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %t/T1 %t/T2 -save_coverage_summary=%t/SUMMARY 2>&1 | FileCheck %s --check-prefix=SAVE_SUMMARY +SAVE_SUMMARY: MERGE-OUTER: writing coverage summary for 6 files to {{.*}}SUMMARY +RUN: rm %t/T1/* +RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %t/T1 %t/T2 -load_coverage_summary=%t/SUMMARY 2>&1 | FileCheck %s --check-prefix=LOAD_SUMMARY +LOAD_SUMMARY: MERGE-OUTER: coverage summary loaded from {{.*}}SUMMAR +LOAD_SUMMARY: MERGE-OUTER: 0 new files with 0 new features added diff --git a/lib/Fuzzer/test/merge.test b/lib/Fuzzer/test/merge.test index 5c7d30e41caa..e59da8c3e091 100644 --- a/lib/Fuzzer/test/merge.test +++ b/lib/Fuzzer/test/merge.test @@ -1,12 +1,13 @@ CHECK: BINGO -RUN: rm -rf %tmp/T1 %tmp/T2 -RUN: mkdir -p %tmp/T1 %tmp/T2 -RUN: echo F..... > %tmp/T1/1 -RUN: echo .U.... > %tmp/T1/2 -RUN: echo ..Z... > %tmp/T1/3 +RUN: rm -rf %tmp/T0 %tmp/T1 %tmp/T2 +RUN: mkdir -p %tmp/T0 %tmp/T1 %tmp/T2 +RUN: echo F..... > %tmp/T0/1 +RUN: echo .U.... > %tmp/T0/2 +RUN: echo ..Z... > %tmp/T0/3 # T1 has 3 elements, T2 is empty. +RUN: cp %tmp/T0/* %tmp/T1/ RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK1 CHECK1: MERGE-OUTER: 3 files, 3 in the initial corpus CHECK1: MERGE-OUTER: 0 new files with 0 new features added @@ -29,13 +30,15 @@ CHECK3: MERGE-OUTER: 12 files, 6 in the initial corpus CHECK3: MERGE-OUTER: 0 new files with 0 new features added # Check that we respect max_len during the merge and don't crash. -RUN: rm %tmp/T1/??* +RUN: rm %tmp/T1/* +RUN: cp %tmp/T0/* %tmp/T1/ RUN: echo looooooooong > %tmp/T2/looooooooong RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 -max_len=6 2>&1 | FileCheck %s --check-prefix=MAX_LEN MAX_LEN: MERGE-OUTER: 3 new files # Check that merge tolerates failures. -RUN: rm %tmp/T1/??* +RUN: rm %tmp/T1/* +RUN: cp %tmp/T0/* %tmp/T1/ RUN: echo 'FUZZER' > %tmp/T2/FUZZER RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=MERGE_WITH_CRASH MERGE_WITH_CRASH: MERGE-OUTER: succesfull in 2 attempt(s) @@ -45,10 +48,6 @@ MERGE_WITH_CRASH: MERGE-OUTER: 3 new files RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 -max_len=5 2>&1 | FileCheck %s --check-prefix=MERGE_LEN5 MERGE_LEN5: MERGE-OUTER: succesfull in 1 attempt(s) -# Check that we honor TMPDIR -RUN: TMPDIR=DIR_DOES_NOT_EXIST not LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=TMPDIR -TMPDIR: MERGE-OUTER: failed to write to the control file: DIR_DOES_NOT_EXIST/libFuzzerTemp - -# Check that we can report an error if file size exceeded -RUN: (ulimit -f 1; not LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=SIGXFSZ) -SIGXFSZ: ERROR: libFuzzer: file size exceeded +RUN: rm -rf %tmp/T1/* %tmp/T2/* +RUN: not LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=EMPTY +EMPTY: MERGE-OUTER: zero succesfull attempts, exiting diff --git a/lib/Fuzzer/test/minimize_crash.test b/lib/Fuzzer/test/minimize_crash.test index 7e5406598e4a..5643c6bacb09 100644 --- a/lib/Fuzzer/test/minimize_crash.test +++ b/lib/Fuzzer/test/minimize_crash.test @@ -1,6 +1,13 @@ RUN: echo 'Hi!rv349f34t3gg' > not_minimal_crash RUN: LLVMFuzzer-NullDerefTest -minimize_crash=1 not_minimal_crash -max_total_time=2 2>&1 | FileCheck %s -CHECK: CRASH_MIN: failed to minimize beyond minimized-from-{{.*}} (3 bytes), exiting +CHECK: CRASH_MIN: failed to minimize beyond ./minimized-from-{{.*}} (3 bytes), exiting RUN: LLVMFuzzer-NullDerefTest -minimize_crash=1 not_minimal_crash -max_total_time=2 -exact_artifact_path=exact_minimized_path 2>&1 | FileCheck %s --check-prefix=CHECK_EXACT CHECK_EXACT: CRASH_MIN: failed to minimize beyond exact_minimized_path (3 bytes), exiting RUN: rm not_minimal_crash minimized-from-* exact_minimized_path + +RUN: echo -n 'abcd*xyz' > not_minimal_crash +RUN: LLVMFuzzer-SingleByteInputTest -minimize_crash=1 not_minimal_crash -exact_artifact_path=exact_minimized_path 2>&1 | FileCheck %s --check-prefix=MIN1 +MIN1: Test unit written to exact_minimized_path +MIN1: Test unit written to exact_minimized_path +MIN1: INFO: The input is small enough, exiting +MIN1: CRASH_MIN: failed to minimize beyond exact_minimized_path (1 bytes), exiting diff --git a/lib/Fuzzer/test/minimize_two_crashes.test b/lib/Fuzzer/test/minimize_two_crashes.test new file mode 100644 index 000000000000..2358d8c2a92e --- /dev/null +++ b/lib/Fuzzer/test/minimize_two_crashes.test @@ -0,0 +1,16 @@ +# Test that the minimizer stops when it sees a differe bug. + +RUN: rm -rf %t && mkdir %t +RUN: echo H12345678901234667888090 > %t/long_crash +RUN: env ASAN_OPTIONS=dedup_token_length=3 LLVMFuzzer-TwoDifferentBugsTest -seed=1 -minimize_crash=1 %t/long_crash -exact_artifact_path=%t/result 2>&1 | FileCheck %s + +CHECK: DedupToken1: DEDUP_TOKEN: Bar +CHECK: DedupToken2: DEDUP_TOKEN: Bar +CHECK: DedupToken1: DEDUP_TOKEN: Bar +CHECK: DedupToken2: DEDUP_TOKEN: Foo +CHECK: CRASH_MIN: mismatch in dedup tokens + +RUN: not LLVMFuzzer-TwoDifferentBugsTest %t/result 2>&1 | FileCheck %s --check-prefix=VERIFY + +VERIFY: ERROR: AddressSanitizer: +VERIFY: in Bar diff --git a/lib/Fuzzer/test/no-coverage/CMakeLists.txt b/lib/Fuzzer/test/no-coverage/CMakeLists.txt index d2f6f438ad79..52e7240333ee 100644 --- a/lib/Fuzzer/test/no-coverage/CMakeLists.txt +++ b/lib/Fuzzer/test/no-coverage/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters,trace-pc-guard") set(NoCoverageTests - UninstrumentedTest + NotinstrumentedTest ) foreach(Test ${NoCoverageTests}) @@ -16,14 +16,14 @@ endforeach() ############################################################################### # AFL Driver test ############################################################################### +if(NOT MSVC) + add_executable(AFLDriverTest + ../AFLDriverTest.cpp ../../afl/afl_driver.cpp) -add_executable(AFLDriverTest - ../AFLDriverTest.cpp ../../afl/afl_driver.cpp) + set_target_properties(AFLDriverTest + PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_BINARY_DIR}/lib/Fuzzer/test" + ) -set_target_properties(AFLDriverTest - PROPERTIES RUNTIME_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/lib/Fuzzer/test" - ) - -# Propagate value into parent directory -set(TestBinaries ${TestBinaries} AFLDriverTest PARENT_SCOPE) + add_dependencies(TestBinaries AFLDriverTest) +endif() diff --git a/lib/Fuzzer/test/trace-malloc-2.test b/lib/Fuzzer/test/trace-malloc-2.test new file mode 100644 index 000000000000..7719b650c791 --- /dev/null +++ b/lib/Fuzzer/test/trace-malloc-2.test @@ -0,0 +1,8 @@ +// FIXME: This test infinite loops on darwin because it crashes +// printing a stack trace repeatedly +UNSUPPORTED: darwin + +RUN: LLVMFuzzer-TraceMallocTest -seed=1 -trace_malloc=2 -runs=1000 2>&1 | FileCheck %s --check-prefix=TRACE2 +TRACE2-DAG: FREE[0] +TRACE2-DAG: MALLOC[0] +TRACE2-DAG: in LLVMFuzzerTestOneInput diff --git a/lib/Fuzzer/test/trace-malloc.test b/lib/Fuzzer/test/trace-malloc.test index c95147904d42..25694cc2de5c 100644 --- a/lib/Fuzzer/test/trace-malloc.test +++ b/lib/Fuzzer/test/trace-malloc.test @@ -3,8 +3,3 @@ CHECK-DAG: MallocFreeTracer: STOP 0 0 (same) CHECK-DAG: MallocFreeTracer: STOP 0 1 (DIFFERENT) CHECK-DAG: MallocFreeTracer: STOP 1 0 (DIFFERENT) CHECK-DAG: MallocFreeTracer: STOP 1 1 (same) - -RUN: LLVMFuzzer-TraceMallocTest -seed=1 -trace_malloc=2 -runs=1000 2>&1 | FileCheck %s --check-prefix=TRACE2 -TRACE2-DAG: FREE[0] -TRACE2-DAG: MALLOC[0] -TRACE2-DAG: in LLVMFuzzerTestOneInput diff --git a/lib/Fuzzer/test/trace-pc.test b/lib/Fuzzer/test/trace-pc.test new file mode 100644 index 000000000000..3709677b71b6 --- /dev/null +++ b/lib/Fuzzer/test/trace-pc.test @@ -0,0 +1,2 @@ +CHECK: BINGO +RUN: LLVMFuzzer-SimpleTest-TracePC -runs=100000 -seed=1 2>&1 | FileCheck %s diff --git a/lib/Fuzzer/test/trace-pc/CMakeLists.txt b/lib/Fuzzer/test/trace-pc/CMakeLists.txt new file mode 100644 index 000000000000..e800f82cc5dc --- /dev/null +++ b/lib/Fuzzer/test/trace-pc/CMakeLists.txt @@ -0,0 +1,13 @@ +# These tests are not instrumented with coverage and don't +# have coverage rt in the binary. + +set(CMAKE_CXX_FLAGS + "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters,trace-pc-guard -fsanitize-coverage=trace-pc") + +set(TracePCTests + SimpleTest + ) + +foreach(Test ${TracePCTests}) + add_libfuzzer_test(${Test}-TracePC SOURCES ../${Test}.cpp) +endforeach() diff --git a/lib/Fuzzer/test/ubsan/CMakeLists.txt b/lib/Fuzzer/test/ubsan/CMakeLists.txt index 7a9eacdbe7df..55e0a118186b 100644 --- a/lib/Fuzzer/test/ubsan/CMakeLists.txt +++ b/lib/Fuzzer/test/ubsan/CMakeLists.txt @@ -10,6 +10,3 @@ set(UbsanTests foreach(Test ${UbsanTests}) add_libfuzzer_test(${Test}-Ubsan SOURCES ../${Test}.cpp) endforeach() - -# Propagate value into parent directory -set(TestBinaries ${TestBinaries} PARENT_SCOPE) diff --git a/lib/Fuzzer/test/ulimit.test b/lib/Fuzzer/test/ulimit.test index a60636c351bd..c2faca13f728 100644 --- a/lib/Fuzzer/test/ulimit.test +++ b/lib/Fuzzer/test/ulimit.test @@ -1,2 +1,4 @@ +REQUIRES: posix + RUN: ulimit -s 1000 RUN: LLVMFuzzer-SimpleTest diff --git a/lib/Fuzzer/test/uninstrumented/CMakeLists.txt b/lib/Fuzzer/test/uninstrumented/CMakeLists.txt index 29b66e6e586a..f4ab59e5b18d 100644 --- a/lib/Fuzzer/test/uninstrumented/CMakeLists.txt +++ b/lib/Fuzzer/test/uninstrumented/CMakeLists.txt @@ -11,6 +11,3 @@ set(UninstrumentedTests foreach(Test ${UninstrumentedTests}) add_libfuzzer_test(${Test}-Uninstrumented SOURCES ../${Test}.cpp) endforeach() - -# Propagate value into parent directory -set(TestBinaries ${TestBinaries} PARENT_SCOPE) diff --git a/lib/Fuzzer/test/value-profile-div.test b/lib/Fuzzer/test/value-profile-div.test index ba45e4129d30..b966a8916512 100644 --- a/lib/Fuzzer/test/value-profile-div.test +++ b/lib/Fuzzer/test/value-profile-div.test @@ -1,3 +1,3 @@ -CHECK: AddressSanitizer: FPE +CHECK: AddressSanitizer: {{FPE|int-divide-by-zero}} RUN: not LLVMFuzzer-DivTest -seed=1 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/lib/Fuzzer/test/value-profile-mem.test b/lib/Fuzzer/test/value-profile-mem.test index 09d737dbe736..880b2692910a 100644 --- a/lib/Fuzzer/test/value-profile-mem.test +++ b/lib/Fuzzer/test/value-profile-mem.test @@ -1,2 +1,2 @@ CHECK: BINGO -RUN: not LLVMFuzzer-SingleMemcmpTest -seed=1 -use_cmp=0 -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s +RUN: not LLVMFuzzer-SingleMemcmpTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/lib/Fuzzer/test/value-profile-strcmp.test b/lib/Fuzzer/test/value-profile-strcmp.test index 1e7ef9b45e96..7f1047594548 100644 --- a/lib/Fuzzer/test/value-profile-strcmp.test +++ b/lib/Fuzzer/test/value-profile-strcmp.test @@ -1,2 +1,2 @@ CHECK: BINGO -RUN: not LLVMFuzzer-SingleStrcmpTest -seed=1 -use_cmp=0 -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s +RUN: not LLVMFuzzer-SingleStrcmpTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s diff --git a/lib/Fuzzer/test/value-profile-strncmp.test b/lib/Fuzzer/test/value-profile-strncmp.test index 650973180c06..84a74c4f0ad2 100644 --- a/lib/Fuzzer/test/value-profile-strncmp.test +++ b/lib/Fuzzer/test/value-profile-strncmp.test @@ -1,2 +1,2 @@ CHECK: BINGO -RUN: not LLVMFuzzer-SingleStrncmpTest -seed=1 -use_cmp=0 -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s +RUN: not LLVMFuzzer-SingleStrncmpTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s |