From: Kostya Serebryany Date: Thu, 23 Mar 2017 22:43:12 +0000 (+0000) Subject: [libFuzzer] create experimental support for user-provided coverage signal X-Git-Tag: llvmorg-5.0.0-rc1~9295 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6ca44f91617d2d6f6d1dd0bb371f275a6a1d1bc6;p=platform%2Fupstream%2Fllvm.git [libFuzzer] create experimental support for user-provided coverage signal llvm-svn: 298654 --- diff --git a/llvm/lib/Fuzzer/CMakeLists.txt b/llvm/lib/Fuzzer/CMakeLists.txt index f490b36..59cef04 100644 --- a/llvm/lib/Fuzzer/CMakeLists.txt +++ b/llvm/lib/Fuzzer/CMakeLists.txt @@ -14,6 +14,7 @@ if( LLVM_USE_SANITIZE_COVERAGE ) FuzzerExtFunctionsDlsym.cpp FuzzerExtFunctionsDlsymWin.cpp FuzzerExtFunctionsWeak.cpp + FuzzerExtraCounters.cpp FuzzerIO.cpp FuzzerIOPosix.cpp FuzzerIOWindows.cpp diff --git a/llvm/lib/Fuzzer/FuzzerDefs.h b/llvm/lib/Fuzzer/FuzzerDefs.h index 6c1f6a1..6b94c68 100644 --- a/llvm/lib/Fuzzer/FuzzerDefs.h +++ b/llvm/lib/Fuzzer/FuzzerDefs.h @@ -55,8 +55,17 @@ #define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) -#define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS ATTRIBUTE_NO_SANITIZE_MEMORY - +#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) @@ -97,6 +106,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/llvm/lib/Fuzzer/FuzzerExtraCounters.cpp b/llvm/lib/Fuzzer/FuzzerExtraCounters.cpp new file mode 100644 index 0000000..07dbe0f --- /dev/null +++ b/llvm/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(ExtraCountersBegin()); + uintptr_t *End = reinterpret_cast(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/llvm/lib/Fuzzer/FuzzerTracePC.cpp b/llvm/lib/Fuzzer/FuzzerTracePC.cpp index c234f16..c73008d 100644 --- a/llvm/lib/Fuzzer/FuzzerTracePC.cpp +++ b/llvm/lib/Fuzzer/FuzzerTracePC.cpp @@ -27,7 +27,7 @@ // The coverage counters and PCs. // These are declared as global variables named "__sancov_*" to simplify // experiments with inlined instrumentation. -alignas(8) ATTRIBUTE_INTERFACE +alignas(64) ATTRIBUTE_INTERFACE uint8_t __sancov_trace_pc_guard_8bit_counters[fuzzer::TracePC::kNumPCs]; ATTRIBUTE_INTERFACE diff --git a/llvm/lib/Fuzzer/FuzzerTracePC.h b/llvm/lib/Fuzzer/FuzzerTracePC.h index 8054aed..c7b8dfe 100644 --- a/llvm/lib/Fuzzer/FuzzerTracePC.h +++ b/llvm/lib/Fuzzer/FuzzerTracePC.h @@ -61,6 +61,7 @@ class TracePC { void ResetMaps() { ValueProfileMap.Reset(); memset(Counters(), 0, GetNumPCs()); + ClearExtraCounters(); } void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize); @@ -107,36 +108,48 @@ private: ValueBitMap ValueProfileMap; }; -template -size_t TracePC::CollectFeatures(Callback CB) const { +template // 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(Begin) % 64)); + for (auto P = Begin; P < End; P += Step) + if (LargeType Bundle = *reinterpret_cast(P)) + for (size_t I = 0; I < Step; I++, Bundle >>= 8) + if (uint8_t V = Bundle & 0xff) + Handle8bitCounter(FirstFeature + P - Begin + I, V); +} + +template // bool Callback(size_t Feature) +ATTRIBUTE_NO_SANITIZE_ALL +__attribute__((noinline)) +size_t TracePC::CollectFeatures(Callback HandleFeature) const { size_t Res = 0; - const size_t Step = 8; uint8_t *Counters = this->Counters(); - assert(reinterpret_cast(Counters) % Step == 0); size_t N = GetNumPCs(); - N = (N + Step - 1) & ~(Step - 1); // Round up. - for (size_t Idx = 0; Idx < N; Idx += Step) { - uint64_t Bundle = *reinterpret_cast(&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; - 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++; - } - } + 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; + if (HandleFeature(Idx * 8 + Bit)) + Res++; + }; + + ForEachNonZeroByte(Counters, Counters + N, 0, Handle8bitCounter); + ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), N * 8, + Handle8bitCounter); + if (UseValueProfile) ValueProfileMap.ForEach([&](size_t Idx) { - if (CB(N * 8 + Idx)) + if (HandleFeature(N * 8 + Idx)) Res++; }); return Res; diff --git a/llvm/lib/Fuzzer/test/CMakeLists.txt b/llvm/lib/Fuzzer/test/CMakeLists.txt index 2a31a80..1e68a1f 100644 --- a/llvm/lib/Fuzzer/test/CMakeLists.txt +++ b/llvm/lib/Fuzzer/test/CMakeLists.txt @@ -123,6 +123,7 @@ set(Tests SwapCmpTest SwitchTest Switch2Test + TableLookupTest ThreadedLeakTest ThreadedTest TimeoutTest diff --git a/llvm/lib/Fuzzer/test/FuzzerUnittest.cpp b/llvm/lib/Fuzzer/test/FuzzerUnittest.cpp index 5eb915a..5705396 100644 --- a/llvm/lib/Fuzzer/test/FuzzerUnittest.cpp +++ b/llvm/lib/Fuzzer/test/FuzzerUnittest.cpp @@ -10,6 +10,7 @@ #include "FuzzerDictionary.h" #include "FuzzerMerge.h" #include "FuzzerMutate.h" +#include "FuzzerTracePC.h" #include "FuzzerRandom.h" #include "gtest/gtest.h" #include @@ -750,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 > 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/llvm/lib/Fuzzer/test/TableLookupTest.cpp b/llvm/lib/Fuzzer/test/TableLookupTest.cpp new file mode 100644 index 0000000..165afdb --- /dev/null +++ b/llvm/lib/Fuzzer/test/TableLookupTest.cpp @@ -0,0 +1,43 @@ +// 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 +#include +#include +#include +#include +#include + +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. +alignas(64) __attribute__((section("__libfuzzer_extra_counters"))) +static uint8_t Counters[N]; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + static std::set 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/llvm/lib/Fuzzer/test/extra-counters.test b/llvm/lib/Fuzzer/test/extra-counters.test new file mode 100644 index 0000000..61fce44 --- /dev/null +++ b/llvm/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]}}