mirror of
https://github.com/RPCS3/llvm.git
synced 2024-12-12 14:20:33 +00:00
[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:
parent
3d91fe7c1a
commit
550e23d356
@ -14,6 +14,7 @@ if( LLVM_USE_SANITIZE_COVERAGE )
|
||||
FuzzerExtFunctionsDlsym.cpp
|
||||
FuzzerExtFunctionsDlsymWin.cpp
|
||||
FuzzerExtFunctionsWeak.cpp
|
||||
FuzzerExtraCounters.cpp
|
||||
FuzzerIO.cpp
|
||||
FuzzerIOPosix.cpp
|
||||
FuzzerIOWindows.cpp
|
||||
|
@ -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
|
||||
|
41
lib/Fuzzer/FuzzerExtraCounters.cpp
Normal file
41
lib/Fuzzer/FuzzerExtraCounters.cpp
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -123,6 +123,7 @@ set(Tests
|
||||
SwapCmpTest
|
||||
SwitchTest
|
||||
Switch2Test
|
||||
TableLookupTest
|
||||
ThreadedLeakTest
|
||||
ThreadedTest
|
||||
TimeoutTest
|
||||
|
@ -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);
|
||||
}
|
||||
|
43
lib/Fuzzer/test/TableLookupTest.cpp
Normal file
43
lib/Fuzzer/test/TableLookupTest.cpp
Normal 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;
|
||||
}
|
6
lib/Fuzzer/test/extra-counters.test
Normal file
6
lib/Fuzzer/test/extra-counters.test
Normal 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]}}
|
Loading…
Reference in New Issue
Block a user