summaryrefslogtreecommitdiff
path: root/lib/Fuzzer
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Fuzzer')
-rw-r--r--lib/Fuzzer/CMakeLists.txt9
-rw-r--r--lib/Fuzzer/FuzzerCorpus.h26
-rw-r--r--lib/Fuzzer/FuzzerDefs.h33
-rw-r--r--lib/Fuzzer/FuzzerDictionary.h7
-rw-r--r--lib/Fuzzer/FuzzerDriver.cpp195
-rw-r--r--lib/Fuzzer/FuzzerExtFunctions.def10
-rw-r--r--lib/Fuzzer/FuzzerExtFunctionsDlsymWin.cpp60
-rw-r--r--lib/Fuzzer/FuzzerExtraCounters.cpp41
-rw-r--r--lib/Fuzzer/FuzzerFlags.def15
-rw-r--r--lib/Fuzzer/FuzzerIO.cpp7
-rw-r--r--lib/Fuzzer/FuzzerIO.h9
-rw-r--r--lib/Fuzzer/FuzzerIOPosix.cpp29
-rw-r--r--lib/Fuzzer/FuzzerIOWindows.cpp45
-rw-r--r--lib/Fuzzer/FuzzerInterface.h2
-rw-r--r--lib/Fuzzer/FuzzerInternal.h41
-rw-r--r--lib/Fuzzer/FuzzerLoop.cpp243
-rw-r--r--lib/Fuzzer/FuzzerMerge.cpp87
-rw-r--r--lib/Fuzzer/FuzzerMerge.h12
-rw-r--r--lib/Fuzzer/FuzzerMutate.cpp88
-rw-r--r--lib/Fuzzer/FuzzerMutate.h13
-rw-r--r--lib/Fuzzer/FuzzerOptions.h2
-rw-r--r--lib/Fuzzer/FuzzerRandom.h10
-rw-r--r--lib/Fuzzer/FuzzerShmem.h69
-rw-r--r--lib/Fuzzer/FuzzerShmemPosix.cpp103
-rw-r--r--lib/Fuzzer/FuzzerShmemWindows.cpp64
-rw-r--r--lib/Fuzzer/FuzzerTracePC.cpp246
-rw-r--r--lib/Fuzzer/FuzzerTracePC.h115
-rw-r--r--lib/Fuzzer/FuzzerTraceState.cpp175
-rw-r--r--lib/Fuzzer/FuzzerUtil.h4
-rw-r--r--lib/Fuzzer/FuzzerUtilPosix.cpp8
-rw-r--r--lib/Fuzzer/FuzzerUtilWindows.cpp18
-rw-r--r--lib/Fuzzer/FuzzerValueBitMap.h21
-rw-r--r--lib/Fuzzer/afl/afl_driver.cpp7
-rwxr-xr-xlib/Fuzzer/build.sh3
-rw-r--r--lib/Fuzzer/test/AbsNegAndConstant64Test.cpp2
-rw-r--r--lib/Fuzzer/test/BadStrcmpTest.cpp19
-rw-r--r--lib/Fuzzer/test/BogusInitializeTest.cpp15
-rw-r--r--lib/Fuzzer/test/CMakeLists.txt107
-rw-r--r--lib/Fuzzer/test/CustomCrossOverAndMutateTest.cpp34
-rw-r--r--lib/Fuzzer/test/CxxStringEqTest.cpp25
-rw-r--r--lib/Fuzzer/test/DSO1.cpp4
-rw-r--r--lib/Fuzzer/test/DSO2.cpp4
-rw-r--r--lib/Fuzzer/test/EquivalenceATest.cpp17
-rw-r--r--lib/Fuzzer/test/EquivalenceBTest.cpp27
-rw-r--r--lib/Fuzzer/test/FuzzerUnittest.cpp43
-rw-r--r--lib/Fuzzer/test/LargeTest.cpp37
-rw-r--r--lib/Fuzzer/test/LoadTest.cpp2
-rw-r--r--lib/Fuzzer/test/Memcmp64BytesTest.cpp20
-rw-r--r--lib/Fuzzer/test/NotinstrumentedTest.cpp (renamed from lib/Fuzzer/test/UninstrumentedTest.cpp)0
-rw-r--r--lib/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp2
-rw-r--r--lib/Fuzzer/test/RepeatedMemcmp.cpp17
-rw-r--r--lib/Fuzzer/test/SimpleCmpTest.cpp9
-rw-r--r--lib/Fuzzer/test/SingleByteInputTest.cpp17
-rw-r--r--lib/Fuzzer/test/SingleStrcmpTest.cpp12
-rw-r--r--lib/Fuzzer/test/SingleStrncmpTest.cpp3
-rw-r--r--lib/Fuzzer/test/SwapCmpTest.cpp3
-rw-r--r--lib/Fuzzer/test/TableLookupTest.cpp45
-rw-r--r--lib/Fuzzer/test/TwoDifferentBugsTest.cpp22
-rw-r--r--lib/Fuzzer/test/afl-driver-extra-stats.test2
-rw-r--r--lib/Fuzzer/test/afl-driver-stderr.test2
-rw-r--r--lib/Fuzzer/test/bad-strcmp.test1
-rw-r--r--lib/Fuzzer/test/coverage.test4
-rw-r--r--lib/Fuzzer/test/cxxstring.test2
-rw-r--r--lib/Fuzzer/test/disable-leaks.test4
-rw-r--r--lib/Fuzzer/test/dump_coverage.test26
-rw-r--r--lib/Fuzzer/test/equivalence-signals.test9
-rw-r--r--lib/Fuzzer/test/equivalence.test8
-rw-r--r--lib/Fuzzer/test/extra-counters.test6
-rw-r--r--lib/Fuzzer/test/fuzzer-customcrossover.test2
-rw-r--r--lib/Fuzzer/test/fuzzer-customcrossoverandmutate.test1
-rw-r--r--lib/Fuzzer/test/fuzzer-dirs.test4
-rw-r--r--lib/Fuzzer/test/fuzzer-jobs.test29
-rw-r--r--lib/Fuzzer/test/fuzzer-leak.test2
-rw-r--r--lib/Fuzzer/test/fuzzer-oom.test6
-rw-r--r--lib/Fuzzer/test/fuzzer-segv.test2
-rw-r--r--lib/Fuzzer/test/fuzzer-singleinputs.test2
-rw-r--r--lib/Fuzzer/test/fuzzer-traces-hooks.test26
-rw-r--r--lib/Fuzzer/test/fuzzer.test9
-rw-r--r--lib/Fuzzer/test/lit.cfg26
-rw-r--r--lib/Fuzzer/test/lit.site.cfg.in1
-rw-r--r--lib/Fuzzer/test/merge-posix.test23
-rw-r--r--lib/Fuzzer/test/merge-summary.test15
-rw-r--r--lib/Fuzzer/test/merge.test27
-rw-r--r--lib/Fuzzer/test/minimize_crash.test9
-rw-r--r--lib/Fuzzer/test/minimize_two_crashes.test16
-rw-r--r--lib/Fuzzer/test/no-coverage/CMakeLists.txt20
-rw-r--r--lib/Fuzzer/test/trace-malloc-2.test8
-rw-r--r--lib/Fuzzer/test/trace-malloc.test5
-rw-r--r--lib/Fuzzer/test/trace-pc.test2
-rw-r--r--lib/Fuzzer/test/trace-pc/CMakeLists.txt13
-rw-r--r--lib/Fuzzer/test/ubsan/CMakeLists.txt3
-rw-r--r--lib/Fuzzer/test/ulimit.test2
-rw-r--r--lib/Fuzzer/test/uninstrumented/CMakeLists.txt3
-rw-r--r--lib/Fuzzer/test/value-profile-div.test2
-rw-r--r--lib/Fuzzer/test/value-profile-mem.test2
-rw-r--r--lib/Fuzzer/test/value-profile-strcmp.test2
-rw-r--r--lib/Fuzzer/test/value-profile-strncmp.test2
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