forked from OSchip/llvm-project
[libFuzzer] add an experimental flag -focus_function: libFuzzer will try to focus on inputs that trigger that function
llvm-svn: 332554
This commit is contained in:
parent
3bf63c9201
commit
e9c6f06cce
|
@ -35,6 +35,7 @@ struct InputInfo {
|
|||
size_t NumSuccessfullMutations = 0;
|
||||
bool MayDeleteFile = false;
|
||||
bool Reduced = false;
|
||||
bool HasFocusFunction = false;
|
||||
Vector<uint32_t> UniqFeatureSet;
|
||||
float FeatureFrequencyScore = 1.0;
|
||||
};
|
||||
|
@ -70,10 +71,17 @@ class InputCorpus {
|
|||
Res = std::max(Res, II->U.size());
|
||||
return Res;
|
||||
}
|
||||
|
||||
size_t NumInputsThatTouchFocusFunction() {
|
||||
return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) {
|
||||
return II->HasFocusFunction;
|
||||
});
|
||||
}
|
||||
|
||||
bool empty() const { return Inputs.empty(); }
|
||||
const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; }
|
||||
void AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile,
|
||||
const Vector<uint32_t> &FeatureSet) {
|
||||
bool HasFocusFunction, const Vector<uint32_t> &FeatureSet) {
|
||||
assert(!U.empty());
|
||||
if (FeatureDebug)
|
||||
Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures);
|
||||
|
@ -83,6 +91,7 @@ class InputCorpus {
|
|||
II.NumFeatures = NumFeatures;
|
||||
II.MayDeleteFile = MayDeleteFile;
|
||||
II.UniqFeatureSet = FeatureSet;
|
||||
II.HasFocusFunction = HasFocusFunction;
|
||||
std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end());
|
||||
ComputeSHA1(U.data(), U.size(), II.Sha1);
|
||||
Hashes.insert(Sha1ToString(II.Sha1));
|
||||
|
@ -265,6 +274,7 @@ private:
|
|||
for (size_t i = 0; i < N; i++)
|
||||
Weights[i] = Inputs[i]->NumFeatures
|
||||
? (i + 1) * Inputs[i]->FeatureFrequencyScore
|
||||
* (Inputs[i]->HasFocusFunction ? 1000 : 1)
|
||||
: 0.;
|
||||
if (FeatureDebug) {
|
||||
for (size_t i = 0; i < N; i++)
|
||||
|
|
|
@ -620,6 +620,8 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
|
|||
Options.ExitOnSrcPos = Flags.exit_on_src_pos;
|
||||
if (Flags.exit_on_item)
|
||||
Options.ExitOnItem = Flags.exit_on_item;
|
||||
if (Flags.focus_function)
|
||||
Options.FocusFunction = Flags.focus_function;
|
||||
|
||||
unsigned Seed = Flags.seed;
|
||||
// Initialize Seed.
|
||||
|
|
|
@ -143,6 +143,8 @@ FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum"
|
|||
FUZZER_FLAG_INT(ignore_remaining_args, 0, "If 1, ignore all arguments passed "
|
||||
"after this one. Useful for fuzzers that need to do their own "
|
||||
"argument parsing.")
|
||||
FUZZER_FLAG_STRING(focus_function, "Experimental. "
|
||||
"Fuzzing will focus on inputs that trigger calls to this function")
|
||||
|
||||
FUZZER_DEPRECATED_FLAG(run_equivalence_server)
|
||||
FUZZER_DEPRECATED_FLAG(use_equivalence_server)
|
||||
|
|
|
@ -159,6 +159,7 @@ Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
|
|||
AllocateCurrentUnitData();
|
||||
CurrentUnitSize = 0;
|
||||
memset(BaseSha1, 0, sizeof(BaseSha1));
|
||||
TPC.SetFocusFunction(Options.FocusFunction);
|
||||
}
|
||||
|
||||
Fuzzer::~Fuzzer() {}
|
||||
|
@ -333,6 +334,8 @@ void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
|
|||
else
|
||||
Printf("/%zdMb", N >> 20);
|
||||
}
|
||||
if (size_t FF = Corpus.NumInputsThatTouchFocusFunction())
|
||||
Printf(" focus: %zd", FF);
|
||||
}
|
||||
if (TmpMaxMutationLen)
|
||||
Printf(" lim: %zd", TmpMaxMutationLen);
|
||||
|
@ -464,6 +467,7 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
|
|||
if (NumNewFeatures) {
|
||||
TPC.UpdateObservedPCs();
|
||||
Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,
|
||||
TPC.ObservedFocusFunction(),
|
||||
UniqFeatureSetTmp);
|
||||
return true;
|
||||
}
|
||||
|
@ -733,6 +737,10 @@ void Fuzzer::ReadAndExecuteSeedCorpora(const Vector<std::string> &CorpusDirs) {
|
|||
}
|
||||
|
||||
PrintStats("INITED");
|
||||
if (!Options.FocusFunction.empty())
|
||||
Printf("INFO: %zd/%zd inputs touch the focus function\n",
|
||||
Corpus.NumInputsThatTouchFocusFunction(), Corpus.size());
|
||||
|
||||
if (Corpus.empty()) {
|
||||
Printf("ERROR: no interesting inputs were found. "
|
||||
"Is the code instrumented for coverage? Exiting.\n");
|
||||
|
|
|
@ -45,6 +45,7 @@ struct FuzzingOptions {
|
|||
std::string ExactArtifactPath;
|
||||
std::string ExitOnSrcPos;
|
||||
std::string ExitOnItem;
|
||||
std::string FocusFunction;
|
||||
bool SaveArtifacts = true;
|
||||
bool PrintNEW = true; // Print a status line when new units are found;
|
||||
bool PrintNewCovPcs = false;
|
||||
|
|
|
@ -229,6 +229,39 @@ void TracePC::IterateCoveredFunctions(CallBack CB) {
|
|||
}
|
||||
}
|
||||
|
||||
void TracePC::SetFocusFunction(const std::string &FuncName) {
|
||||
// This function should be called once.
|
||||
assert(FocusFunction.first > NumModulesWithInline8bitCounters);
|
||||
if (FuncName.empty())
|
||||
return;
|
||||
for (size_t M = 0; M < NumModulesWithInline8bitCounters; M++) {
|
||||
auto &PCTE = ModulePCTable[M];
|
||||
size_t N = PCTE.Stop - PCTE.Start;
|
||||
for (size_t I = 0; I < N; I++) {
|
||||
if (!(PCTE.Start[I].PCFlags & 1)) continue; // not a function entry.
|
||||
auto Name = DescribePC("%F", GetNextInstructionPc(PCTE.Start[I].PC));
|
||||
if (Name[0] == 'i' && Name[1] == 'n' && Name[2] == ' ')
|
||||
Name = Name.substr(3, std::string::npos);
|
||||
if (FuncName != Name) continue;
|
||||
Printf("INFO: Focus function is set to '%s'\n", Name.c_str());
|
||||
FocusFunction = {M, I};
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TracePC::ObservedFocusFunction() {
|
||||
size_t I = FocusFunction.first;
|
||||
size_t J = FocusFunction.second;
|
||||
if (I >= NumModulesWithInline8bitCounters)
|
||||
return false;
|
||||
auto &MC = ModuleCounters[I];
|
||||
size_t Size = MC.Stop - MC.Start;
|
||||
if (J >= Size)
|
||||
return false;
|
||||
return MC.Start[J] != 0;
|
||||
}
|
||||
|
||||
void TracePC::PrintCoverage() {
|
||||
if (!EF->__sanitizer_symbolize_pc ||
|
||||
!EF->__sanitizer_get_module_and_offset_for_pc) {
|
||||
|
|
|
@ -131,6 +131,9 @@ class TracePC {
|
|||
CB(PC);
|
||||
}
|
||||
|
||||
void SetFocusFunction(const std::string &FuncName);
|
||||
bool ObservedFocusFunction();
|
||||
|
||||
private:
|
||||
bool UseCounters = false;
|
||||
bool UseValueProfile = false;
|
||||
|
@ -163,6 +166,9 @@ private:
|
|||
Set<uintptr_t> ObservedPCs;
|
||||
Set<uintptr_t> ObservedFuncs;
|
||||
|
||||
std::pair<size_t, size_t> FocusFunction = {-1, -1}; // Module and PC IDs.
|
||||
|
||||
|
||||
ValueBitMap ValueProfileMap;
|
||||
uintptr_t InitialStack;
|
||||
};
|
||||
|
|
|
@ -579,7 +579,7 @@ TEST(Corpus, Distribution) {
|
|||
size_t N = 10;
|
||||
size_t TriesPerUnit = 1<<16;
|
||||
for (size_t i = 0; i < N; i++)
|
||||
C->AddToCorpus(Unit{ static_cast<uint8_t>(i) }, 1, false, {});
|
||||
C->AddToCorpus(Unit{ static_cast<uint8_t>(i) }, 1, false, false, {});
|
||||
|
||||
Vector<size_t> Hist(N);
|
||||
for (size_t i = 0; i < N * TriesPerUnit; i++) {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
const size_t N = 2048;
|
||||
typedef const uint8_t *IN;
|
||||
|
||||
extern "C" {
|
||||
__attribute__((noinline)) void bad() {
|
||||
fprintf(stderr, "BINGO\n");
|
||||
abort();
|
||||
|
@ -27,6 +28,8 @@ __attribute__((noinline)) void fC(IN in) { if (in[2] == 'C') f0(in); }
|
|||
__attribute__((noinline)) void fB(IN in) { if (in[1] == 'B') fC(in); }
|
||||
__attribute__((noinline)) void fA(IN in) { if (in[0] == 'A') fB(in); }
|
||||
|
||||
} // extern "C"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
if (Size < N) return 0;
|
||||
fA((IN)Data);
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Tests -focus_function
|
||||
#
|
||||
# TODO: don't require linux.
|
||||
# REQUIRES: linux
|
||||
#
|
||||
RUN: %cpp_compiler %S/OnlySomeBytesTest.cpp -o %t-exe
|
||||
|
||||
RUN: %t-exe -runs=100 2>&1 | FileCheck %s --check-prefix=FOCUS_NONE
|
||||
FOCUS_NONE-NOT: INFO: Focus function is set to
|
||||
FOCUS_NONE-NOT: INFO: {{.*}} inputs touch the focus function
|
||||
|
||||
RUN: %t-exe -runs=100 -focus_function=WRONG 2>&1 | FileCheck %s --check-prefix=FOCUS_WRONG
|
||||
FOCUS_WRONG-NOT: INFO: Focus function is set to
|
||||
FOCUS_WRONG: INFO: 0/1 inputs touch the focus function
|
||||
|
||||
RUN: %t-exe -runs=100 -focus_function=f0 2>&1 | FileCheck %s --check-prefix=FOCUS_F0
|
||||
FOCUS_F0: INFO: Focus function is set to 'f0'
|
||||
FOCUS_F0: INFO: 0/1 inputs touch the focus function
|
||||
|
||||
RUN: rm -rf %t-corpus
|
||||
RUN: mkdir %t-corpus
|
||||
# ABC triggers the focus function, others don't.
|
||||
RUN: echo ABC$(for((i=0;i<2048;i++)); do echo -n x; done) > %t-corpus/ABC
|
||||
RUN: echo AXY$(for((i=0;i<2048;i++)); do echo -n x; done) > %t-corpus/AXY
|
||||
RUN: echo ABX$(for((i=0;i<2048;i++)); do echo -n x; done) > %t-corpus/ABX
|
||||
|
||||
RUN: %t-exe -runs=10000 -focus_function=f0 %t-corpus 2>&1 | FileCheck %s --check-prefix=CORPUS_1_3
|
||||
CORPUS_1_3: INFO: 1/3 inputs touch the focus function
|
||||
CORPUS_1_3: DONE {{.*}} focus:
|
Loading…
Reference in New Issue