diff --git a/lib/Fuzzer/FuzzerDriver.cpp b/lib/Fuzzer/FuzzerDriver.cpp index ba6edb33f39..f520a5cfdc4 100644 --- a/lib/Fuzzer/FuzzerDriver.cpp +++ b/lib/Fuzzer/FuzzerDriver.cpp @@ -309,6 +309,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.UseIndirCalls = Flags.use_indir_calls; Options.UseTraces = Flags.use_traces; Options.UseMemcmp = Flags.use_memcmp; + Options.UseMemmem = Flags.use_memmem; Options.ShuffleAtStartUp = Flags.shuffle; Options.PreferSmall = Flags.prefer_small; Options.Reload = Flags.reload; diff --git a/lib/Fuzzer/FuzzerFlags.def b/lib/Fuzzer/FuzzerFlags.def index 2945152ae70..599ac368634 100644 --- a/lib/Fuzzer/FuzzerFlags.def +++ b/lib/Fuzzer/FuzzerFlags.def @@ -42,6 +42,8 @@ FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters") FUZZER_FLAG_INT(use_traces, 0, "Experimental: use instruction traces") 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(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn" " this number of jobs in separate worker processes" " with stdout/stderr redirected to fuzz-JOB.log.") diff --git a/lib/Fuzzer/FuzzerInternal.h b/lib/Fuzzer/FuzzerInternal.h index d27ac8684fc..08f8801ac5f 100644 --- a/lib/Fuzzer/FuzzerInternal.h +++ b/lib/Fuzzer/FuzzerInternal.h @@ -217,6 +217,7 @@ struct FuzzingOptions { bool UseIndirCalls = true; bool UseTraces = false; bool UseMemcmp = true; + bool UseMemmem = true; bool UseFullCoverageSet = false; bool Reload = true; bool ShuffleAtStartUp = true; @@ -293,7 +294,7 @@ public: void AddWordToManualDictionary(const Word &W); - void AddWordToAutoDictionary(const Word &W, size_t PositionHint); + void AddWordToAutoDictionary(DictionaryEntry DE); void ClearAutoDictionary(); void PrintRecommendedDictionary(); diff --git a/lib/Fuzzer/FuzzerMutate.cpp b/lib/Fuzzer/FuzzerMutate.cpp index 72b095d651a..65e1650bfea 100644 --- a/lib/Fuzzer/FuzzerMutate.cpp +++ b/lib/Fuzzer/FuzzerMutate.cpp @@ -313,11 +313,10 @@ void MutationDispatcher::AddWordToManualDictionary(const Word &W) { {W, std::numeric_limits::max()}); } -void MutationDispatcher::AddWordToAutoDictionary(const Word &W, - size_t PositionHint) { +void MutationDispatcher::AddWordToAutoDictionary(DictionaryEntry DE) { static const size_t kMaxAutoDictSize = 1 << 14; if (TempAutoDictionary.size() >= kMaxAutoDictSize) return; - TempAutoDictionary.push_back({W, PositionHint}); + TempAutoDictionary.push_back(DE); } void MutationDispatcher::ClearAutoDictionary() { diff --git a/lib/Fuzzer/FuzzerTraceState.cpp b/lib/Fuzzer/FuzzerTraceState.cpp index cbfa87d2108..d6e1f79791f 100644 --- a/lib/Fuzzer/FuzzerTraceState.cpp +++ b/lib/Fuzzer/FuzzerTraceState.cpp @@ -77,6 +77,7 @@ #include #include #include +#include #if !LLVM_FUZZER_SUPPORTS_DFSAN // Stubs for dfsan for platforms where dfsan does not exist and weak @@ -171,6 +172,7 @@ struct TraceBasedMutation { // Declared as static globals for faster checks inside the hooks. static bool RecordingTraces = false; static bool RecordingMemcmp = false; +static bool RecordingMemmem = false; class TraceState { public: @@ -204,7 +206,9 @@ public: return; RecordingTraces = Options.UseTraces; RecordingMemcmp = Options.UseMemcmp; + RecordingMemmem = Options.UseMemmem; NumMutations = 0; + InterestingWords.clear(); MD.ClearAutoDictionary(); } @@ -233,8 +237,10 @@ public: } } } - MD.AddWordToAutoDictionary(M.W, M.Pos); + 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) { @@ -249,6 +255,14 @@ public: AddMutation(Pos, Size, reinterpret_cast(&Data)); } + void AddInterestingWord(const uint8_t *Data, size_t Size) { + if (!RecordingMemmem || !F->InFuzzingThread()) return; + if (Size <= 1) return; + Size = std::min(Size, Word::GetMaxSize()); + Word W(Data, Size); + InterestingWords.insert(W); + } + void EnsureDfsanLabels(size_t Size) { for (; LastDfsanLabel < Size; LastDfsanLabel++) { dfsan_label L = dfsan_create_label("input", (void *)(LastDfsanLabel + 1)); @@ -285,6 +299,8 @@ public: 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 InterestingWords; LabelRange LabelRanges[1 << (sizeof(dfsan_label) * 8)]; size_t LastDfsanLabel = 0; MutationDispatcher &MD; @@ -605,6 +621,27 @@ void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, reinterpret_cast(s2)); } +void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result) { + return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result); +} +void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, + const char *s2, int result) { + return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result); +} +void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1, + const char *s2, char *result) { + TS->AddInterestingWord(reinterpret_cast(s2), strlen(s2)); +} +void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, + const char *s2, char *result) { + TS->AddInterestingWord(reinterpret_cast(s2), strlen(s2)); +} +void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1, + const void *s2, size_t len2, void *result) { + // TODO: can't hook memmem since memmem is used by libFuzzer. +} + #endif // LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS __attribute__((visibility("default"))) diff --git a/lib/Fuzzer/test/CMakeLists.txt b/lib/Fuzzer/test/CMakeLists.txt index e079650f5a8..cbc983e9e41 100644 --- a/lib/Fuzzer/test/CMakeLists.txt +++ b/lib/Fuzzer/test/CMakeLists.txt @@ -90,6 +90,7 @@ set(Tests SpamyTest StrcmpTest StrncmpTest + StrstrTest SwitchTest ThreadedLeakTest ThreadedTest diff --git a/lib/Fuzzer/test/FuzzerUnittest.cpp b/lib/Fuzzer/test/FuzzerUnittest.cpp index 3630e39a24a..3fd87e5b9e0 100644 --- a/lib/Fuzzer/test/FuzzerUnittest.cpp +++ b/lib/Fuzzer/test/FuzzerUnittest.cpp @@ -311,7 +311,7 @@ void TestAddWordFromDictionaryWithHint(Mutator M, int NumIter) { MutationDispatcher MD(Rand, {}); uint8_t W[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xFF, 0xEE, 0xEF}; size_t PosHint = 7777; - MD.AddWordToAutoDictionary(Word(W, sizeof(W)), PosHint); + MD.AddWordToAutoDictionary({Word(W, sizeof(W)), PosHint}); int FoundMask = 0; for (int i = 0; i < NumIter; i++) { uint8_t T[10000]; diff --git a/lib/Fuzzer/test/StrstrTest.cpp b/lib/Fuzzer/test/StrstrTest.cpp new file mode 100644 index 00000000000..90d539b660a --- /dev/null +++ b/lib/Fuzzer/test/StrstrTest.cpp @@ -0,0 +1,18 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test strstr and strcasestr hooks. +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + std::string s(reinterpret_cast(Data), Size); + if (strstr(s.c_str(), "FUZZ") && strcasestr(s.c_str(), "aBcD")) { + fprintf(stderr, "BINGO\n"); + exit(1); + } + return 0; +} diff --git a/lib/Fuzzer/test/fuzzer-traces-hooks.test b/lib/Fuzzer/test/fuzzer-traces-hooks.test index b98972d252d..71fe6f2daf1 100644 --- a/lib/Fuzzer/test/fuzzer-traces-hooks.test +++ b/lib/Fuzzer/test/fuzzer-traces-hooks.test @@ -14,6 +14,8 @@ RUN: LLVMFuzzer-StrncmpTest -use_memcmp=0 -seed=3 -runs=1000000 2>&1 | File 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 RECOMMENDED_DICT:###### Recommended dictionary. ######