Skip to content

Commit

Permalink
[libFuzzer] Implement stat::stability_rate based on the percentage of…
Browse files Browse the repository at this point in the history
… unstable edges.

Summary:
Created a -print_unstable_stats flag.
When -print_unstable_stats=1, we run it 2 more times on interesting inputs poisoning unstable edges in an array.
On program termination, we run PrintUnstableStats() which will print a line with a stability percentage like AFL does.

Patch by Kyungtak Woo (@kevinwkt).

Reviewers: metzman, Dor1s, kcc, morehouse

Reviewed By: metzman, Dor1s, morehouse

Subscribers: delcypher, llvm-commits, #sanitizers, kcc, morehouse, Dor1s

Differential Revision: https://reviews.llvm.org/D49212

llvm-svn: 337187
  • Loading branch information
Dor1s committed Jul 16, 2018
1 parent 40c4aa7 commit 08dad54
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 0 deletions.
1 change: 1 addition & 0 deletions compiler-rt/lib/fuzzer/FuzzerDriver.cpp
Expand Up @@ -617,6 +617,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
Options.PrintFinalStats = Flags.print_final_stats;
Options.PrintCorpusStats = Flags.print_corpus_stats;
Options.PrintCoverage = Flags.print_coverage;
Options.PrintUnstableStats = Flags.print_unstable_stats;
Options.DumpCoverage = Flags.dump_coverage;
if (Flags.exit_on_src_pos)
Options.ExitOnSrcPos = Flags.exit_on_src_pos;
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/fuzzer/FuzzerFlags.def
Expand Up @@ -110,6 +110,8 @@ FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text"
FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated."
" If 1, dump coverage information as a"
" .sancov file at exit.")
FUZZER_FLAG_INT(print_unstable_stats, 0, "Experimental."
" If 1, print unstable statistics at exit.")
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.")
FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.")
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/fuzzer/FuzzerInternal.h
Expand Up @@ -67,6 +67,7 @@ class Fuzzer {
static void StaticGracefulExitCallback();

void ExecuteCallback(const uint8_t *Data, size_t Size);
void CheckForUnstableCounters(const uint8_t *Data, size_t Size);
bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false,
InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr);

Expand Down
31 changes: 31 additions & 0 deletions compiler-rt/lib/fuzzer/FuzzerLoop.cpp
Expand Up @@ -352,6 +352,8 @@ void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
void Fuzzer::PrintFinalStats() {
if (Options.PrintCoverage)
TPC.PrintCoverage();
if (Options.PrintUnstableStats)
TPC.PrintUnstableStats();
if (Options.DumpCoverage)
TPC.DumpCoverage();
if (Options.PrintCorpusStats)
Expand Down Expand Up @@ -444,6 +446,29 @@ void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) {
}
}

void Fuzzer::CheckForUnstableCounters(const uint8_t *Data, size_t Size) {
auto CBSetupAndRun = [&]() {
ScopedEnableMsanInterceptorChecks S;
UnitStartTime = system_clock::now();
TPC.ResetMaps();
RunningCB = true;
CB(Data, Size);
RunningCB = false;
UnitStopTime = system_clock::now();
};

// Copy original run counters into our unstable counters
TPC.InitializeUnstableCounters();

// First Rerun
CBSetupAndRun();
TPC.UpdateUnstableCounters();

// Second Rerun
CBSetupAndRun();
TPC.UpdateUnstableCounters();
}

bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
InputInfo *II, bool *FoundUniqFeatures) {
if (!Size)
Expand All @@ -466,6 +491,12 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
*FoundUniqFeatures = FoundUniqFeaturesOfII;
PrintPulseAndReportSlowInput(Data, Size);
size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore;

// If print_unstable_stats, execute the same input two more times to detect
// unstable edges.
if (NumNewFeatures && Options.PrintUnstableStats)
CheckForUnstableCounters(Data, Size);

if (NumNewFeatures) {
TPC.UpdateObservedPCs();
Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/fuzzer/FuzzerOptions.h
Expand Up @@ -54,6 +54,7 @@ struct FuzzingOptions {
bool PrintFinalStats = false;
bool PrintCorpusStats = false;
bool PrintCoverage = false;
bool PrintUnstableStats = false;
bool DumpCoverage = false;
bool DetectLeaks = true;
int PurgeAllocatorIntervalSec = 1;
Expand Down
40 changes: 40 additions & 0 deletions compiler-rt/lib/fuzzer/FuzzerTracePC.cpp
Expand Up @@ -59,6 +59,37 @@ size_t TracePC::GetTotalPCCoverage() {
return Res;
}

// Initializes unstable counters by copying Inline8bitCounters to unstable
// counters.
void TracePC::InitializeUnstableCounters() {
if (NumInline8bitCounters && NumInline8bitCounters == NumPCsInPCTables) {
size_t UnstableIdx = 0;
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
uint8_t *Beg = ModuleCounters[i].Start;
size_t Size = ModuleCounters[i].Stop - Beg;
assert(Size == (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
for (size_t j = 0; j < Size; j++, UnstableIdx++)
if (UnstableCounters[UnstableIdx] != kUnstableCounter)
UnstableCounters[UnstableIdx] = Beg[j];
}
}
}

// Compares the current counters with counters from previous runs
// and records differences as unstable edges.
void TracePC::UpdateUnstableCounters() {
if (NumInline8bitCounters && NumInline8bitCounters == NumPCsInPCTables) {
size_t UnstableIdx = 0;
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
uint8_t *Beg = ModuleCounters[i].Start;
size_t Size = ModuleCounters[i].Stop - Beg;
assert(Size == (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
for (size_t j = 0; j < Size; j++, UnstableIdx++)
if (Beg[j] != UnstableCounters[UnstableIdx])
UnstableCounters[UnstableIdx] = kUnstableCounter;
}
}
}

void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) {
if (Start == Stop) return;
Expand Down Expand Up @@ -310,6 +341,15 @@ void TracePC::DumpCoverage() {
}
}

void TracePC::PrintUnstableStats() {
size_t count = 0;
for (size_t i = 0; i < NumInline8bitCounters; i++)
if (UnstableCounters[i] == kUnstableCounter)
count++;
Printf("stat::stability_rate: %.2f\n",
100 - static_cast<float>(count * 100) / NumInline8bitCounters);
}

// Value profile.
// We keep track of various values that affect control flow.
// These values are inserted into a bit-set-based hash map.
Expand Down
11 changes: 11 additions & 0 deletions compiler-rt/lib/fuzzer/FuzzerTracePC.h
Expand Up @@ -103,6 +103,7 @@ class TracePC {

void PrintCoverage();
void DumpCoverage();
void PrintUnstableStats();

template<class CallBack>
void IterateCoveredFunctions(CallBack CB);
Expand Down Expand Up @@ -135,7 +136,17 @@ class TracePC {
void SetFocusFunction(const std::string &FuncName);
bool ObservedFocusFunction();

void InitializeUnstableCounters();
void UpdateUnstableCounters();

private:
// Value used to represent unstable edge.
static constexpr int16_t kUnstableCounter = -1;

// Uses 16-bit signed type to be able to accommodate any possible value from
// uint8_t counter and -1 constant as well.
int16_t UnstableCounters[kNumPCs];

bool UseCounters = false;
uint32_t UseValueProfileMask = false;
bool DoPrintNewPCs = false;
Expand Down
69 changes: 69 additions & 0 deletions compiler-rt/test/fuzzer/PrintUnstableStatsTest.cpp
@@ -0,0 +1,69 @@
#include <assert.h>
#include <cstdint>
#include <cstdio>
#include <cstdlib>

int x = 0;
bool skip0 = false;
bool skip1 = false;
bool skip2 = false;

__attribute__((noinline)) void det0() { x++; }
__attribute__((noinline)) void det1() { x++; }
__attribute__((noinline)) void det2() { x++; }
__attribute__((noinline)) void det3() { x++; }
__attribute__((noinline)) void det4() { x++; }

__attribute__((noinline)) void ini0() { x++; }
__attribute__((noinline)) void ini1() { x++; }
__attribute__((noinline)) void ini2() { x++; }

__attribute__((noinline)) void t0() { x++; }
__attribute__((noinline)) void t1() { x++; }
__attribute__((noinline)) void t2() { x++; }
__attribute__((noinline)) void t3() { x++; }
__attribute__((noinline)) void t4() { x++; }

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size == 1 && Data[0] == 'A' && !skip0) {
skip0 = true;
ini0();
}
if (Size == 1 && Data[0] == 'B' && !skip1) {
skip1 = true;
ini1();
}
if (Size == 1 && Data[0] == 'C' && !skip2) {
skip2 = true;
ini2();
}

det0();
det1();
int a = rand();
det2();

switch (a % 5) {
case 0:
t0();
break;
case 1:
t1();
break;
case 2:
t2();
break;
case 3:
t3();
break;
case 4:
t4();
break;
default:
assert(false);
}

det3();
det4();
return 0;
}
3 changes: 3 additions & 0 deletions compiler-rt/test/fuzzer/print_unstable_stats.test
@@ -0,0 +1,3 @@
RUN: %cpp_compiler %S/PrintUnstableStatsTest.cpp -o %t-PrintUnstableStatsTest
RUN: %run %t-PrintUnstableStatsTest -print_unstable_stats=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=LONG
LONG: stat::stability_rate: 27.59

0 comments on commit 08dad54

Please sign in to comment.