From 379359c53a3b0728b4706a58208e9e6520ba31c8 Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Wed, 5 Oct 2016 01:09:40 +0000 Subject: [PATCH] [libFuzzer] add ShrinkValueProfileTest, move code around, NFC llvm-svn: 283286 --- llvm/lib/Fuzzer/FuzzerTracePC.cpp | 107 ++++++++++++++++++++ llvm/lib/Fuzzer/FuzzerTracePC.h | 5 + llvm/lib/Fuzzer/FuzzerTraceState.cpp | 111 +-------------------- llvm/lib/Fuzzer/test/CMakeLists.txt | 3 +- ...izeCorpusTest.cpp => ShrinkControlFlowTest.cpp} | 0 llvm/lib/Fuzzer/test/ShrinkValueProfileTest.cpp | 21 ++++ llvm/lib/Fuzzer/test/fuzzer.test | 2 +- llvm/lib/Fuzzer/test/trace-pc/CMakeLists.txt | 3 +- 8 files changed, 141 insertions(+), 111 deletions(-) rename llvm/lib/Fuzzer/test/{MinimizeCorpusTest.cpp => ShrinkControlFlowTest.cpp} (100%) create mode 100644 llvm/lib/Fuzzer/test/ShrinkValueProfileTest.cpp diff --git a/llvm/lib/Fuzzer/FuzzerTracePC.cpp b/llvm/lib/Fuzzer/FuzzerTracePC.cpp index 07e3264..c752002 100644 --- a/llvm/lib/Fuzzer/FuzzerTracePC.cpp +++ b/llvm/lib/Fuzzer/FuzzerTracePC.cpp @@ -117,6 +117,70 @@ void TracePC::PrintCoverage() { } } +// Value profile. +// We keep track of various values that affect control flow. +// These values are inserted into a bit-set-based hash map. +// Every new bit in the map is treated as a new coverage. +// +// For memcmp/strcmp/etc the interesting value is the length of the common +// prefix of the parameters. +// 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. + +void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, + size_t n) { + if (!n) return; + size_t Len = std::min(n, (size_t)32); + const uint8_t *A1 = reinterpret_cast(s1); + const uint8_t *A2 = reinterpret_cast(s2); + size_t I = 0; + for (; I < Len; I++) + if (A1[I] != A2[I]) + break; + size_t PC = reinterpret_cast(caller_pc); + size_t Idx = I; + // if (I < Len) + // Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1; + TPC.HandleValueProfile((PC & 4095) | (Idx << 12)); +} + +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); + const uint8_t *A1 = reinterpret_cast(s1); + const uint8_t *A2 = reinterpret_cast(s2); + size_t I = 0; + for (; I < Len; I++) + if (A1[I] != A2[I] || A1[I] == 0) + break; + size_t PC = reinterpret_cast(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)); +} + +ATTRIBUTE_TARGET_POPCNT +static void AddValueForCmp(void *PCptr, uint64_t Arg1, uint64_t Arg2) { + if (Arg1 == Arg2) + return; + uintptr_t PC = reinterpret_cast(PCptr); + uint64_t ArgDistance = __builtin_popcountl(Arg1 ^ Arg2) - 1; // [0,63] + uintptr_t Idx = (PC & 4095) | (ArgDistance << 12); + TPC.HandleValueProfile(Idx); +} + +static void AddValueForSingleVal(void *PCptr, uintptr_t Val) { + if (!Val) return; + uintptr_t PC = reinterpret_cast(PCptr); + uint64_t ArgDistance = __builtin_popcountl(Val) - 1; // [0,63] + uintptr_t Idx = (PC & 4095) | (ArgDistance << 12); + TPC.HandleValueProfile(Idx); +} + + + } // namespace fuzzer extern "C" { @@ -136,4 +200,47 @@ void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) { uintptr_t PC = (uintptr_t)__builtin_return_address(0); fuzzer::TPC.HandleCallerCallee(PC, Callee); } + +// TODO: this one will not be used with the newest clang. Remove it. +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1, + uint64_t Arg2) { + fuzzer::AddValueForCmp(__builtin_return_address(0), Arg1, Arg2); +} + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp8(uint64_t Arg1, int64_t Arg2) { + fuzzer::AddValueForCmp(__builtin_return_address(0), Arg1, Arg2); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp4(uint32_t Arg1, int32_t Arg2) { + fuzzer::AddValueForCmp(__builtin_return_address(0), Arg1, Arg2); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp2(uint16_t Arg1, int16_t Arg2) { + fuzzer::AddValueForCmp(__builtin_return_address(0), Arg1, Arg2); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_cmp1(uint8_t Arg1, int8_t Arg2) { + fuzzer::AddValueForCmp(__builtin_return_address(0), Arg1, Arg2); +} + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { + // TODO(kcc): support value profile here. } + +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_div4(uint32_t Val) { + fuzzer::AddValueForSingleVal(__builtin_return_address(0), Val); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_div8(uint64_t Val) { + fuzzer::AddValueForSingleVal(__builtin_return_address(0), Val); +} +__attribute__((visibility("default"))) +void __sanitizer_cov_trace_gep(uintptr_t Idx) { + fuzzer::AddValueForSingleVal(__builtin_return_address(0), Idx); +} + +} // extern "C" diff --git a/llvm/lib/Fuzzer/FuzzerTracePC.h b/llvm/lib/Fuzzer/FuzzerTracePC.h index 79ec7fb..a96a2c0 100644 --- a/llvm/lib/Fuzzer/FuzzerTracePC.h +++ b/llvm/lib/Fuzzer/FuzzerTracePC.h @@ -62,6 +62,11 @@ class TracePC { bool HasFeature(size_t Idx) { return CounterMap.Get(Idx); } + 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); + private: bool UseCounters = false; bool UseValueProfile = false; diff --git a/llvm/lib/Fuzzer/FuzzerTraceState.cpp b/llvm/lib/Fuzzer/FuzzerTraceState.cpp index ace4527..cea348b 100644 --- a/llvm/lib/Fuzzer/FuzzerTraceState.cpp +++ b/llvm/lib/Fuzzer/FuzzerTraceState.cpp @@ -251,68 +251,6 @@ static size_t InternalStrnlen(const char *S, size_t MaxLen) { return Len; } -// Value profile. -// We keep track of various values that affect control flow. -// These values are inserted into a bit-set-based hash map. -// Every new bit in the map is treated as a new coverage. -// -// For memcmp/strcmp/etc the interesting value is the length of the common -// prefix of the parameters. -// 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. - -static void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, - size_t n) { - if (!n) return; - size_t Len = std::min(n, (size_t)32); - const uint8_t *A1 = reinterpret_cast(s1); - const uint8_t *A2 = reinterpret_cast(s2); - size_t I = 0; - for (; I < Len; I++) - if (A1[I] != A2[I]) - break; - size_t PC = reinterpret_cast(caller_pc); - size_t Idx = I; - // if (I < Len) - // Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1; - TPC.HandleValueProfile((PC & 4095) | (Idx << 12)); -} - -static void 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); - const uint8_t *A1 = reinterpret_cast(s1); - const uint8_t *A2 = reinterpret_cast(s2); - size_t I = 0; - for (; I < Len; I++) - if (A1[I] != A2[I] || A1[I] == 0) - break; - size_t PC = reinterpret_cast(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)); -} - -ATTRIBUTE_TARGET_POPCNT -static void AddValueForCmp(void *PCptr, uint64_t Arg1, uint64_t Arg2) { - if (Arg1 == Arg2) - return; - uintptr_t PC = reinterpret_cast(PCptr); - uint64_t ArgDistance = __builtin_popcountl(Arg1 ^ Arg2) - 1; // [0,63] - uintptr_t Idx = (PC & 4095) | (ArgDistance << 12); - TPC.HandleValueProfile(Idx); -} - -static void AddValueForSingleVal(void *PCptr, uintptr_t Val) { - if (!Val) return; - uintptr_t PC = reinterpret_cast(PCptr); - uint64_t ArgDistance = __builtin_popcountl(Val) - 1; // [0,63] - uintptr_t Idx = (PC & 4095) | (ArgDistance << 12); - TPC.HandleValueProfile(Idx); -} - } // namespace fuzzer using fuzzer::TS; @@ -328,7 +266,7 @@ extern "C" { #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) { - fuzzer::AddValueForMemcmp(caller_pc, s1, s2, n); + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n); if (!RecordingMemcmp) return; if (result == 0) return; // No reason to mutate. if (n <= 1) return; // Not interesting. @@ -338,7 +276,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) { - fuzzer::AddValueForStrcmp(caller_pc, s1, s2, n); + fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, n); if (!RecordingMemcmp) return; if (result == 0) return; // No reason to mutate. size_t Len1 = fuzzer::InternalStrnlen(s1, n); @@ -352,7 +290,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) { - fuzzer::AddValueForStrcmp(caller_pc, s1, s2, 64); + fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, 64); if (!RecordingMemcmp) return; if (result == 0) return; // No reason to mutate. size_t Len1 = strlen(s1); @@ -386,47 +324,4 @@ void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1, } #endif // LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS - -// TODO: this one will not be used with the newest clang. Remove it. -__attribute__((visibility("default"))) -void __sanitizer_cov_trace_cmp(uint64_t SizeAndType, uint64_t Arg1, - uint64_t Arg2) { - fuzzer::AddValueForCmp(__builtin_return_address(0), Arg1, Arg2); -} - -__attribute__((visibility("default"))) -void __sanitizer_cov_trace_cmp8(uint64_t Arg1, int64_t Arg2) { - fuzzer::AddValueForCmp(__builtin_return_address(0), Arg1, Arg2); -} -__attribute__((visibility("default"))) -void __sanitizer_cov_trace_cmp4(uint32_t Arg1, int32_t Arg2) { - fuzzer::AddValueForCmp(__builtin_return_address(0), Arg1, Arg2); -} -__attribute__((visibility("default"))) -void __sanitizer_cov_trace_cmp2(uint16_t Arg1, int16_t Arg2) { - fuzzer::AddValueForCmp(__builtin_return_address(0), Arg1, Arg2); -} -__attribute__((visibility("default"))) -void __sanitizer_cov_trace_cmp1(uint8_t Arg1, int8_t Arg2) { - fuzzer::AddValueForCmp(__builtin_return_address(0), Arg1, Arg2); -} - -__attribute__((visibility("default"))) -void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { - // TODO(kcc): support value profile here. -} - -__attribute__((visibility("default"))) -void __sanitizer_cov_trace_div4(uint32_t Val) { - fuzzer::AddValueForSingleVal(__builtin_return_address(0), Val); -} -__attribute__((visibility("default"))) -void __sanitizer_cov_trace_div8(uint64_t Val) { - fuzzer::AddValueForSingleVal(__builtin_return_address(0), Val); -} -__attribute__((visibility("default"))) -void __sanitizer_cov_trace_gep(uintptr_t Idx) { - fuzzer::AddValueForSingleVal(__builtin_return_address(0), Idx); -} - } // extern "C" diff --git a/llvm/lib/Fuzzer/test/CMakeLists.txt b/llvm/lib/Fuzzer/test/CMakeLists.txt index bf8e552..d2ccc23 100644 --- a/llvm/lib/Fuzzer/test/CMakeLists.txt +++ b/llvm/lib/Fuzzer/test/CMakeLists.txt @@ -80,7 +80,6 @@ set(Tests LeakTest LeakTimeoutTest LoadTest - MinimizeCorpusTest NullDerefTest NullDerefOnEmptyTest NthRunCrashTest @@ -98,6 +97,8 @@ set(Tests SingleStrcmpTest SingleStrncmpTest SpamyTest + ShrinkControlFlowTest + ShrinkValueProfileTest StrcmpTest StrncmpTest StrstrTest diff --git a/llvm/lib/Fuzzer/test/MinimizeCorpusTest.cpp b/llvm/lib/Fuzzer/test/ShrinkControlFlowTest.cpp similarity index 100% rename from llvm/lib/Fuzzer/test/MinimizeCorpusTest.cpp rename to llvm/lib/Fuzzer/test/ShrinkControlFlowTest.cpp diff --git a/llvm/lib/Fuzzer/test/ShrinkValueProfileTest.cpp b/llvm/lib/Fuzzer/test/ShrinkValueProfileTest.cpp new file mode 100644 index 0000000..9ff5b19 --- /dev/null +++ b/llvm/lib/Fuzzer/test/ShrinkValueProfileTest.cpp @@ -0,0 +1,21 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test that we can find the minimal item in the corpus (3 bytes: "FUZ"). +#include +#include +#include +#include +#include + +static volatile uint32_t Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < sizeof(uint32_t)) return 0; + uint32_t X; + size_t Offset = Size < 8 ? 0 : Size / 2; + memcpy(&X, Data + Offset, sizeof(uint32_t)); + Sink = X == 0xAABBCCDD; + return 0; +} + diff --git a/llvm/lib/Fuzzer/test/fuzzer.test b/llvm/lib/Fuzzer/test/fuzzer.test index b48d734..98139c0 100644 --- a/llvm/lib/Fuzzer/test/fuzzer.test +++ b/llvm/lib/Fuzzer/test/fuzzer.test @@ -54,6 +54,6 @@ DSO: INFO: Loaded 3 modules DSO: BINGO RUN: LLVMFuzzer-SimpleTest-TracePC -exit_on_src_pos=SimpleTest.cpp:17 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS -RUN: LLVMFuzzer-MinimizeCorpusTest-TracePC -exit_on_src_pos=MinimizeCorpusTest.cpp:23 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS +RUN: LLVMFuzzer-ShrinkControlFlowTest-TracePC -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. diff --git a/llvm/lib/Fuzzer/test/trace-pc/CMakeLists.txt b/llvm/lib/Fuzzer/test/trace-pc/CMakeLists.txt index 3def727..a1eeb72 100644 --- a/llvm/lib/Fuzzer/test/trace-pc/CMakeLists.txt +++ b/llvm/lib/Fuzzer/test/trace-pc/CMakeLists.txt @@ -8,7 +8,8 @@ set(TracePCTests CounterTest CallerCalleeTest NullDerefTest - MinimizeCorpusTest + ShrinkControlFlowTest + ShrinkValueProfileTest FullCoverageSetTest ) -- 2.7.4