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