diff --git a/llvm/lib/Fuzzer/FuzzerDriver.cpp b/llvm/lib/Fuzzer/FuzzerDriver.cpp index eaa308cac723..4ae14bb0bb70 100644 --- a/llvm/lib/Fuzzer/FuzzerDriver.cpp +++ b/llvm/lib/Fuzzer/FuzzerDriver.cpp @@ -270,6 +270,7 @@ int FuzzerDriver(const std::vector &Args, Options.UseCounters = Flags.use_counters; Options.UseIndirCalls = Flags.use_indir_calls; Options.UseTraces = Flags.use_traces; + Options.UseMemcmp = Flags.use_memcmp; Options.ShuffleAtStartUp = Flags.shuffle; Options.PreferSmallDuringInitialShuffle = Flags.prefer_small_during_initial_shuffle; diff --git a/llvm/lib/Fuzzer/FuzzerFlags.def b/llvm/lib/Fuzzer/FuzzerFlags.def index 977efb76922b..a36b6ba50bc5 100644 --- a/llvm/lib/Fuzzer/FuzzerFlags.def +++ b/llvm/lib/Fuzzer/FuzzerFlags.def @@ -38,6 +38,8 @@ FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be " 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_traces, 0, "Experimental: use instruction traces") +FUZZER_FLAG_INT(use_memcmp, 1, + "Use hints from intercepting memcmp, strcmp, etc") FUZZER_FLAG_INT(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn" " this number of jobs in separate worker processes" " with stdout/stderr redirected to fuzz-JOB.log.") diff --git a/llvm/lib/Fuzzer/FuzzerInternal.h b/llvm/lib/Fuzzer/FuzzerInternal.h index 7d4de95afd92..6c16b7642e1e 100644 --- a/llvm/lib/Fuzzer/FuzzerInternal.h +++ b/llvm/lib/Fuzzer/FuzzerInternal.h @@ -83,6 +83,7 @@ class Fuzzer { bool UseCounters = false; bool UseIndirCalls = true; bool UseTraces = false; + bool UseMemcmp = true; bool UseFullCoverageSet = false; bool Reload = true; bool ShuffleAtStartUp = true; diff --git a/llvm/lib/Fuzzer/FuzzerTraceState.cpp b/llvm/lib/Fuzzer/FuzzerTraceState.cpp index 153b2e63aa3e..1948b282c94c 100644 --- a/llvm/lib/Fuzzer/FuzzerTraceState.cpp +++ b/llvm/lib/Fuzzer/FuzzerTraceState.cpp @@ -172,6 +172,10 @@ struct TraceBasedMutation { const size_t TraceBasedMutation::kMaxSize; +// Declared as static globals for faster checks inside the hooks. +static bool RecordingTraces = false; +static bool RecordingMemcmp = false; + class TraceState { public: TraceState(UserSuppliedFuzzer &USF, const Fuzzer::FuzzingOptions &Options, @@ -206,15 +210,17 @@ class TraceState { const uint8_t *DesiredData, size_t DataSize); void StartTraceRecording() { - if (!Options.UseTraces) return; - RecordingTraces = true; + if (!Options.UseTraces && !Options.UseMemcmp) return; + RecordingTraces = Options.UseTraces; + RecordingMemcmp = Options.UseMemcmp; NumMutations = 0; USF.GetMD().ClearAutoDictionary(); } void StopTraceRecording() { - if (!RecordingTraces) return; + if (!RecordingTraces && !RecordingMemcmp) return; RecordingTraces = false; + RecordingMemcmp = false; for (size_t i = 0; i < NumMutations; i++) { auto &M = Mutations[i]; Unit U(M.Data, M.Data + M.Size); @@ -260,7 +266,6 @@ class TraceState { Signed >>= 16; return Signed == 0 || Signed == -1L; } - bool RecordingTraces = false; static const size_t kMaxMutations = 1 << 16; size_t NumMutations; TraceBasedMutation Mutations[kMaxMutations]; @@ -320,7 +325,7 @@ void TraceState::DFSanMemcmpCallback(size_t CmpSize, const uint8_t *Data1, dfsan_label L2) { assert(ReallyHaveDFSan()); - if (!RecordingTraces || !IsMyThread) return; + if (!RecordingMemcmp || !IsMyThread) return; if (L1 == 0 && L2 == 0) return; // Not actionable. if (L1 != 0 && L2 != 0) @@ -417,7 +422,7 @@ void TraceState::TraceCmpCallback(uintptr_t PC, size_t CmpSize, size_t CmpType, void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1, const uint8_t *Data2) { - if (!RecordingTraces || !IsMyThread) return; + if (!RecordingMemcmp || !IsMyThread) return; CmpSize = std::min(CmpSize, TraceBasedMutation::kMaxSize); int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize); int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize); @@ -462,14 +467,14 @@ void Fuzzer::StopTraceRecording() { } void Fuzzer::AssignTaintLabels(uint8_t *Data, size_t Size) { - if (!Options.UseTraces) return; + if (!Options.UseTraces && !Options.UseMemcmp) return; if (!ReallyHaveDFSan()) return; for (size_t i = 0; i < Size; i++) dfsan_set_label(i + 1, &Data[i], 1); } void Fuzzer::InitializeTraceState() { - if (!Options.UseTraces) return; + if (!Options.UseTraces && !Options.UseMemcmp) return; TS = new TraceState(USF, Options, &CurrentUnitData, &CurrentUnitSize); if (ReallyHaveDFSan()) { for (size_t i = 0; i < static_cast(Options.MaxLen); i++) { @@ -492,12 +497,14 @@ static size_t InternalStrnlen(const char *S, size_t MaxLen) { } // namespace fuzzer using fuzzer::TS; +using fuzzer::RecordingTraces; +using fuzzer::RecordingMemcmp; extern "C" { void __dfsw___sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1, uint64_t Arg2, dfsan_label L0, dfsan_label L1, dfsan_label L2) { - if (!TS) return; + if (!RecordingTraces) return; assert(L0 == 0); uintptr_t PC = reinterpret_cast(__builtin_return_address(0)); uint64_t CmpSize = (SizeAndType >> 32) / 8; @@ -507,7 +514,7 @@ void __dfsw___sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1, void __dfsw___sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases, dfsan_label L1, dfsan_label L2) { - if (!TS) return; + if (!RecordingTraces) return; uintptr_t PC = reinterpret_cast(__builtin_return_address(0)); TS->DFSanSwitchCallback(PC, Cases[1], Val, Cases[0], Cases+2, L1); } @@ -515,7 +522,7 @@ void __dfsw___sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases, void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2, size_t n, dfsan_label s1_label, dfsan_label s2_label, dfsan_label n_label) { - if (!TS) return; + if (!RecordingMemcmp) return; dfsan_label L1 = dfsan_read_label(s1, n); dfsan_label L2 = dfsan_read_label(s2, n); TS->DFSanMemcmpCallback(n, reinterpret_cast(s1), @@ -525,7 +532,7 @@ void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2, void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2, size_t n, dfsan_label s1_label, dfsan_label s2_label, dfsan_label n_label) { - if (!TS) return; + if (!RecordingMemcmp) return; n = std::min(n, fuzzer::InternalStrnlen(s1, n)); n = std::min(n, fuzzer::InternalStrnlen(s2, n)); dfsan_label L1 = dfsan_read_label(s1, n); @@ -536,7 +543,7 @@ void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2, void dfsan_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2, dfsan_label s1_label, dfsan_label s2_label) { - if (!TS) return; + if (!RecordingMemcmp) return; size_t Len1 = strlen(s1); size_t Len2 = strlen(s2); size_t N = std::min(Len1, Len2); @@ -555,7 +562,7 @@ void dfsan_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2, #if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2, size_t n, int result) { - if (!TS) return; + if (!RecordingMemcmp) return; if (result == 0) return; // No reason to mutate. if (n <= 1) return; // Not interesting. TS->TraceMemcmpCallback(n, reinterpret_cast(s1), @@ -564,7 +571,7 @@ void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2, size_t n, int result) { - if (!TS) return; + if (!RecordingMemcmp) return; if (result == 0) return; // No reason to mutate. size_t Len1 = fuzzer::InternalStrnlen(s1, n); size_t Len2 = fuzzer::InternalStrnlen(s2, n); @@ -577,7 +584,7 @@ void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2, int result) { - if (!TS) return; + if (!RecordingMemcmp) return; if (result == 0) return; // No reason to mutate. size_t Len1 = strlen(s1); size_t Len2 = strlen(s2); @@ -592,7 +599,7 @@ void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, __attribute__((visibility("default"))) void __sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1, uint64_t Arg2) { - if (!TS) return; + if (!RecordingTraces) return; uintptr_t PC = reinterpret_cast(__builtin_return_address(0)); uint64_t CmpSize = (SizeAndType >> 32) / 8; uint64_t Type = (SizeAndType << 32) >> 32; @@ -601,7 +608,7 @@ void __sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1, __attribute__((visibility("default"))) void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { - if (!TS) return; + if (!RecordingTraces) return; uintptr_t PC = reinterpret_cast(__builtin_return_address(0)); TS->TraceSwitchCallback(PC, Cases[1], Val, Cases[0], Cases + 2); } diff --git a/llvm/lib/Fuzzer/test/fuzzer-traces.test b/llvm/lib/Fuzzer/test/fuzzer-traces.test index 12d4e9b5e45e..59a32024f6d6 100644 --- a/llvm/lib/Fuzzer/test/fuzzer-traces.test +++ b/llvm/lib/Fuzzer/test/fuzzer-traces.test @@ -4,14 +4,14 @@ Done10000000: Done 10000000 runs in RUN: not LLVMFuzzer-SimpleCmpTest -use_traces=1 -seed=1 -runs=10000001 2>&1 | FileCheck %s -RUN: not LLVMFuzzer-MemcmpTest -use_traces=1 -seed=4294967295 -runs=100000 2>&1 | FileCheck %s -RUN: LLVMFuzzer-MemcmpTest -seed=4294967295 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 +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-StrncmpTest -use_traces=1 -seed=1 -runs=100000 2>&1 | FileCheck %s -RUN: LLVMFuzzer-StrncmpTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 +RUN: not LLVMFuzzer-StrncmpTest -seed=1 -runs=100000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-StrncmpTest -use_memcmp=0 -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 -RUN: not LLVMFuzzer-StrcmpTest -use_traces=1 -seed=1 -runs=200000 2>&1 | FileCheck %s -RUN: LLVMFuzzer-StrcmpTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 +RUN: not LLVMFuzzer-StrcmpTest -seed=1 -runs=200000 2>&1 | FileCheck %s +RUN: LLVMFuzzer-StrcmpTest -use_memcmp=0 -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 RUN: not LLVMFuzzer-SwitchTest -use_traces=1 -seed=1 -runs=1000002 2>&1 | FileCheck %s RUN: LLVMFuzzer-SwitchTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000 @@ -19,7 +19,7 @@ RUN: LLVMFuzzer-SwitchTest -seed=1 -runs=1000000 2>&1 | FileC RUN: not LLVMFuzzer-SimpleHashTest -use_traces=1 -seed=1 -runs=10000000 2>&1 | FileCheck %s RUN: LLVMFuzzer-SimpleHashTest -seed=1 -runs=10000000 2>&1 | FileCheck %s --check-prefix=Done10000000 -RUN: LLVMFuzzer-RepeatedMemcmp -use_traces=1 -seed=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=RECOMMENDED_DICT +RUN: LLVMFuzzer-RepeatedMemcmp -seed=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=RECOMMENDED_DICT RECOMMENDED_DICT:###### Recommended dictionary. ###### RECOMMENDED_DICT-DAG: "foo" RECOMMENDED_DICT-DAG: "bar"