diff --git a/compiler-rt/lib/fuzzer/FuzzerCorpus.h b/compiler-rt/lib/fuzzer/FuzzerCorpus.h --- a/compiler-rt/lib/fuzzer/FuzzerCorpus.h +++ b/compiler-rt/lib/fuzzer/FuzzerCorpus.h @@ -33,6 +33,7 @@ // Stats. size_t NumExecutedMutations = 0; size_t NumSuccessfullMutations = 0; + bool SeedInput = false; bool MayDeleteFile = false; bool Reduced = false; bool HasFocusFunction = false; @@ -131,9 +132,12 @@ EntropicOptions Entropic; + bool KeepSeed = false; + public: - InputCorpus(const std::string &OutputCorpus, EntropicOptions Entropic) - : Entropic(Entropic), OutputCorpus(OutputCorpus) { + InputCorpus(const std::string &OutputCorpus, EntropicOptions Entropic, + bool KeepSeed) + : Entropic(Entropic), OutputCorpus(OutputCorpus), KeepSeed(KeepSeed) { memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature)); memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature)); } @@ -177,7 +181,7 @@ bool empty() const { return Inputs.empty(); } const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; } InputInfo *AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile, - bool HasFocusFunction, + bool HasFocusFunction, bool SeedInput, const Vector &FeatureSet, const DataFlowTrace &DFT, const InputInfo *BaseII) { assert(!U.empty()); @@ -187,6 +191,7 @@ InputInfo &II = *Inputs.back(); II.U = U; II.NumFeatures = NumFeatures; + II.SeedInput = SeedInput; II.MayDeleteFile = MayDeleteFile; II.UniqFeatureSet = FeatureSet; II.HasFocusFunction = HasFocusFunction; @@ -276,6 +281,11 @@ return Idx; } + InputInfo &ChooseUnitToCrossOverWith(Random &Rand) { + InputInfo &II = *Inputs[Rand(Inputs.size())]; + return II; + } + void PrintStats() { for (size_t i = 0; i < Inputs.size(); i++) { const auto &II = *Inputs[i]; diff --git a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp --- a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp @@ -649,6 +649,7 @@ Options.Verbosity = Flags.verbosity; Options.MaxLen = Flags.max_len; Options.LenControl = Flags.len_control; + Options.KeepSeed = Flags.keep_seed; Options.UnitTimeoutSec = Flags.timeout; Options.ErrorExitCode = Flags.error_exitcode; Options.TimeoutExitCode = Flags.timeout_exitcode; @@ -657,6 +658,7 @@ Options.IgnoreCrashes = Flags.ignore_crashes; Options.MaxTotalTimeSec = Flags.max_total_time; Options.DoCrossOver = Flags.cross_over; + Options.CrossOverUniformDist = Flags.cross_over_uniformdist; Options.MutateDepth = Flags.mutate_depth; Options.ReduceDepth = Flags.reduce_depth; Options.UseCounters = Flags.use_counters; @@ -753,7 +755,8 @@ Random Rand(Seed); auto *MD = new MutationDispatcher(Rand, Options); - auto *Corpus = new InputCorpus(Options.OutputCorpus, Entropic); + auto *Corpus = + new InputCorpus(Options.OutputCorpus, Entropic, Options.KeepSeed); auto *F = new Fuzzer(Callback, *Corpus, *MD, Options); for (auto &U: Dictionary) diff --git a/compiler-rt/lib/fuzzer/FuzzerFlags.def b/compiler-rt/lib/fuzzer/FuzzerFlags.def --- a/compiler-rt/lib/fuzzer/FuzzerFlags.def +++ b/compiler-rt/lib/fuzzer/FuzzerFlags.def @@ -23,7 +23,11 @@ FUZZER_FLAG_STRING(seed_inputs, "A comma-separated list of input files " "to use as an additional seed corpus. Alternatively, an \"@\" followed by " "the name of a file containing the comma-separated list.") +FUZZER_FLAG_INT(keep_seed, 0, "If 1, keep seed inputs for mutation even if " + "they do not produce new coverage.") FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.") +FUZZER_FLAG_INT(cross_over_uniformdist, 0, "Experimental. If 1, use a uniform " + "probability distribution when choosing inputs to cross over with.") FUZZER_FLAG_INT(mutate_depth, 5, "Apply this number of consecutive mutations to each input.") FUZZER_FLAG_INT(reduce_depth, 0, "Experimental/internal. " diff --git a/compiler-rt/lib/fuzzer/FuzzerFork.cpp b/compiler-rt/lib/fuzzer/FuzzerFork.cpp --- a/compiler-rt/lib/fuzzer/FuzzerFork.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerFork.cpp @@ -309,11 +309,15 @@ else Env.MainCorpusDir = CorpusDirs[0]; - auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); - CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features, - {}, &Env.Cov, - CFPath, false); - RemoveFile(CFPath); + if (Options.KeepSeed) { + for (auto &File : SeedFiles) + Env.Files.push_back(File.File); + } else { + auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); + CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features, + {}, &Env.Cov, CFPath, false); + RemoveFile(CFPath); + } Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs, Env.Files.size(), Env.TempDir.c_str()); diff --git a/compiler-rt/lib/fuzzer/FuzzerInternal.h b/compiler-rt/lib/fuzzer/FuzzerInternal.h --- a/compiler-rt/lib/fuzzer/FuzzerInternal.h +++ b/compiler-rt/lib/fuzzer/FuzzerInternal.h @@ -119,6 +119,8 @@ size_t LastCorpusUpdateRun = 0; + bool IsExecutingSeedCorpora = false; + bool HasMoreMallocsThanFrees = false; size_t NumberOfLeakDetectionAttempts = 0; diff --git a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp --- a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp @@ -478,7 +478,7 @@ UniqFeatureSetTmp.push_back(Feature); if (Options.Entropic) Corpus.UpdateFeatureFrequency(II, Feature); - if (Options.ReduceInputs && II) + if (Options.ReduceInputs && II && !(Options.KeepSeed && II->SeedInput)) if (std::binary_search(II->UniqFeatureSet.begin(), II->UniqFeatureSet.end(), Feature)) FoundUniqFeaturesOfII++; @@ -487,11 +487,12 @@ *FoundUniqFeatures = FoundUniqFeaturesOfII; PrintPulseAndReportSlowInput(Data, Size); size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore; - if (NumNewFeatures) { + if (NumNewFeatures || (Options.KeepSeed && IsExecutingSeedCorpora)) { TPC.UpdateObservedPCs(); - auto NewII = Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, - MayDeleteFile, TPC.ObservedFocusFunction(), - UniqFeatureSetTmp, DFT, II); + auto NewII = + Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile, + TPC.ObservedFocusFunction(), IsExecutingSeedCorpora, + UniqFeatureSetTmp, DFT, II); WriteFeatureSetToFile(Options.FeaturesDir, Sha1ToString(NewII->Sha1), NewII->UniqFeatureSet); return true; @@ -664,8 +665,14 @@ MD.StartMutationSequence(); auto &II = Corpus.ChooseUnitToMutate(MD.GetRand()); - if (Options.DoCrossOver) - MD.SetCrossOverWith(&Corpus.ChooseUnitToMutate(MD.GetRand()).U); + if (Options.DoCrossOver) { + if (Options.CrossOverUniformDist) { + MD.SetCrossOverWith(&Corpus.ChooseUnitToCrossOverWith(MD.GetRand()).U); + } + else { + MD.SetCrossOverWith(&Corpus.ChooseUnitToMutate(MD.GetRand()).U); + } + } const auto &U = II.U; memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1)); assert(CurrentUnitData); @@ -764,6 +771,8 @@ assert(CorporaFiles.front().Size <= CorporaFiles.back().Size); } + IsExecutingSeedCorpora = true; + // Load and execute inputs one by one. for (auto &SF : CorporaFiles) { auto U = FileToVector(SF.File, MaxInputLen, /*ExitOnError=*/false); @@ -773,6 +782,8 @@ TryDetectingAMemoryLeak(U.data(), U.size(), /*DuringInitialCorpusExecution*/ true); } + + IsExecutingSeedCorpora = false; } PrintStats("INITED"); @@ -785,6 +796,8 @@ Corpus.NumInputsThatTouchFocusFunction()); } + Printf("INFO: corpus size = %d\n", Corpus.size()); + if (Corpus.empty() && Options.MaxNumberOfRuns) { 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 --- a/compiler-rt/lib/fuzzer/FuzzerOptions.h +++ b/compiler-rt/lib/fuzzer/FuzzerOptions.h @@ -18,6 +18,7 @@ int Verbosity = 1; size_t MaxLen = 0; size_t LenControl = 1000; + bool KeepSeed = false; int UnitTimeoutSec = 300; int TimeoutExitCode = 70; int OOMExitCode = 71; @@ -30,6 +31,7 @@ int RssLimitMb = 0; int MallocLimitMb = 0; bool DoCrossOver = true; + bool CrossOverUniformDist = false; int MutateDepth = 5; bool ReduceDepth = false; bool UseCounters = false; diff --git a/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp b/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp --- a/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp +++ b/compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp @@ -593,7 +593,8 @@ DataFlowTrace DFT; Random Rand(0); struct EntropicOptions Entropic = {false, 0xFF, 100}; - std::unique_ptr C(new InputCorpus("", Entropic)); + bool KeepSeed = false; + std::unique_ptr C(new InputCorpus("", Entropic, KeepSeed)); size_t N = 10; size_t TriesPerUnit = 1<<16; for (size_t i = 0; i < N; i++) @@ -1057,7 +1058,8 @@ size_t Index; // Create input corpus with default entropic configuration struct EntropicOptions Entropic = {true, 0xFF, 100}; - std::unique_ptr C(new InputCorpus("", Entropic)); + bool KeepSeed = false; + std::unique_ptr C(new InputCorpus("", Entropic, KeepSeed)); std::unique_ptr II(new InputInfo()); C->AddRareFeature(FeatIdx1); @@ -1094,7 +1096,8 @@ TEST(Entropic, ComputeEnergy) { const double Precision = 0.01; struct EntropicOptions Entropic = {true, 0xFF, 100}; - std::unique_ptr C(new InputCorpus("", Entropic)); + bool KeepSeed = false; + std::unique_ptr C(new InputCorpus("", Entropic, KeepSeed)); std::unique_ptr II(new InputInfo()); Vector> FeatureFreqs = {{1, 3}, {2, 3}, {3, 3}}; II->FeatureFreqs = FeatureFreqs;