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 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 790b57cec5SDimitry 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 1120b57cec5SDimitry Andric std::string StopFile() { return DirPlusFile(TempDir, "STOP"); } 1130b57cec5SDimitry Andric 1140b57cec5SDimitry 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 1200b57cec5SDimitry 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 1990b57cec5SDimitry 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 2620b57cec5SDimitry 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 2850b57cec5SDimitry 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 } 2920b57cec5SDimitry 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 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. 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