From a2bcec7132cca9307e8fc713fa3c928c60b4c02b Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Fri, 11 Aug 2017 23:03:22 +0000 Subject: [PATCH] [libFuzzer] experimental support for Clang's coverage (fprofile-instr-generate), Linux-only llvm-svn: 310771 --- lib/Fuzzer/CMakeLists.txt | 1 + lib/Fuzzer/FuzzerClangCounters.cpp | 49 ++++++++++++++++++++ lib/Fuzzer/FuzzerDefs.h | 4 ++ lib/Fuzzer/FuzzerTracePC.cpp | 20 +++++--- lib/Fuzzer/FuzzerTracePC.h | 33 +++++++++---- lib/Fuzzer/test/fprofile-instr-generate.test | 7 +++ 6 files changed, 99 insertions(+), 15 deletions(-) create mode 100644 lib/Fuzzer/FuzzerClangCounters.cpp create mode 100644 lib/Fuzzer/test/fprofile-instr-generate.test diff --git a/lib/Fuzzer/CMakeLists.txt b/lib/Fuzzer/CMakeLists.txt index daff0e693bf..46fac24c385 100644 --- a/lib/Fuzzer/CMakeLists.txt +++ b/lib/Fuzzer/CMakeLists.txt @@ -31,6 +31,7 @@ endif() if (LIBFUZZER_ENABLE) add_library(LLVMFuzzerNoMainObjects OBJECT + FuzzerClangCounters.cpp FuzzerCrossOver.cpp FuzzerDriver.cpp FuzzerExtFunctionsDlsym.cpp diff --git a/lib/Fuzzer/FuzzerClangCounters.cpp b/lib/Fuzzer/FuzzerClangCounters.cpp new file mode 100644 index 00000000000..f69e922cf00 --- /dev/null +++ b/lib/Fuzzer/FuzzerClangCounters.cpp @@ -0,0 +1,49 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// Coverage counters from Clang's SourceBasedCodeCoverage. +//===----------------------------------------------------------------------===// + +// Support for SourceBasedCodeCoverage is experimental: +// * Works only for the main binary, not DSOs yet. +// * Works only on Linux. +// * Does not implement print_pcs/print_coverage yet. +// * Is not fully evaluated for performance and sensitivity. +// We expect large performance drop due to 64-bit counters, +// and *maybe* better sensitivity due to more fine-grained counters. +// Preliminary comparison on a single benchmark (RE2) shows +// a bit worse sensitivity though. + +#include "FuzzerDefs.h" + +#if LIBFUZZER_LINUX +__attribute__((weak)) extern uint64_t __start___llvm_prf_cnts; +__attribute__((weak)) extern uint64_t __stop___llvm_prf_cnts; +namespace fuzzer { +uint64_t *ClangCountersBegin() { return &__start___llvm_prf_cnts; } +uint64_t *ClangCountersEnd() { return &__stop___llvm_prf_cnts; } +} // namespace fuzzer +#else +// TODO: Implement on Mac (if the data shows it's worth it). +//__attribute__((visibility("hidden"))) +//extern uint64_t CountersStart __asm("section$start$__DATA$__llvm_prf_cnts"); +//__attribute__((visibility("hidden"))) +//extern uint64_t CountersEnd __asm("section$end$__DATA$__llvm_prf_cnts"); +namespace fuzzer { +uint64_t *ClangCountersBegin() { return nullptr; } +uint64_t *ClangCountersEnd() { return nullptr; } +} // namespace fuzzer +#endif + +namespace fuzzer { +ATTRIBUTE_NO_SANITIZE_ALL +void ClearClangCounters() { // hand-written memset, don't asan-ify. + for (auto P = ClangCountersBegin(); P < ClangCountersEnd(); P++) + *P = 0; +} +} diff --git a/lib/Fuzzer/FuzzerDefs.h b/lib/Fuzzer/FuzzerDefs.h index 27f5719236d..bbb44514aab 100644 --- a/lib/Fuzzer/FuzzerDefs.h +++ b/lib/Fuzzer/FuzzerDefs.h @@ -123,6 +123,10 @@ uint8_t *ExtraCountersBegin(); uint8_t *ExtraCountersEnd(); void ClearExtraCounters(); +uint64_t *ClangCountersBegin(); +uint64_t *ClangCountersEnd(); +void ClearClangCounters(); + } // namespace fuzzer #endif // LLVM_FUZZER_DEFS_H diff --git a/lib/Fuzzer/FuzzerTracePC.cpp b/lib/Fuzzer/FuzzerTracePC.cpp index d038374dc43..a54a8c1e99f 100644 --- a/lib/Fuzzer/FuzzerTracePC.cpp +++ b/lib/Fuzzer/FuzzerTracePC.cpp @@ -126,6 +126,8 @@ void TracePC::PrintModuleInfo() { _Exit(1); } } + if (size_t NumClangCounters = ClangCountersEnd() - ClangCountersBegin()) + Printf("INFO: %zd Clang Coverage Counters\n", NumClangCounters); } ATTRIBUTE_NO_SANITIZE_ALL @@ -137,13 +139,12 @@ void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) { } void TracePC::UpdateObservedPCs() { + auto Observe = [&](uintptr_t PC) { + bool Inserted = ObservedPCs.insert(PC).second; + if (Inserted && DoPrintNewPCs) + PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PC + 1); + }; if (NumPCsInPCTables) { - auto Observe = [&](uintptr_t PC) { - bool Inserted = ObservedPCs.insert(PC).second; - if (Inserted && DoPrintNewPCs) - PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PC + 1); - }; - if (NumInline8bitCounters == NumPCsInPCTables) { for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) { uint8_t *Beg = ModuleCounters[i].Start; @@ -167,6 +168,13 @@ void TracePC::UpdateObservedPCs() { } } } + if (size_t NumClangCounters = + ClangCountersEnd() - ClangCountersBegin()) { + auto P = ClangCountersBegin(); + for (size_t Idx = 0; Idx < NumClangCounters; Idx++) + if (P[Idx]) + Observe((uintptr_t)Idx); + } } inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) { diff --git a/lib/Fuzzer/FuzzerTracePC.h b/lib/Fuzzer/FuzzerTracePC.h index ea6794c75aa..40827b32828 100644 --- a/lib/Fuzzer/FuzzerTracePC.h +++ b/lib/Fuzzer/FuzzerTracePC.h @@ -91,6 +91,7 @@ class TracePC { memset(Counters(), 0, GetNumPCs()); ClearExtraCounters(); ClearInlineCounters(); + ClearClangCounters(); } void ClearInlineCounters(); @@ -196,14 +197,9 @@ void ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End, Handle8bitCounter(FirstFeature, P - Begin, V); } -template // bool Callback(size_t Feature) -ATTRIBUTE_NO_SANITIZE_ADDRESS -__attribute__((noinline)) -void TracePC::CollectFeatures(Callback HandleFeature) const { - uint8_t *Counters = this->Counters(); - size_t N = GetNumPCs(); - auto Handle8bitCounter = [&](size_t FirstFeature, - size_t Idx, uint8_t Counter) { +// Given a non-zero Counters returns a number in [0,7]. +template +unsigned CounterToFeature(T Counter) { assert(Counter); unsigned Bit = 0; /**/ if (Counter >= 128) Bit = 7; @@ -213,7 +209,18 @@ void TracePC::CollectFeatures(Callback HandleFeature) const { else if (Counter >= 4) Bit = 3; else if (Counter >= 3) Bit = 2; else if (Counter >= 2) Bit = 1; - HandleFeature(FirstFeature + Idx * 8 + Bit); + return Bit; +} + +template // bool Callback(size_t Feature) +ATTRIBUTE_NO_SANITIZE_ADDRESS +__attribute__((noinline)) +void TracePC::CollectFeatures(Callback HandleFeature) const { + uint8_t *Counters = this->Counters(); + size_t N = GetNumPCs(); + auto Handle8bitCounter = [&](size_t FirstFeature, + size_t Idx, uint8_t Counter) { + HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Counter)); }; size_t FirstFeature = 0; @@ -231,6 +238,14 @@ void TracePC::CollectFeatures(Callback HandleFeature) const { } } + if (size_t NumClangCounters = ClangCountersEnd() - ClangCountersBegin()) { + auto P = ClangCountersBegin(); + for (size_t Idx = 0; Idx < NumClangCounters; Idx++) + if (auto Cnt = P[Idx]) + HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Cnt)); + FirstFeature += NumClangCounters; + } + ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), FirstFeature, Handle8bitCounter); FirstFeature += (ExtraCountersEnd() - ExtraCountersBegin()) * 8; diff --git a/lib/Fuzzer/test/fprofile-instr-generate.test b/lib/Fuzzer/test/fprofile-instr-generate.test new file mode 100644 index 00000000000..94eb3cc1a59 --- /dev/null +++ b/lib/Fuzzer/test/fprofile-instr-generate.test @@ -0,0 +1,7 @@ +# Test libFuzzer + -fprofile-instr-generate +REQUIRES: linux +RUN: %cpp_compiler %S/SimpleTest.cpp -fsanitize-coverage=0 -fprofile-instr-generate -o %t-SimpleTest-fprofile-instr-generate +CHECK-NOT: INFO: Loaded 1 modules +CHECK: INFO: {{.*}} Clang Coverage Counters +CHECK: BINGO +RUN: not %t-SimpleTest-fprofile-instr-generate -runs=1000000 -seed=1 2>&1 | FileCheck %s