diff --git a/compiler-rt/lib/fuzzer/FuzzerCorpus.h b/compiler-rt/lib/fuzzer/FuzzerCorpus.h index 2da929835f45..05d527ba0ea9 100644 --- a/compiler-rt/lib/fuzzer/FuzzerCorpus.h +++ b/compiler-rt/lib/fuzzer/FuzzerCorpus.h @@ -35,6 +35,7 @@ struct InputInfo { size_t NumSuccessfullMutations = 0; bool MayDeleteFile = false; bool Reduced = false; + bool HasFocusFunction = false; Vector 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 &FeatureSet) { + bool HasFocusFunction, const Vector &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++) diff --git a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp index 4f14373b381e..26e5548a9a8e 100644 --- a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp @@ -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. diff --git a/compiler-rt/lib/fuzzer/FuzzerFlags.def b/compiler-rt/lib/fuzzer/FuzzerFlags.def index 64bd36281c85..1ff3fd95e4d5 100644 --- a/compiler-rt/lib/fuzzer/FuzzerFlags.def +++ b/compiler-rt/lib/fuzzer/FuzzerFlags.def @@ -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) diff --git a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp index dfa6cf38b1fa..4bf5c7802460 100644 --- a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp @@ -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 &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"); diff --git a/compiler-rt/lib/fuzzer/FuzzerOptions.h b/compiler-rt/lib/fuzzer/FuzzerOptions.h index 4fd4e8d35adc..0c51d9e39464 100644 --- a/compiler-rt/lib/fuzzer/FuzzerOptions.h +++ b/compiler-rt/lib/fuzzer/FuzzerOptions.h @@ -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; diff --git a/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp b/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp index 20230d496e9c..ed62cdcf49be 100644 --- a/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp @@ -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) { diff --git a/compiler-rt/lib/fuzzer/FuzzerTracePC.h b/compiler-rt/lib/fuzzer/FuzzerTracePC.h index d68da76b2e2d..e1db5127cf4d 100644 --- a/compiler-rt/lib/fuzzer/FuzzerTracePC.h +++ b/compiler-rt/lib/fuzzer/FuzzerTracePC.h @@ -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 ObservedPCs; Set ObservedFuncs; + std::pair FocusFunction = {-1, -1}; // Module and PC IDs. + + ValueBitMap ValueProfileMap; uintptr_t InitialStack; }; diff --git a/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp b/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp index c795eddc656e..a38a45344e9c 100644 --- a/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp +++ b/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp @@ -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(i) }, 1, false, {}); + C->AddToCorpus(Unit{ static_cast(i) }, 1, false, false, {}); Vector Hist(N); for (size_t i = 0; i < N * TriesPerUnit; i++) { diff --git a/compiler-rt/test/fuzzer/OnlySomeBytesTest.cpp b/compiler-rt/test/fuzzer/OnlySomeBytesTest.cpp index 05793f0abec7..3873b710ba7f 100644 --- a/compiler-rt/test/fuzzer/OnlySomeBytesTest.cpp +++ b/compiler-rt/test/fuzzer/OnlySomeBytesTest.cpp @@ -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); diff --git a/compiler-rt/test/fuzzer/target-function.test b/compiler-rt/test/fuzzer/target-function.test new file mode 100644 index 000000000000..f39e1ac1b915 --- /dev/null +++ b/compiler-rt/test/fuzzer/target-function.test @@ -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: