[libfuzzer] Refactoring coverage state-management code.

It is now less state-dependent and will allow easier comparing of
coverages of different units.

Differential Revision: http://reviews.llvm.org/D20085

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@269140 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Mike Aizatsky 2016-05-10 23:43:15 +00:00
parent 3ddae02e7c
commit 0d68393b0f
4 changed files with 206 additions and 116 deletions

View File

@ -25,6 +25,7 @@
#include <vector> #include <vector>
#include "FuzzerInterface.h" #include "FuzzerInterface.h"
#include "FuzzerTracePC.h"
namespace fuzzer { namespace fuzzer {
using namespace std::chrono; using namespace std::chrono;
@ -112,13 +113,6 @@ int GetPid();
int SignalToMainThread(); int SignalToMainThread();
void SleepSeconds(int Seconds); void SleepSeconds(int Seconds);
// Clears the current PC Map.
void PcMapResetCurrent();
// Merges the current PC Map into the combined one, and clears the former.
void PcMapMergeCurrentToCombined();
// Returns the size of the combined PC Map.
size_t PcMapCombinedSize();
class Random { class Random {
public: public:
Random(unsigned int seed) : R(seed) {} Random(unsigned int seed) : R(seed) {}
@ -309,6 +303,35 @@ public:
bool PrintFinalStats = false; bool PrintFinalStats = false;
bool DetectLeaks = true; bool DetectLeaks = true;
}; };
// Aggregates all available coverage measurements.
struct Coverage {
Coverage() { Reset(); }
void Reset() {
BlockCoverage = 0;
CallerCalleeCoverage = 0;
PcMapBits = 0;
CounterBitmapBits = 0;
PcBufferLen = 0;
CounterBitmap.clear();
PCMap.Reset();
}
std::string DebugString() const;
size_t BlockCoverage;
size_t CallerCalleeCoverage;
size_t PcBufferLen;
// Precalculated number of bits in CounterBitmap.
size_t CounterBitmapBits;
std::vector<uint8_t> CounterBitmap;
// Precalculated number of bits in PCMap.
size_t PcMapBits;
PcCoverageMap PCMap;
};
Fuzzer(UserCallback CB, MutationDispatcher &MD, FuzzingOptions Options); Fuzzer(UserCallback CB, MutationDispatcher &MD, FuzzingOptions Options);
void AddToCorpus(const Unit &U) { void AddToCorpus(const Unit &U) {
Corpus.push_back(U); Corpus.push_back(U);
@ -378,11 +401,8 @@ private:
// Must be called whenever the corpus or unit weights are changed. // Must be called whenever the corpus or unit weights are changed.
void UpdateCorpusDistribution(); void UpdateCorpusDistribution();
size_t RecordBlockCoverage();
size_t RecordCallerCalleeCoverage();
void PrepareCoverageBeforeRun();
bool CheckCoverageAfterRun();
void ResetCoverage(); void ResetCoverage();
bool UpdateMaxCoverage();
// Trace-based fuzzing: we run a unit with some kind of tracing // Trace-based fuzzing: we run a unit with some kind of tracing
// enabled and record potentially useful mutations. Then // enabled and record potentially useful mutations. Then
@ -410,16 +430,6 @@ private:
std::vector<Unit> Corpus; std::vector<Unit> Corpus;
std::unordered_set<std::string> UnitHashesAddedToCorpus; std::unordered_set<std::string> UnitHashesAddedToCorpus;
// For UseCounters
std::vector<uint8_t> CounterBitmap;
size_t TotalBits() { // Slow. Call it only for printing stats.
size_t Res = 0;
for (auto x : CounterBitmap)
Res += __builtin_popcount(x);
return Res;
}
std::vector<uint8_t> MutateInPlaceHere; std::vector<uint8_t> MutateInPlaceHere;
std::piecewise_constant_distribution<double> CorpusDistribution; std::piecewise_constant_distribution<double> CorpusDistribution;
@ -430,10 +440,9 @@ private:
system_clock::time_point UnitStartTime; system_clock::time_point UnitStartTime;
long TimeOfLongestUnitInSeconds = 0; long TimeOfLongestUnitInSeconds = 0;
long EpochOfLastReadOfOutputCorpus = 0; long EpochOfLastReadOfOutputCorpus = 0;
size_t LastRecordedBlockCoverage = 0;
size_t LastRecordedPcMapSize = 0; // Maximum recorded coverage.
size_t LastRecordedCallerCalleeCoverage = 0; Coverage MaxCoverage;
size_t LastCoveragePcBufferLen = 0;
}; };
}; // namespace fuzzer }; // namespace fuzzer

View File

@ -81,12 +81,83 @@ size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
return F->GetMD().Mutate(Data, Size, MaxSize); return F->GetMD().Mutate(Data, Size, MaxSize);
} }
struct CoverageController {
static void Reset() {
CHECK_WEAK_API_FUNCTION(__sanitizer_reset_coverage);
__sanitizer_reset_coverage();
PcMapResetCurrent();
}
static void ResetCounters(const Fuzzer::FuzzingOptions &Options) {
if (Options.UseCounters) {
__sanitizer_update_counter_bitset_and_clear_counters(0);
}
}
static void Prepare(const Fuzzer::FuzzingOptions &Options,
Fuzzer::Coverage *C) {
if (Options.UseCounters) {
size_t NumCounters = __sanitizer_get_number_of_counters();
C->CounterBitmap.resize(NumCounters);
}
}
// Records data to a maximum coverage tracker. Returns true if additional
// coverage was discovered.
static bool RecordMax(const Fuzzer::FuzzingOptions &Options,
Fuzzer::Coverage *C) {
bool Res = false;
uint64_t NewBlockCoverage = __sanitizer_get_total_unique_coverage();
if (NewBlockCoverage > C->BlockCoverage) {
Res = true;
C->BlockCoverage = NewBlockCoverage;
}
if (Options.UseIndirCalls &&
__sanitizer_get_total_unique_caller_callee_pairs) {
uint64_t NewCallerCalleeCoverage =
__sanitizer_get_total_unique_caller_callee_pairs();
if (NewCallerCalleeCoverage > C->CallerCalleeCoverage) {
Res = true;
C->CallerCalleeCoverage = NewCallerCalleeCoverage;
}
}
if (Options.UseCounters) {
uint64_t CounterDelta =
__sanitizer_update_counter_bitset_and_clear_counters(
C->CounterBitmap.data());
if (CounterDelta > 0) {
Res = true;
C->CounterBitmapBits += CounterDelta;
}
}
uint64_t NewPcMapBits = PcMapMergeInto(&C->PCMap);
if (NewPcMapBits > C->PcMapBits) {
Res = true;
C->PcMapBits = NewPcMapBits;
}
uintptr_t *CoverageBuf;
uint64_t NewPcBufferLen = __sanitizer_get_coverage_pc_buffer(&CoverageBuf);
if (NewPcBufferLen > C->PcBufferLen) {
Res = true;
C->PcBufferLen = NewPcBufferLen;
}
return Res;
}
};
Fuzzer::Fuzzer(UserCallback CB, MutationDispatcher &MD, FuzzingOptions Options) Fuzzer::Fuzzer(UserCallback CB, MutationDispatcher &MD, FuzzingOptions Options)
: CB(CB), MD(MD), Options(Options) { : CB(CB), MD(MD), Options(Options) {
SetDeathCallback(); SetDeathCallback();
InitializeTraceState(); InitializeTraceState();
assert(!F); assert(!F);
F = this; F = this;
ResetCoverage();
} }
void Fuzzer::SetDeathCallback() { void Fuzzer::SetDeathCallback() {
@ -208,22 +279,21 @@ void Fuzzer::PrintStats(const char *Where, const char *End) {
Printf("runs,block_cov,bits,cc_cov,corpus,execs_per_sec,tbms,reason\n"); Printf("runs,block_cov,bits,cc_cov,corpus,execs_per_sec,tbms,reason\n");
} }
Printf("%zd,%zd,%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns, Printf("%zd,%zd,%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns,
LastRecordedBlockCoverage, TotalBits(), MaxCoverage.BlockCoverage, MaxCoverage.CounterBitmapBits,
LastRecordedCallerCalleeCoverage, Corpus.size(), ExecPerSec, MaxCoverage.CallerCalleeCoverage, Corpus.size(), ExecPerSec, Where);
Where);
} }
if (!Options.Verbosity) if (!Options.Verbosity)
return; return;
Printf("#%zd\t%s", TotalNumberOfRuns, Where); Printf("#%zd\t%s", TotalNumberOfRuns, Where);
if (LastRecordedBlockCoverage) if (MaxCoverage.BlockCoverage)
Printf(" cov: %zd", LastRecordedBlockCoverage); Printf(" cov: %zd", MaxCoverage.BlockCoverage);
if (LastRecordedPcMapSize) if (MaxCoverage.PcMapBits)
Printf(" path: %zd", LastRecordedPcMapSize); Printf(" path: %zd", MaxCoverage.PcMapBits);
if (auto TB = TotalBits()) if (auto TB = MaxCoverage.CounterBitmapBits)
Printf(" bits: %zd", TB); Printf(" bits: %zd", TB);
if (LastRecordedCallerCalleeCoverage) if (MaxCoverage.CallerCalleeCoverage)
Printf(" indir: %zd", LastRecordedCallerCalleeCoverage); Printf(" indir: %zd", MaxCoverage.CallerCalleeCoverage);
Printf(" units: %zd exec/s: %zd", Corpus.size(), ExecPerSec); Printf(" units: %zd exec/s: %zd", Corpus.size(), ExecPerSec);
Printf("%s", End); Printf("%s", End);
} }
@ -298,7 +368,7 @@ void Fuzzer::ShuffleAndMinimize() {
if (RunOne(U)) { if (RunOne(U)) {
NewCorpus.push_back(U); NewCorpus.push_back(U);
if (Options.Verbosity >= 2) if (Options.Verbosity >= 2)
Printf("NEW0: %zd L %zd\n", LastRecordedBlockCoverage, U.size()); Printf("NEW0: %zd L %zd\n", MaxCoverage.BlockCoverage, U.size());
} }
} }
Corpus = NewCorpus; Corpus = NewCorpus;
@ -309,12 +379,29 @@ void Fuzzer::ShuffleAndMinimize() {
CheckForMemoryLeaks(); CheckForMemoryLeaks();
} }
bool Fuzzer::UpdateMaxCoverage() {
uintptr_t PrevBufferLen = MaxCoverage.PcBufferLen;
bool Res = CoverageController::RecordMax(Options, &MaxCoverage);
if (Options.PrintNewCovPcs && PrevBufferLen != MaxCoverage.PcBufferLen) {
uintptr_t *CoverageBuf;
__sanitizer_get_coverage_pc_buffer(&CoverageBuf);
assert(CoverageBuf);
for (size_t I = PrevBufferLen; I < MaxCoverage.PcBufferLen; ++I) {
Printf("%p\n", CoverageBuf[I]);
}
}
return Res;
}
bool Fuzzer::RunOne(const uint8_t *Data, size_t Size) { bool Fuzzer::RunOne(const uint8_t *Data, size_t Size) {
TotalNumberOfRuns++; TotalNumberOfRuns++;
PrepareCoverageBeforeRun(); // TODO(aizatsky): this Reset call seems to be not needed.
CoverageController::ResetCounters(Options);
ExecuteCallback(Data, Size); ExecuteCallback(Data, Size);
bool Res = CheckCoverageAfterRun(); bool Res = UpdateMaxCoverage();
auto UnitStopTime = system_clock::now(); auto UnitStopTime = system_clock::now();
auto TimeOfUnit = auto TimeOfUnit =
@ -378,61 +465,14 @@ void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
assert(Res == 0); assert(Res == 0);
} }
size_t Fuzzer::RecordBlockCoverage() { std::string Fuzzer::Coverage::DebugString() const {
CHECK_WEAK_API_FUNCTION(__sanitizer_get_total_unique_coverage); std::string Result =
uintptr_t PrevCoverage = LastRecordedBlockCoverage; std::string("Coverage{") + "BlockCoverage=" +
LastRecordedBlockCoverage = __sanitizer_get_total_unique_coverage(); std::to_string(BlockCoverage) + " CallerCalleeCoverage=" +
std::to_string(CallerCalleeCoverage) + " CounterBitmapBits=" +
if (PrevCoverage == LastRecordedBlockCoverage || !Options.PrintNewCovPcs) std::to_string(CounterBitmapBits) + " PcMapBits=" +
return LastRecordedBlockCoverage; std::to_string(PcMapBits) + "}";
return Result;
uintptr_t PrevBufferLen = LastCoveragePcBufferLen;
uintptr_t *CoverageBuf;
LastCoveragePcBufferLen = __sanitizer_get_coverage_pc_buffer(&CoverageBuf);
assert(CoverageBuf);
for (size_t i = PrevBufferLen; i < LastCoveragePcBufferLen; ++i) {
Printf("%p\n", CoverageBuf[i]);
}
return LastRecordedBlockCoverage;
}
size_t Fuzzer::RecordCallerCalleeCoverage() {
if (!Options.UseIndirCalls)
return 0;
if (!__sanitizer_get_total_unique_caller_callee_pairs)
return 0;
return LastRecordedCallerCalleeCoverage =
__sanitizer_get_total_unique_caller_callee_pairs();
}
void Fuzzer::PrepareCoverageBeforeRun() {
if (Options.UseCounters) {
size_t NumCounters = __sanitizer_get_number_of_counters();
CounterBitmap.resize(NumCounters);
__sanitizer_update_counter_bitset_and_clear_counters(0);
}
RecordBlockCoverage();
RecordCallerCalleeCoverage();
}
bool Fuzzer::CheckCoverageAfterRun() {
size_t OldCoverage = LastRecordedBlockCoverage;
size_t NewCoverage = RecordBlockCoverage();
size_t OldCallerCalleeCoverage = LastRecordedCallerCalleeCoverage;
size_t NewCallerCalleeCoverage = RecordCallerCalleeCoverage();
size_t NumNewBits = 0;
size_t OldPcMapSize = LastRecordedPcMapSize;
PcMapMergeCurrentToCombined();
size_t NewPcMapSize = PcMapCombinedSize();
LastRecordedPcMapSize = NewPcMapSize;
if (NewPcMapSize > OldPcMapSize)
return true;
if (Options.UseCounters)
NumNewBits = __sanitizer_update_counter_bitset_and_clear_counters(
CounterBitmap.data());
return NewCoverage > OldCoverage ||
NewCallerCalleeCoverage > OldCallerCalleeCoverage || NumNewBits;
} }
void Fuzzer::WriteToOutputCorpus(const Unit &U) { void Fuzzer::WriteToOutputCorpus(const Unit &U) {
@ -639,9 +679,9 @@ size_t Fuzzer::ChooseUnitIdxToMutate() {
} }
void Fuzzer::ResetCoverage() { void Fuzzer::ResetCoverage() {
CHECK_WEAK_API_FUNCTION(__sanitizer_reset_coverage); CoverageController::Reset();
__sanitizer_reset_coverage(); MaxCoverage.Reset();
CounterBitmap.clear(); CoverageController::Prepare(Options, &MaxCoverage);
} }
// Experimental search heuristic: drilling. // Experimental search heuristic: drilling.

View File

@ -15,39 +15,43 @@
#include "FuzzerInternal.h" #include "FuzzerInternal.h"
namespace fuzzer { namespace fuzzer {
static const size_t kMapSizeInBits = 65371; // Prime.
static const size_t kMapSizeInBitsAligned = 65536; // 2^16 void PcCoverageMap::Reset() { memset(Map, 0, sizeof(Map)); }
static const size_t kBitsInWord =(sizeof(uintptr_t) * 8);
static const size_t kMapSizeInWords = kMapSizeInBitsAligned / kBitsInWord; void PcCoverageMap::Update(uintptr_t Addr) {
static uintptr_t CurrentMap[kMapSizeInWords] __attribute__((aligned(512))); uintptr_t Idx = Addr % kMapSizeInBits;
static uintptr_t CombinedMap[kMapSizeInWords] __attribute__((aligned(512))); uintptr_t WordIdx = Idx / kBitsInWord;
static size_t CombinedMapSize; uintptr_t BitIdx = Idx % kBitsInWord;
Map[WordIdx] |= 1UL << BitIdx;
}
size_t PcCoverageMap::MergeFrom(const PcCoverageMap &Other) {
uintptr_t Res = 0;
for (size_t i = 0; i < kMapSizeInWords; i++)
Res += __builtin_popcountl(Map[i] |= Other.Map[i]);
return Res;
}
static PcCoverageMap CurrentMap;
static thread_local uintptr_t Prev; static thread_local uintptr_t Prev;
void PcMapResetCurrent() { void PcMapResetCurrent() {
if (Prev) { if (Prev) {
Prev = 0; Prev = 0;
memset(CurrentMap, 0, sizeof(CurrentMap)); CurrentMap.Reset();
} }
} }
void PcMapMergeCurrentToCombined() { size_t PcMapMergeInto(PcCoverageMap *Map) {
if (!Prev) return; if (!Prev)
uintptr_t Res = 0; return 0;
for (size_t i = 0; i < kMapSizeInWords; i++) return Map->MergeFrom(CurrentMap);
Res += __builtin_popcountl(CombinedMap[i] |= CurrentMap[i]);
CombinedMapSize = Res;
} }
size_t PcMapCombinedSize() { return CombinedMapSize; }
static void HandlePC(uint32_t PC) { static void HandlePC(uint32_t PC) {
// We take 12 bits of PC and mix it with the previous PCs. // We take 12 bits of PC and mix it with the previous PCs.
uintptr_t Next = (Prev << 5) ^ (PC & 4095); uintptr_t Next = (Prev << 5) ^ (PC & 4095);
uintptr_t Idx = Next % kMapSizeInBits; CurrentMap.Update(Next);
uintptr_t WordIdx = Idx / kBitsInWord;
uintptr_t BitIdx = Idx % kBitsInWord;
CurrentMap[WordIdx] |= 1UL << BitIdx;
Prev = Next; Prev = Next;
} }

View File

@ -0,0 +1,37 @@
//===- FuzzerTracePC.h - INTERNAL - Path tracer. --------*- C++ -* ===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Trace PCs.
// This module implements __sanitizer_cov_trace_pc, a callback required
// for -fsanitize-coverage=trace-pc instrumentation.
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZER_TRACE_PC_H
#define LLVM_FUZZER_TRACE_PC_H
namespace fuzzer {
struct PcCoverageMap {
static const size_t kMapSizeInBits = 65371; // Prime.
static const size_t kMapSizeInBitsAligned = 65536; // 2^16
static const size_t kBitsInWord = (sizeof(uintptr_t) * 8);
static const size_t kMapSizeInWords = kMapSizeInBitsAligned / kBitsInWord;
void Reset();
inline void Update(uintptr_t Addr);
size_t MergeFrom(const PcCoverageMap &Other);
uintptr_t Map[kMapSizeInWords] __attribute__((aligned(512)));
};
// Clears the current PC Map.
void PcMapResetCurrent();
// Merges the current PC Map into the combined one, and clears the former.
size_t PcMapMergeInto(PcCoverageMap *Map);
}
#endif