[libFuzzer] create experimental support for user-provided coverage signal

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@298654 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Kostya Serebryany 2017-03-23 22:43:12 +00:00
parent 3d91fe7c1a
commit 550e23d356
9 changed files with 169 additions and 28 deletions

View File

@ -14,6 +14,7 @@ if( LLVM_USE_SANITIZE_COVERAGE )
FuzzerExtFunctionsDlsym.cpp
FuzzerExtFunctionsDlsymWin.cpp
FuzzerExtFunctionsWeak.cpp
FuzzerExtraCounters.cpp
FuzzerIO.cpp
FuzzerIOPosix.cpp
FuzzerIOWindows.cpp

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <class Callback>
size_t TracePC::CollectFeatures(Callback CB) const {
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))
size_t TracePC::CollectFeatures(Callback HandleFeature) const {
size_t Res = 0;
const size_t Step = 8;
uint8_t *Counters = this->Counters();
assert(reinterpret_cast<uintptr_t>(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<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;
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;

View File

@ -123,6 +123,7 @@ set(Tests
SwapCmpTest
SwitchTest
Switch2Test
TableLookupTest
ThreadedLeakTest
ThreadedTest
TimeoutTest

View File

@ -10,6 +10,7 @@
#include "FuzzerDictionary.h"
#include "FuzzerMerge.h"
#include "FuzzerMutate.h"
#include "FuzzerTracePC.h"
#include "FuzzerRandom.h"
#include "gtest/gtest.h"
#include <memory>
@ -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<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);
}

View File

@ -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 <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.
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<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;
}

View File

@ -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]}}