xref: /freebsd/contrib/llvm-project/compiler-rt/lib/fuzzer/FuzzerFork.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===- FuzzerFork.cpp - run fuzzing in separate subprocesses --------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric // Spawn and orchestrate separate fuzzing processes.
90b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
100b57cec5SDimitry Andric 
110b57cec5SDimitry Andric #include "FuzzerCommand.h"
120b57cec5SDimitry Andric #include "FuzzerFork.h"
130b57cec5SDimitry Andric #include "FuzzerIO.h"
140b57cec5SDimitry Andric #include "FuzzerInternal.h"
150b57cec5SDimitry Andric #include "FuzzerMerge.h"
160b57cec5SDimitry Andric #include "FuzzerSHA1.h"
170b57cec5SDimitry Andric #include "FuzzerTracePC.h"
180b57cec5SDimitry Andric #include "FuzzerUtil.h"
190b57cec5SDimitry Andric 
200b57cec5SDimitry Andric #include <atomic>
210b57cec5SDimitry Andric #include <chrono>
220b57cec5SDimitry Andric #include <condition_variable>
230b57cec5SDimitry Andric #include <fstream>
240b57cec5SDimitry Andric #include <memory>
250b57cec5SDimitry Andric #include <mutex>
260b57cec5SDimitry Andric #include <queue>
270b57cec5SDimitry Andric #include <sstream>
280b57cec5SDimitry Andric #include <thread>
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric namespace fuzzer {
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric struct Stats {
330b57cec5SDimitry Andric   size_t number_of_executed_units = 0;
340b57cec5SDimitry Andric   size_t peak_rss_mb = 0;
350b57cec5SDimitry Andric   size_t average_exec_per_sec = 0;
360b57cec5SDimitry Andric };
370b57cec5SDimitry Andric 
ParseFinalStatsFromLog(const std::string & LogPath)380b57cec5SDimitry Andric static Stats ParseFinalStatsFromLog(const std::string &LogPath) {
390b57cec5SDimitry Andric   std::ifstream In(LogPath);
400b57cec5SDimitry Andric   std::string Line;
410b57cec5SDimitry Andric   Stats Res;
420b57cec5SDimitry Andric   struct {
430b57cec5SDimitry Andric     const char *Name;
440b57cec5SDimitry Andric     size_t *Var;
450b57cec5SDimitry Andric   } NameVarPairs[] = {
460b57cec5SDimitry Andric       {"stat::number_of_executed_units:", &Res.number_of_executed_units},
470b57cec5SDimitry Andric       {"stat::peak_rss_mb:", &Res.peak_rss_mb},
480b57cec5SDimitry Andric       {"stat::average_exec_per_sec:", &Res.average_exec_per_sec},
490b57cec5SDimitry Andric       {nullptr, nullptr},
500b57cec5SDimitry Andric   };
510b57cec5SDimitry Andric   while (std::getline(In, Line, '\n')) {
520b57cec5SDimitry Andric     if (Line.find("stat::") != 0) continue;
530b57cec5SDimitry Andric     std::istringstream ISS(Line);
540b57cec5SDimitry Andric     std::string Name;
550b57cec5SDimitry Andric     size_t Val;
560b57cec5SDimitry Andric     ISS >> Name >> Val;
570b57cec5SDimitry Andric     for (size_t i = 0; NameVarPairs[i].Name; i++)
580b57cec5SDimitry Andric       if (Name == NameVarPairs[i].Name)
590b57cec5SDimitry Andric         *NameVarPairs[i].Var = Val;
600b57cec5SDimitry Andric   }
610b57cec5SDimitry Andric   return Res;
620b57cec5SDimitry Andric }
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric struct FuzzJob {
650b57cec5SDimitry Andric   // Inputs.
660b57cec5SDimitry Andric   Command Cmd;
670b57cec5SDimitry Andric   std::string CorpusDir;
680b57cec5SDimitry Andric   std::string FeaturesDir;
690b57cec5SDimitry Andric   std::string LogPath;
700b57cec5SDimitry Andric   std::string SeedListPath;
710b57cec5SDimitry Andric   std::string CFPath;
720b57cec5SDimitry Andric   size_t      JobId;
730b57cec5SDimitry Andric 
740b57cec5SDimitry Andric   int         DftTimeInSeconds = 0;
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric   // Fuzzing Outputs.
770b57cec5SDimitry Andric   int ExitCode;
780b57cec5SDimitry Andric 
~FuzzJobfuzzer::FuzzJob790b57cec5SDimitry Andric   ~FuzzJob() {
800b57cec5SDimitry Andric     RemoveFile(CFPath);
810b57cec5SDimitry Andric     RemoveFile(LogPath);
820b57cec5SDimitry Andric     RemoveFile(SeedListPath);
830b57cec5SDimitry Andric     RmDirRecursive(CorpusDir);
840b57cec5SDimitry Andric     RmDirRecursive(FeaturesDir);
850b57cec5SDimitry Andric   }
860b57cec5SDimitry Andric };
870b57cec5SDimitry Andric 
880b57cec5SDimitry Andric struct GlobalEnv {
89349cc55cSDimitry Andric   std::vector<std::string> Args;
90349cc55cSDimitry Andric   std::vector<std::string> CorpusDirs;
910b57cec5SDimitry Andric   std::string MainCorpusDir;
920b57cec5SDimitry Andric   std::string TempDir;
930b57cec5SDimitry Andric   std::string DFTDir;
940b57cec5SDimitry Andric   std::string DataFlowBinary;
95349cc55cSDimitry Andric   std::set<uint32_t> Features, Cov;
96349cc55cSDimitry Andric   std::set<std::string> FilesWithDFT;
97349cc55cSDimitry Andric   std::vector<std::string> Files;
98349cc55cSDimitry Andric   std::vector<std::size_t> FilesSizes;
990b57cec5SDimitry Andric   Random *Rand;
1000b57cec5SDimitry Andric   std::chrono::system_clock::time_point ProcessStartTime;
1010b57cec5SDimitry Andric   int Verbosity = 0;
102349cc55cSDimitry Andric   int Group = 0;
103349cc55cSDimitry Andric   int NumCorpuses = 8;
1040b57cec5SDimitry Andric 
1050b57cec5SDimitry Andric   size_t NumTimeouts = 0;
1060b57cec5SDimitry Andric   size_t NumOOMs = 0;
1070b57cec5SDimitry Andric   size_t NumCrashes = 0;
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric 
1100b57cec5SDimitry Andric   size_t NumRuns = 0;
1110b57cec5SDimitry Andric 
StopFilefuzzer::GlobalEnv1120b57cec5SDimitry Andric   std::string StopFile() { return DirPlusFile(TempDir, "STOP"); }
1130b57cec5SDimitry Andric 
secondsSinceProcessStartUpfuzzer::GlobalEnv1140b57cec5SDimitry Andric   size_t secondsSinceProcessStartUp() const {
1150b57cec5SDimitry Andric     return std::chrono::duration_cast<std::chrono::seconds>(
1160b57cec5SDimitry Andric                std::chrono::system_clock::now() - ProcessStartTime)
1170b57cec5SDimitry Andric         .count();
1180b57cec5SDimitry Andric   }
1190b57cec5SDimitry Andric 
CreateNewJobfuzzer::GlobalEnv1200b57cec5SDimitry Andric   FuzzJob *CreateNewJob(size_t JobId) {
1210b57cec5SDimitry Andric     Command Cmd(Args);
1220b57cec5SDimitry Andric     Cmd.removeFlag("fork");
1230b57cec5SDimitry Andric     Cmd.removeFlag("runs");
1240b57cec5SDimitry Andric     Cmd.removeFlag("collect_data_flow");
1250b57cec5SDimitry Andric     for (auto &C : CorpusDirs) // Remove all corpora from the args.
1260b57cec5SDimitry Andric       Cmd.removeArgument(C);
1270b57cec5SDimitry Andric     Cmd.addFlag("reload", "0");  // working in an isolated dir, no reload.
1280b57cec5SDimitry Andric     Cmd.addFlag("print_final_stats", "1");
1290b57cec5SDimitry Andric     Cmd.addFlag("print_funcs", "0");  // no need to spend time symbolizing.
1300b57cec5SDimitry Andric     Cmd.addFlag("max_total_time", std::to_string(std::min((size_t)300, JobId)));
1310b57cec5SDimitry Andric     Cmd.addFlag("stop_file", StopFile());
1320b57cec5SDimitry Andric     if (!DataFlowBinary.empty()) {
1330b57cec5SDimitry Andric       Cmd.addFlag("data_flow_trace", DFTDir);
1340b57cec5SDimitry Andric       if (!Cmd.hasFlag("focus_function"))
1350b57cec5SDimitry Andric         Cmd.addFlag("focus_function", "auto");
1360b57cec5SDimitry Andric     }
1370b57cec5SDimitry Andric     auto Job = new FuzzJob;
1380b57cec5SDimitry Andric     std::string Seeds;
1390b57cec5SDimitry Andric     if (size_t CorpusSubsetSize =
1400b57cec5SDimitry Andric             std::min(Files.size(), (size_t)sqrt(Files.size() + 2))) {
1410b57cec5SDimitry Andric       auto Time1 = std::chrono::system_clock::now();
142349cc55cSDimitry Andric       if (Group) { // whether to group the corpus.
143349cc55cSDimitry Andric         size_t AverageCorpusSize = Files.size() / NumCorpuses + 1;
144349cc55cSDimitry Andric         size_t StartIndex = ((JobId - 1) % NumCorpuses) * AverageCorpusSize;
145349cc55cSDimitry Andric         for (size_t i = 0; i < CorpusSubsetSize; i++) {
146349cc55cSDimitry Andric           size_t RandNum = (*Rand)(AverageCorpusSize);
147349cc55cSDimitry Andric           size_t Index = RandNum + StartIndex;
148349cc55cSDimitry Andric           Index = Index < Files.size() ? Index
149349cc55cSDimitry Andric                                        : Rand->SkewTowardsLast(Files.size());
150349cc55cSDimitry Andric           auto &SF = Files[Index];
151349cc55cSDimitry Andric           Seeds += (Seeds.empty() ? "" : ",") + SF;
152349cc55cSDimitry Andric           CollectDFT(SF);
153349cc55cSDimitry Andric         }
154349cc55cSDimitry Andric       } else {
1550b57cec5SDimitry Andric         for (size_t i = 0; i < CorpusSubsetSize; i++) {
1560b57cec5SDimitry Andric           auto &SF = Files[Rand->SkewTowardsLast(Files.size())];
1570b57cec5SDimitry Andric           Seeds += (Seeds.empty() ? "" : ",") + SF;
1580b57cec5SDimitry Andric           CollectDFT(SF);
1590b57cec5SDimitry Andric         }
160349cc55cSDimitry Andric       }
1610b57cec5SDimitry Andric       auto Time2 = std::chrono::system_clock::now();
162fe6060f1SDimitry Andric       auto DftTimeInSeconds = duration_cast<seconds>(Time2 - Time1).count();
163fe6060f1SDimitry Andric       assert(DftTimeInSeconds < std::numeric_limits<int>::max());
164fe6060f1SDimitry Andric       Job->DftTimeInSeconds = static_cast<int>(DftTimeInSeconds);
1650b57cec5SDimitry Andric     }
1660b57cec5SDimitry Andric     if (!Seeds.empty()) {
1670b57cec5SDimitry Andric       Job->SeedListPath =
1680b57cec5SDimitry Andric           DirPlusFile(TempDir, std::to_string(JobId) + ".seeds");
1690b57cec5SDimitry Andric       WriteToFile(Seeds, Job->SeedListPath);
1700b57cec5SDimitry Andric       Cmd.addFlag("seed_inputs", "@" + Job->SeedListPath);
1710b57cec5SDimitry Andric     }
1720b57cec5SDimitry Andric     Job->LogPath = DirPlusFile(TempDir, std::to_string(JobId) + ".log");
1730b57cec5SDimitry Andric     Job->CorpusDir = DirPlusFile(TempDir, "C" + std::to_string(JobId));
1740b57cec5SDimitry Andric     Job->FeaturesDir = DirPlusFile(TempDir, "F" + std::to_string(JobId));
1750b57cec5SDimitry Andric     Job->CFPath = DirPlusFile(TempDir, std::to_string(JobId) + ".merge");
1760b57cec5SDimitry Andric     Job->JobId = JobId;
1770b57cec5SDimitry Andric 
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric     Cmd.addArgument(Job->CorpusDir);
1800b57cec5SDimitry Andric     Cmd.addFlag("features_dir", Job->FeaturesDir);
1810b57cec5SDimitry Andric 
1820b57cec5SDimitry Andric     for (auto &D : {Job->CorpusDir, Job->FeaturesDir}) {
1830b57cec5SDimitry Andric       RmDirRecursive(D);
1840b57cec5SDimitry Andric       MkDir(D);
1850b57cec5SDimitry Andric     }
1860b57cec5SDimitry Andric 
1870b57cec5SDimitry Andric     Cmd.setOutputFile(Job->LogPath);
1880b57cec5SDimitry Andric     Cmd.combineOutAndErr();
1890b57cec5SDimitry Andric 
1900b57cec5SDimitry Andric     Job->Cmd = Cmd;
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric     if (Verbosity >= 2)
1930b57cec5SDimitry Andric       Printf("Job %zd/%p Created: %s\n", JobId, Job,
1940b57cec5SDimitry Andric              Job->Cmd.toString().c_str());
1950b57cec5SDimitry Andric     // Start from very short runs and gradually increase them.
1960b57cec5SDimitry Andric     return Job;
1970b57cec5SDimitry Andric   }
1980b57cec5SDimitry Andric 
RunOneMergeJobfuzzer::GlobalEnv1990b57cec5SDimitry Andric   void RunOneMergeJob(FuzzJob *Job) {
2000b57cec5SDimitry Andric     auto Stats = ParseFinalStatsFromLog(Job->LogPath);
2010b57cec5SDimitry Andric     NumRuns += Stats.number_of_executed_units;
2020b57cec5SDimitry Andric 
203349cc55cSDimitry Andric     std::vector<SizedFile> TempFiles, MergeCandidates;
2040b57cec5SDimitry Andric     // Read all newly created inputs and their feature sets.
2050b57cec5SDimitry Andric     // Choose only those inputs that have new features.
2060b57cec5SDimitry Andric     GetSizedFilesFromDir(Job->CorpusDir, &TempFiles);
2070b57cec5SDimitry Andric     std::sort(TempFiles.begin(), TempFiles.end());
2080b57cec5SDimitry Andric     for (auto &F : TempFiles) {
2090b57cec5SDimitry Andric       auto FeatureFile = F.File;
2100b57cec5SDimitry Andric       FeatureFile.replace(0, Job->CorpusDir.size(), Job->FeaturesDir);
2110b57cec5SDimitry Andric       auto FeatureBytes = FileToVector(FeatureFile, 0, false);
2120b57cec5SDimitry Andric       assert((FeatureBytes.size() % sizeof(uint32_t)) == 0);
213349cc55cSDimitry Andric       std::vector<uint32_t> NewFeatures(FeatureBytes.size() / sizeof(uint32_t));
2140b57cec5SDimitry Andric       memcpy(NewFeatures.data(), FeatureBytes.data(), FeatureBytes.size());
2150b57cec5SDimitry Andric       for (auto Ft : NewFeatures) {
2160b57cec5SDimitry Andric         if (!Features.count(Ft)) {
2170b57cec5SDimitry Andric           MergeCandidates.push_back(F);
2180b57cec5SDimitry Andric           break;
2190b57cec5SDimitry Andric         }
2200b57cec5SDimitry Andric       }
2210b57cec5SDimitry Andric     }
2220b57cec5SDimitry Andric     // if (!FilesToAdd.empty() || Job->ExitCode != 0)
223bdd1243dSDimitry Andric     Printf("#%zd: cov: %zd ft: %zd corp: %zd exec/s: %zd "
2240b57cec5SDimitry Andric            "oom/timeout/crash: %zd/%zd/%zd time: %zds job: %zd dft_time: %d\n",
2250b57cec5SDimitry Andric            NumRuns, Cov.size(), Features.size(), Files.size(),
2260b57cec5SDimitry Andric            Stats.average_exec_per_sec, NumOOMs, NumTimeouts, NumCrashes,
2270b57cec5SDimitry Andric            secondsSinceProcessStartUp(), Job->JobId, Job->DftTimeInSeconds);
2280b57cec5SDimitry Andric 
2290b57cec5SDimitry Andric     if (MergeCandidates.empty()) return;
2300b57cec5SDimitry Andric 
231349cc55cSDimitry Andric     std::vector<std::string> FilesToAdd;
232349cc55cSDimitry Andric     std::set<uint32_t> NewFeatures, NewCov;
233349cc55cSDimitry Andric     bool IsSetCoverMerge =
234349cc55cSDimitry Andric         !Job->Cmd.getFlagValue("set_cover_merge").compare("1");
2350b57cec5SDimitry Andric     CrashResistantMerge(Args, {}, MergeCandidates, &FilesToAdd, Features,
236349cc55cSDimitry Andric                         &NewFeatures, Cov, &NewCov, Job->CFPath, false,
237349cc55cSDimitry Andric                         IsSetCoverMerge);
2380b57cec5SDimitry Andric     for (auto &Path : FilesToAdd) {
2390b57cec5SDimitry Andric       auto U = FileToVector(Path);
2400b57cec5SDimitry Andric       auto NewPath = DirPlusFile(MainCorpusDir, Hash(U));
2410b57cec5SDimitry Andric       WriteToFile(U, NewPath);
242349cc55cSDimitry Andric       if (Group) { // Insert the queue according to the size of the seed.
243349cc55cSDimitry Andric         size_t UnitSize = U.size();
244349cc55cSDimitry Andric         auto Idx =
245349cc55cSDimitry Andric             std::upper_bound(FilesSizes.begin(), FilesSizes.end(), UnitSize) -
246349cc55cSDimitry Andric             FilesSizes.begin();
247349cc55cSDimitry Andric         FilesSizes.insert(FilesSizes.begin() + Idx, UnitSize);
248349cc55cSDimitry Andric         Files.insert(Files.begin() + Idx, NewPath);
249349cc55cSDimitry Andric       } else {
2500b57cec5SDimitry Andric         Files.push_back(NewPath);
2510b57cec5SDimitry Andric       }
252349cc55cSDimitry Andric     }
2530b57cec5SDimitry Andric     Features.insert(NewFeatures.begin(), NewFeatures.end());
2540b57cec5SDimitry Andric     Cov.insert(NewCov.begin(), NewCov.end());
2550b57cec5SDimitry Andric     for (auto Idx : NewCov)
2560b57cec5SDimitry Andric       if (auto *TE = TPC.PCTableEntryByIdx(Idx))
2570b57cec5SDimitry Andric         if (TPC.PcIsFuncEntry(TE))
2580b57cec5SDimitry Andric           PrintPC("  NEW_FUNC: %p %F %L\n", "",
2590b57cec5SDimitry Andric                   TPC.GetNextInstructionPc(TE->PC));
2600b57cec5SDimitry Andric   }
2610b57cec5SDimitry Andric 
CollectDFTfuzzer::GlobalEnv2620b57cec5SDimitry Andric   void CollectDFT(const std::string &InputPath) {
2630b57cec5SDimitry Andric     if (DataFlowBinary.empty()) return;
2640b57cec5SDimitry Andric     if (!FilesWithDFT.insert(InputPath).second) return;
2650b57cec5SDimitry Andric     Command Cmd(Args);
2660b57cec5SDimitry Andric     Cmd.removeFlag("fork");
2670b57cec5SDimitry Andric     Cmd.removeFlag("runs");
2680b57cec5SDimitry Andric     Cmd.addFlag("data_flow_trace", DFTDir);
2690b57cec5SDimitry Andric     Cmd.addArgument(InputPath);
2700b57cec5SDimitry Andric     for (auto &C : CorpusDirs) // Remove all corpora from the args.
2710b57cec5SDimitry Andric       Cmd.removeArgument(C);
2720b57cec5SDimitry Andric     Cmd.setOutputFile(DirPlusFile(TempDir, "dft.log"));
2730b57cec5SDimitry Andric     Cmd.combineOutAndErr();
2740b57cec5SDimitry Andric     // Printf("CollectDFT: %s\n", Cmd.toString().c_str());
2750b57cec5SDimitry Andric     ExecuteCommand(Cmd);
2760b57cec5SDimitry Andric   }
2770b57cec5SDimitry Andric 
2780b57cec5SDimitry Andric };
2790b57cec5SDimitry Andric 
2800b57cec5SDimitry Andric struct JobQueue {
2810b57cec5SDimitry Andric   std::queue<FuzzJob *> Qu;
2820b57cec5SDimitry Andric   std::mutex Mu;
2830b57cec5SDimitry Andric   std::condition_variable Cv;
2840b57cec5SDimitry Andric 
Pushfuzzer::JobQueue2850b57cec5SDimitry Andric   void Push(FuzzJob *Job) {
2860b57cec5SDimitry Andric     {
2870b57cec5SDimitry Andric       std::lock_guard<std::mutex> Lock(Mu);
2880b57cec5SDimitry Andric       Qu.push(Job);
2890b57cec5SDimitry Andric     }
2900b57cec5SDimitry Andric     Cv.notify_one();
2910b57cec5SDimitry Andric   }
Popfuzzer::JobQueue2920b57cec5SDimitry Andric   FuzzJob *Pop() {
2930b57cec5SDimitry Andric     std::unique_lock<std::mutex> Lk(Mu);
2940b57cec5SDimitry Andric     // std::lock_guard<std::mutex> Lock(Mu);
2950b57cec5SDimitry Andric     Cv.wait(Lk, [&]{return !Qu.empty();});
2960b57cec5SDimitry Andric     assert(!Qu.empty());
2970b57cec5SDimitry Andric     auto Job = Qu.front();
2980b57cec5SDimitry Andric     Qu.pop();
2990b57cec5SDimitry Andric     return Job;
3000b57cec5SDimitry Andric   }
3010b57cec5SDimitry Andric };
3020b57cec5SDimitry Andric 
WorkerThread(JobQueue * FuzzQ,JobQueue * MergeQ)3030b57cec5SDimitry Andric void WorkerThread(JobQueue *FuzzQ, JobQueue *MergeQ) {
3040b57cec5SDimitry Andric   while (auto Job = FuzzQ->Pop()) {
3050b57cec5SDimitry Andric     // Printf("WorkerThread: job %p\n", Job);
3060b57cec5SDimitry Andric     Job->ExitCode = ExecuteCommand(Job->Cmd);
3070b57cec5SDimitry Andric     MergeQ->Push(Job);
3080b57cec5SDimitry Andric   }
3090b57cec5SDimitry Andric }
3100b57cec5SDimitry Andric 
3110b57cec5SDimitry Andric // This is just a skeleton of an experimental -fork=1 feature.
FuzzWithFork(Random & Rand,const FuzzingOptions & Options,const std::vector<std::string> & Args,const std::vector<std::string> & CorpusDirs,int NumJobs)3120b57cec5SDimitry Andric void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
313349cc55cSDimitry Andric                   const std::vector<std::string> &Args,
314349cc55cSDimitry Andric                   const std::vector<std::string> &CorpusDirs, int NumJobs) {
3150b57cec5SDimitry Andric   Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs);
3160b57cec5SDimitry Andric 
3170b57cec5SDimitry Andric   GlobalEnv Env;
3180b57cec5SDimitry Andric   Env.Args = Args;
3190b57cec5SDimitry Andric   Env.CorpusDirs = CorpusDirs;
3200b57cec5SDimitry Andric   Env.Rand = &Rand;
3210b57cec5SDimitry Andric   Env.Verbosity = Options.Verbosity;
3220b57cec5SDimitry Andric   Env.ProcessStartTime = std::chrono::system_clock::now();
3230b57cec5SDimitry Andric   Env.DataFlowBinary = Options.CollectDataFlow;
324349cc55cSDimitry Andric   Env.Group = Options.ForkCorpusGroups;
3250b57cec5SDimitry Andric 
326349cc55cSDimitry Andric   std::vector<SizedFile> SeedFiles;
3270b57cec5SDimitry Andric   for (auto &Dir : CorpusDirs)
3280b57cec5SDimitry Andric     GetSizedFilesFromDir(Dir, &SeedFiles);
3290b57cec5SDimitry Andric   std::sort(SeedFiles.begin(), SeedFiles.end());
3305ffd83dbSDimitry Andric   Env.TempDir = TempPath("FuzzWithFork", ".dir");
3310b57cec5SDimitry Andric   Env.DFTDir = DirPlusFile(Env.TempDir, "DFT");
3320b57cec5SDimitry Andric   RmDirRecursive(Env.TempDir);  // in case there is a leftover from old runs.
3330b57cec5SDimitry Andric   MkDir(Env.TempDir);
3340b57cec5SDimitry Andric   MkDir(Env.DFTDir);
3350b57cec5SDimitry Andric 
3360b57cec5SDimitry Andric 
3370b57cec5SDimitry Andric   if (CorpusDirs.empty())
3380b57cec5SDimitry Andric     MkDir(Env.MainCorpusDir = DirPlusFile(Env.TempDir, "C"));
3390b57cec5SDimitry Andric   else
3400b57cec5SDimitry Andric     Env.MainCorpusDir = CorpusDirs[0];
3410b57cec5SDimitry Andric 
342e8d8bef9SDimitry Andric   if (Options.KeepSeed) {
343e8d8bef9SDimitry Andric     for (auto &File : SeedFiles)
344e8d8bef9SDimitry Andric       Env.Files.push_back(File.File);
345e8d8bef9SDimitry Andric   } else {
3460b57cec5SDimitry Andric     auto CFPath = DirPlusFile(Env.TempDir, "merge.txt");
347349cc55cSDimitry Andric     std::set<uint32_t> NewFeatures, NewCov;
348fe6060f1SDimitry Andric     CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, Env.Features,
349349cc55cSDimitry Andric                         &NewFeatures, Env.Cov, &NewCov, CFPath,
350349cc55cSDimitry Andric                         /*Verbose=*/false, /*IsSetCoverMerge=*/false);
351fe6060f1SDimitry Andric     Env.Features.insert(NewFeatures.begin(), NewFeatures.end());
352*0fca6ea1SDimitry Andric     Env.Cov.insert(NewCov.begin(), NewCov.end());
3530b57cec5SDimitry Andric     RemoveFile(CFPath);
354e8d8bef9SDimitry Andric   }
355349cc55cSDimitry Andric 
356349cc55cSDimitry Andric   if (Env.Group) {
357349cc55cSDimitry Andric     for (auto &path : Env.Files)
358349cc55cSDimitry Andric       Env.FilesSizes.push_back(FileSize(path));
359349cc55cSDimitry Andric   }
360349cc55cSDimitry Andric 
3610b57cec5SDimitry Andric   Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs,
3620b57cec5SDimitry Andric          Env.Files.size(), Env.TempDir.c_str());
3630b57cec5SDimitry Andric 
3640b57cec5SDimitry Andric   int ExitCode = 0;
3650b57cec5SDimitry Andric 
3660b57cec5SDimitry Andric   JobQueue FuzzQ, MergeQ;
3670b57cec5SDimitry Andric 
3680b57cec5SDimitry Andric   auto StopJobs = [&]() {
3690b57cec5SDimitry Andric     for (int i = 0; i < NumJobs; i++)
3700b57cec5SDimitry Andric       FuzzQ.Push(nullptr);
3710b57cec5SDimitry Andric     MergeQ.Push(nullptr);
3720b57cec5SDimitry Andric     WriteToFile(Unit({1}), Env.StopFile());
3730b57cec5SDimitry Andric   };
3740b57cec5SDimitry Andric 
375349cc55cSDimitry Andric   size_t MergeCycle = 20;
376349cc55cSDimitry Andric   size_t JobExecuted = 0;
3770b57cec5SDimitry Andric   size_t JobId = 1;
378349cc55cSDimitry Andric   std::vector<std::thread> Threads;
3790b57cec5SDimitry Andric   for (int t = 0; t < NumJobs; t++) {
3800b57cec5SDimitry Andric     Threads.push_back(std::thread(WorkerThread, &FuzzQ, &MergeQ));
3810b57cec5SDimitry Andric     FuzzQ.Push(Env.CreateNewJob(JobId++));
3820b57cec5SDimitry Andric   }
3830b57cec5SDimitry Andric 
3840b57cec5SDimitry Andric   while (true) {
3850b57cec5SDimitry Andric     std::unique_ptr<FuzzJob> Job(MergeQ.Pop());
3860b57cec5SDimitry Andric     if (!Job)
3870b57cec5SDimitry Andric       break;
3880b57cec5SDimitry Andric     ExitCode = Job->ExitCode;
3890b57cec5SDimitry Andric     if (ExitCode == Options.InterruptExitCode) {
3900b57cec5SDimitry Andric       Printf("==%lu== libFuzzer: a child was interrupted; exiting\n", GetPid());
3910b57cec5SDimitry Andric       StopJobs();
3920b57cec5SDimitry Andric       break;
3930b57cec5SDimitry Andric     }
3940b57cec5SDimitry Andric     Fuzzer::MaybeExitGracefully();
3950b57cec5SDimitry Andric 
3960b57cec5SDimitry Andric     Env.RunOneMergeJob(Job.get());
3970b57cec5SDimitry Andric 
398349cc55cSDimitry Andric     // merge the corpus .
399349cc55cSDimitry Andric     JobExecuted++;
400349cc55cSDimitry Andric     if (Env.Group && JobExecuted >= MergeCycle) {
401349cc55cSDimitry Andric       std::vector<SizedFile> CurrentSeedFiles;
402349cc55cSDimitry Andric       for (auto &Dir : CorpusDirs)
403349cc55cSDimitry Andric         GetSizedFilesFromDir(Dir, &CurrentSeedFiles);
404349cc55cSDimitry Andric       std::sort(CurrentSeedFiles.begin(), CurrentSeedFiles.end());
405349cc55cSDimitry Andric 
406349cc55cSDimitry Andric       auto CFPath = DirPlusFile(Env.TempDir, "merge.txt");
407349cc55cSDimitry Andric       std::set<uint32_t> TmpNewFeatures, TmpNewCov;
408349cc55cSDimitry Andric       std::set<uint32_t> TmpFeatures, TmpCov;
409349cc55cSDimitry Andric       Env.Files.clear();
410349cc55cSDimitry Andric       Env.FilesSizes.clear();
411349cc55cSDimitry Andric       CrashResistantMerge(Env.Args, {}, CurrentSeedFiles, &Env.Files,
412349cc55cSDimitry Andric                           TmpFeatures, &TmpNewFeatures, TmpCov, &TmpNewCov,
413349cc55cSDimitry Andric                           CFPath, /*Verbose=*/false, /*IsSetCoverMerge=*/false);
414349cc55cSDimitry Andric       for (auto &path : Env.Files)
415349cc55cSDimitry Andric         Env.FilesSizes.push_back(FileSize(path));
416349cc55cSDimitry Andric       RemoveFile(CFPath);
417349cc55cSDimitry Andric       JobExecuted = 0;
418349cc55cSDimitry Andric       MergeCycle += 5;
419349cc55cSDimitry Andric     }
420349cc55cSDimitry Andric 
421349cc55cSDimitry Andric     // Since the number of corpus seeds will gradually increase, in order to
422349cc55cSDimitry Andric     // control the number in each group to be about three times the number of
423349cc55cSDimitry Andric     // seeds selected each time, the number of groups is dynamically adjusted.
424349cc55cSDimitry Andric     if (Env.Files.size() < 2000)
425349cc55cSDimitry Andric       Env.NumCorpuses = 12;
426349cc55cSDimitry Andric     else if (Env.Files.size() < 6000)
427349cc55cSDimitry Andric       Env.NumCorpuses = 20;
428349cc55cSDimitry Andric     else if (Env.Files.size() < 12000)
429349cc55cSDimitry Andric       Env.NumCorpuses = 32;
430349cc55cSDimitry Andric     else if (Env.Files.size() < 16000)
431349cc55cSDimitry Andric       Env.NumCorpuses = 40;
432349cc55cSDimitry Andric     else if (Env.Files.size() < 24000)
433349cc55cSDimitry Andric       Env.NumCorpuses = 60;
434349cc55cSDimitry Andric     else
435349cc55cSDimitry Andric       Env.NumCorpuses = 80;
436349cc55cSDimitry Andric 
437349cc55cSDimitry Andric     // Continue if our crash is one of the ignored ones.
4380b57cec5SDimitry Andric     if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode)
4390b57cec5SDimitry Andric       Env.NumTimeouts++;
4400b57cec5SDimitry Andric     else if (Options.IgnoreOOMs && ExitCode == Options.OOMExitCode)
4410b57cec5SDimitry Andric       Env.NumOOMs++;
4420b57cec5SDimitry Andric     else if (ExitCode != 0) {
4430b57cec5SDimitry Andric       Env.NumCrashes++;
4440b57cec5SDimitry Andric       if (Options.IgnoreCrashes) {
4450b57cec5SDimitry Andric         std::ifstream In(Job->LogPath);
4460b57cec5SDimitry Andric         std::string Line;
4470b57cec5SDimitry Andric         while (std::getline(In, Line, '\n'))
4480b57cec5SDimitry Andric           if (Line.find("ERROR:") != Line.npos ||
4490b57cec5SDimitry Andric               Line.find("runtime error:") != Line.npos)
4500b57cec5SDimitry Andric             Printf("%s\n", Line.c_str());
4510b57cec5SDimitry Andric       } else {
4520b57cec5SDimitry Andric         // And exit if we don't ignore this crash.
4530b57cec5SDimitry Andric         Printf("INFO: log from the inner process:\n%s",
4540b57cec5SDimitry Andric                FileToString(Job->LogPath).c_str());
4550b57cec5SDimitry Andric         StopJobs();
4560b57cec5SDimitry Andric         break;
4570b57cec5SDimitry Andric       }
4580b57cec5SDimitry Andric     }
4590b57cec5SDimitry Andric 
4600b57cec5SDimitry Andric     // Stop if we are over the time budget.
4610b57cec5SDimitry Andric     // This is not precise, since other threads are still running
4620b57cec5SDimitry Andric     // and we will wait while joining them.
4630b57cec5SDimitry Andric     // We also don't stop instantly: other jobs need to finish.
4640b57cec5SDimitry Andric     if (Options.MaxTotalTimeSec > 0 &&
4650b57cec5SDimitry Andric         Env.secondsSinceProcessStartUp() >= (size_t)Options.MaxTotalTimeSec) {
4660b57cec5SDimitry Andric       Printf("INFO: fuzzed for %zd seconds, wrapping up soon\n",
4670b57cec5SDimitry Andric              Env.secondsSinceProcessStartUp());
4680b57cec5SDimitry Andric       StopJobs();
4690b57cec5SDimitry Andric       break;
4700b57cec5SDimitry Andric     }
4710b57cec5SDimitry Andric     if (Env.NumRuns >= Options.MaxNumberOfRuns) {
4720b57cec5SDimitry Andric       Printf("INFO: fuzzed for %zd iterations, wrapping up soon\n",
4730b57cec5SDimitry Andric              Env.NumRuns);
4740b57cec5SDimitry Andric       StopJobs();
4750b57cec5SDimitry Andric       break;
4760b57cec5SDimitry Andric     }
4770b57cec5SDimitry Andric 
4780b57cec5SDimitry Andric     FuzzQ.Push(Env.CreateNewJob(JobId++));
4790b57cec5SDimitry Andric   }
4800b57cec5SDimitry Andric 
4810b57cec5SDimitry Andric   for (auto &T : Threads)
4820b57cec5SDimitry Andric     T.join();
4830b57cec5SDimitry Andric 
4840b57cec5SDimitry Andric   // The workers have terminated. Don't try to remove the directory before they
4850b57cec5SDimitry Andric   // terminate to avoid a race condition preventing cleanup on Windows.
4860b57cec5SDimitry Andric   RmDirRecursive(Env.TempDir);
4870b57cec5SDimitry Andric 
4880b57cec5SDimitry Andric   // Use the exit code from the last child process.
4890b57cec5SDimitry Andric   Printf("INFO: exiting: %d time: %zds\n", ExitCode,
4900b57cec5SDimitry Andric          Env.secondsSinceProcessStartUp());
4910b57cec5SDimitry Andric   exit(ExitCode);
4920b57cec5SDimitry Andric }
4930b57cec5SDimitry Andric 
4940b57cec5SDimitry Andric } // namespace fuzzer
495