1commit f80733b3b1b5e05e7dfd7a071f60050fe20108c3 2Author: Jesse Schwartzentruber <truber@mozilla.com> 3Date: Mon Mar 1 15:47:38 2021 -0500 4 5 [libfuzzer] In most cases, return instead of exit(). 6 7diff --git a/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.cpp b/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.cpp 8index 0e9cdf7e66b1..06ea287a3cfe 100644 9--- a/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.cpp 10+++ b/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.cpp 11@@ -102,9 +102,11 @@ Vector<double> BlockCoverage::FunctionWeights(size_t NumFunctions) const { 12 return Res; 13 } 14 15-void DataFlowTrace::ReadCoverage(const std::string &DirPath) { 16+int DataFlowTrace::ReadCoverage(const std::string &DirPath) { 17 Vector<SizedFile> Files; 18- GetSizedFilesFromDir(DirPath, &Files); 19+ int Res = GetSizedFilesFromDir(DirPath, &Files); 20+ if (Res != 0) 21+ return Res; 22 for (auto &SF : Files) { 23 auto Name = Basename(SF.File); 24 if (Name == kFunctionsTxt) continue; 25@@ -112,6 +114,7 @@ void DataFlowTrace::ReadCoverage(const std::string &DirPath) { 26 std::ifstream IF(SF.File); 27 Coverage.AppendCoverage(IF); 28 } 29+ return 0; 30 } 31 32 static void DFTStringAppendToVector(Vector<uint8_t> *DFT, 33@@ -157,12 +160,14 @@ static bool ParseDFTLine(const std::string &Line, size_t *FunctionNum, 34 return true; 35 } 36 37-bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, 38+int DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, 39 Vector<SizedFile> &CorporaFiles, Random &Rand) { 40- if (DirPath.empty()) return false; 41+ if (DirPath.empty()) return 0; 42 Printf("INFO: DataFlowTrace: reading from '%s'\n", DirPath.c_str()); 43 Vector<SizedFile> Files; 44- GetSizedFilesFromDir(DirPath, &Files); 45+ int Res = GetSizedFilesFromDir(DirPath, &Files); 46+ if (Res != 0) 47+ return Res; 48 std::string L; 49 size_t FocusFuncIdx = SIZE_MAX; 50 Vector<std::string> FunctionNames; 51@@ -181,14 +186,16 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, 52 FocusFuncIdx = NumFunctions - 1; 53 } 54 if (!NumFunctions) 55- return false; 56+ return 0; 57 58 if (*FocusFunction == "auto") { 59 // AUTOFOCUS works like this: 60 // * reads the coverage data from the DFT files. 61 // * assigns weights to functions based on coverage. 62 // * chooses a random function according to the weights. 63- ReadCoverage(DirPath); 64+ Res = ReadCoverage(DirPath); 65+ if (Res != 0) 66+ return Res; 67 auto Weights = Coverage.FunctionWeights(NumFunctions); 68 Vector<double> Intervals(NumFunctions + 1); 69 std::iota(Intervals.begin(), Intervals.end(), 0); 70@@ -209,7 +216,7 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, 71 } 72 73 if (!NumFunctions || FocusFuncIdx == SIZE_MAX || Files.size() <= 1) 74- return false; 75+ return 0; 76 77 // Read traces. 78 size_t NumTraceFiles = 0; 79@@ -228,8 +235,10 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, 80 FunctionNum == FocusFuncIdx) { 81 NumTracesWithFocusFunction++; 82 83- if (FunctionNum >= NumFunctions) 84- return ParseError("N is greater than the number of functions", L); 85+ if (FunctionNum >= NumFunctions) { 86+ ParseError("N is greater than the number of functions", L); 87+ return 0; 88+ } 89 Traces[Name] = DFTStringToVector(DFTString); 90 // Print just a few small traces. 91 if (NumTracesWithFocusFunction <= 3 && DFTString.size() <= 16) 92@@ -241,7 +250,7 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, 93 Printf("INFO: DataFlowTrace: %zd trace files, %zd functions, " 94 "%zd traces with focus function\n", 95 NumTraceFiles, NumFunctions, NumTracesWithFocusFunction); 96- return NumTraceFiles > 0; 97+ return 0; 98 } 99 100 int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, 101diff --git a/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.h b/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.h 102index d6e3de30a4ef..767bad24f1d0 100644 103--- a/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.h 104+++ b/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.h 105@@ -113,8 +113,8 @@ class BlockCoverage { 106 107 class DataFlowTrace { 108 public: 109- void ReadCoverage(const std::string &DirPath); 110- bool Init(const std::string &DirPath, std::string *FocusFunction, 111+ int ReadCoverage(const std::string &DirPath); 112+ int Init(const std::string &DirPath, std::string *FocusFunction, 113 Vector<SizedFile> &CorporaFiles, Random &Rand); 114 void Clear() { Traces.clear(); } 115 const Vector<uint8_t> *Get(const std::string &InputSha1) const { 116diff --git a/tools/fuzzing/libfuzzer/FuzzerDriver.cpp b/tools/fuzzing/libfuzzer/FuzzerDriver.cpp 117index cd720200848b..bedad16efa7b 100644 118--- a/tools/fuzzing/libfuzzer/FuzzerDriver.cpp 119+++ b/tools/fuzzing/libfuzzer/FuzzerDriver.cpp 120@@ -326,7 +326,7 @@ int CleanseCrashInput(const Vector<std::string> &Args, 121 if (Inputs->size() != 1 || !Flags.exact_artifact_path) { 122 Printf("ERROR: -cleanse_crash should be given one input file and" 123 " -exact_artifact_path\n"); 124- exit(1); 125+ return 1; 126 } 127 std::string InputFilePath = Inputs->at(0); 128 std::string OutputFilePath = Flags.exact_artifact_path; 129@@ -380,7 +380,7 @@ int MinimizeCrashInput(const Vector<std::string> &Args, 130 const FuzzingOptions &Options) { 131 if (Inputs->size() != 1) { 132 Printf("ERROR: -minimize_crash should be given one input file\n"); 133- exit(1); 134+ return 1; 135 } 136 std::string InputFilePath = Inputs->at(0); 137 Command BaseCmd(Args); 138@@ -411,7 +411,7 @@ int MinimizeCrashInput(const Vector<std::string> &Args, 139 bool Success = ExecuteCommand(Cmd, &CmdOutput); 140 if (Success) { 141 Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str()); 142- exit(1); 143+ return 1; 144 } 145 Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize " 146 "it further\n", 147@@ -466,42 +466,51 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { 148 Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size()); 149 if (U.size() < 2) { 150 Printf("INFO: The input is small enough, exiting\n"); 151- exit(0); 152+ return 0; 153 } 154 F->SetMaxInputLen(U.size()); 155 F->SetMaxMutationLen(U.size() - 1); 156 F->MinimizeCrashLoop(U); 157 Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n"); 158- exit(0); 159 return 0; 160 } 161 162-void Merge(Fuzzer *F, FuzzingOptions &Options, const Vector<std::string> &Args, 163+int Merge(Fuzzer *F, FuzzingOptions &Options, const Vector<std::string> &Args, 164 const Vector<std::string> &Corpora, const char *CFPathOrNull) { 165 if (Corpora.size() < 2) { 166 Printf("INFO: Merge requires two or more corpus dirs\n"); 167- exit(0); 168+ return 0; 169 } 170 171 Vector<SizedFile> OldCorpus, NewCorpus; 172- GetSizedFilesFromDir(Corpora[0], &OldCorpus); 173- for (size_t i = 1; i < Corpora.size(); i++) 174- GetSizedFilesFromDir(Corpora[i], &NewCorpus); 175+ int Res = GetSizedFilesFromDir(Corpora[0], &OldCorpus); 176+ if (Res != 0) 177+ return Res; 178+ for (size_t i = 1; i < Corpora.size(); i++) { 179+ Res = GetSizedFilesFromDir(Corpora[i], &NewCorpus); 180+ if (Res != 0) 181+ return Res; 182+ } 183 std::sort(OldCorpus.begin(), OldCorpus.end()); 184 std::sort(NewCorpus.begin(), NewCorpus.end()); 185 186 std::string CFPath = CFPathOrNull ? CFPathOrNull : TempPath("Merge", ".txt"); 187 Vector<std::string> NewFiles; 188 Set<uint32_t> NewFeatures, NewCov; 189- CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures, 190+ Res = CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures, 191 {}, &NewCov, CFPath, true); 192+ if (Res != 0) 193+ return Res; 194+ 195+ if (F->isGracefulExitRequested()) 196+ return 0; 197 for (auto &Path : NewFiles) 198 F->WriteToOutputCorpus(FileToVector(Path, Options.MaxLen)); 199 // We are done, delete the control file if it was a temporary one. 200 if (!Flags.merge_control_file) 201 RemoveFile(CFPath); 202 203- exit(0); 204+ return 0; 205 } 206 207 int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict, 208@@ -570,10 +579,9 @@ int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict, 209 return 0; 210 } 211 212-Vector<std::string> ParseSeedInuts(const char *seed_inputs) { 213+int ParseSeedInuts(const char *seed_inputs, Vector<std::string> &Files) { 214 // Parse -seed_inputs=file1,file2,... or -seed_inputs=@seed_inputs_file 215- Vector<std::string> Files; 216- if (!seed_inputs) return Files; 217+ if (!seed_inputs) return 0; 218 std::string SeedInputs; 219 if (Flags.seed_inputs[0] == '@') 220 SeedInputs = FileToString(Flags.seed_inputs + 1); // File contains list. 221@@ -581,7 +589,7 @@ Vector<std::string> ParseSeedInuts(const char *seed_inputs) { 222 SeedInputs = Flags.seed_inputs; // seed_inputs contains the list. 223 if (SeedInputs.empty()) { 224 Printf("seed_inputs is empty or @file does not exist.\n"); 225- exit(1); 226+ return 1; 227 } 228 // Parse SeedInputs. 229 size_t comma_pos = 0; 230@@ -590,7 +598,7 @@ Vector<std::string> ParseSeedInuts(const char *seed_inputs) { 231 SeedInputs = SeedInputs.substr(0, comma_pos); 232 } 233 Files.push_back(SeedInputs); 234- return Files; 235+ return 0; 236 } 237 238 static Vector<SizedFile> ReadCorpora(const Vector<std::string> &CorpusDirs, 239@@ -624,7 +632,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { 240 ProgName = new std::string(Args[0]); 241 if (Argv0 != *ProgName) { 242 Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n"); 243- exit(1); 244+ return 1; 245 } 246 ParseFlags(Args, EF); 247 if (Flags.help) { 248@@ -723,7 +731,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { 249 if (!Options.FocusFunction.empty()) { 250 Printf("ERROR: The parameters `--entropic` and `--focus_function` cannot " 251 "be used together.\n"); 252- exit(1); 253+ return 1; 254 } 255 Printf("INFO: Running with entropic power schedule (0x%X, %d).\n", 256 Options.EntropicFeatureFrequencyThreshold, 257@@ -809,22 +817,21 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { 258 "*** executed the target code on a fixed set of inputs.\n" 259 "***\n"); 260 F->PrintFinalStats(); 261- exit(0); 262+ return 0; 263 } 264 265 if (Flags.fork) 266- FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork); 267+ return FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork); 268 269 if (Flags.merge) 270- Merge(F, Options, Args, *Inputs, Flags.merge_control_file); 271+ return Merge(F, Options, Args, *Inputs, Flags.merge_control_file); 272 273 if (Flags.merge_inner) { 274 const size_t kDefaultMaxMergeLen = 1 << 20; 275 if (Options.MaxLen == 0) 276 F->SetMaxInputLen(kDefaultMaxMergeLen); 277 assert(Flags.merge_control_file); 278- F->CrashResistantMergeInternalStep(Flags.merge_control_file); 279- exit(0); 280+ return F->CrashResistantMergeInternalStep(Flags.merge_control_file); 281 } 282 283 if (Flags.analyze_dict) { 284@@ -842,21 +849,31 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { 285 } 286 if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) { 287 Printf("Dictionary analysis failed\n"); 288- exit(1); 289+ return 1; 290 } 291 Printf("Dictionary analysis succeeded\n"); 292- exit(0); 293+ return 0; 294 } 295 296- auto CorporaFiles = ReadCorpora(*Inputs, ParseSeedInuts(Flags.seed_inputs)); 297- F->Loop(CorporaFiles); 298+ { 299+ Vector<std::string> Files; 300+ int Res = ParseSeedInuts(Flags.seed_inputs, Files); 301+ if (Res != 0) 302+ return Res; 303+ auto CorporaFiles = ReadCorpora(*Inputs, Files); 304+ Res = F->Loop(CorporaFiles); 305+ if (Res != 0) 306+ return Res; 307+ if (F->isGracefulExitRequested()) 308+ return 0; 309+ } 310 311 if (Flags.verbosity) 312 Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(), 313 F->secondsSinceProcessStartUp()); 314 F->PrintFinalStats(); 315 316- exit(0); // Don't let F destroy itself. 317+ return 0; // Don't let F destroy itself. 318 } 319 320 extern "C" ATTRIBUTE_INTERFACE int 321diff --git a/tools/fuzzing/libfuzzer/FuzzerFork.cpp b/tools/fuzzing/libfuzzer/FuzzerFork.cpp 322index d9e6b79443e0..ee2a99a250c1 100644 323--- a/tools/fuzzing/libfuzzer/FuzzerFork.cpp 324+++ b/tools/fuzzing/libfuzzer/FuzzerFork.cpp 325@@ -177,14 +177,16 @@ struct GlobalEnv { 326 return Job; 327 } 328 329- void RunOneMergeJob(FuzzJob *Job) { 330+ int RunOneMergeJob(FuzzJob *Job) { 331 auto Stats = ParseFinalStatsFromLog(Job->LogPath); 332 NumRuns += Stats.number_of_executed_units; 333 334 Vector<SizedFile> TempFiles, MergeCandidates; 335 // Read all newly created inputs and their feature sets. 336 // Choose only those inputs that have new features. 337- GetSizedFilesFromDir(Job->CorpusDir, &TempFiles); 338+ int Res = GetSizedFilesFromDir(Job->CorpusDir, &TempFiles); 339+ if (Res != 0) 340+ return Res; 341 std::sort(TempFiles.begin(), TempFiles.end()); 342 for (auto &F : TempFiles) { 343 auto FeatureFile = F.File; 344@@ -207,12 +209,14 @@ struct GlobalEnv { 345 Stats.average_exec_per_sec, NumOOMs, NumTimeouts, NumCrashes, 346 secondsSinceProcessStartUp(), Job->JobId, Job->DftTimeInSeconds); 347 348- if (MergeCandidates.empty()) return; 349+ if (MergeCandidates.empty()) return 0; 350 351 Vector<std::string> FilesToAdd; 352 Set<uint32_t> NewFeatures, NewCov; 353 CrashResistantMerge(Args, {}, MergeCandidates, &FilesToAdd, Features, 354 &NewFeatures, Cov, &NewCov, Job->CFPath, false); 355+ if (Fuzzer::isGracefulExitRequested()) 356+ return 0; 357 for (auto &Path : FilesToAdd) { 358 auto U = FileToVector(Path); 359 auto NewPath = DirPlusFile(MainCorpusDir, Hash(U)); 360@@ -226,7 +230,7 @@ struct GlobalEnv { 361 if (TPC.PcIsFuncEntry(TE)) 362 PrintPC(" NEW_FUNC: %p %F %L\n", "", 363 TPC.GetNextInstructionPc(TE->PC)); 364- 365+ return 0; 366 } 367 368 369@@ -280,7 +284,7 @@ void WorkerThread(JobQueue *FuzzQ, JobQueue *MergeQ) { 370 } 371 372 // This is just a skeleton of an experimental -fork=1 feature. 373-void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, 374+int FuzzWithFork(Random &Rand, const FuzzingOptions &Options, 375 const Vector<std::string> &Args, 376 const Vector<std::string> &CorpusDirs, int NumJobs) { 377 Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs); 378@@ -294,8 +298,12 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, 379 Env.DataFlowBinary = Options.CollectDataFlow; 380 381 Vector<SizedFile> SeedFiles; 382- for (auto &Dir : CorpusDirs) 383- GetSizedFilesFromDir(Dir, &SeedFiles); 384+ int Res; 385+ for (auto &Dir : CorpusDirs) { 386+ Res = GetSizedFilesFromDir(Dir, &SeedFiles); 387+ if (Res != 0) 388+ return Res; 389+ } 390 std::sort(SeedFiles.begin(), SeedFiles.end()); 391 Env.TempDir = TempPath("FuzzWithFork", ".dir"); 392 Env.DFTDir = DirPlusFile(Env.TempDir, "DFT"); 393@@ -310,9 +318,14 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, 394 Env.MainCorpusDir = CorpusDirs[0]; 395 396 auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); 397- CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features, 398+ Res = CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features, 399 {}, &Env.Cov, 400 CFPath, false); 401+ if (Res != 0) 402+ return Res; 403+ if (Fuzzer::isGracefulExitRequested()) 404+ return 0; 405+ 406 RemoveFile(CFPath); 407 Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs, 408 Env.Files.size(), Env.TempDir.c_str()); 409@@ -345,9 +358,14 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, 410 StopJobs(); 411 break; 412 } 413- Fuzzer::MaybeExitGracefully(); 414+ if (Fuzzer::MaybeExitGracefully()) 415+ return 0; 416 417- Env.RunOneMergeJob(Job.get()); 418+ Res = Env.RunOneMergeJob(Job.get()); 419+ if (Res != 0) 420+ return Res; 421+ if (Fuzzer::isGracefulExitRequested()) 422+ return 0; 423 424 // Continue if our crash is one of the ignorred ones. 425 if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode) 426@@ -403,7 +421,7 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, 427 // Use the exit code from the last child process. 428 Printf("INFO: exiting: %d time: %zds\n", ExitCode, 429 Env.secondsSinceProcessStartUp()); 430- exit(ExitCode); 431+ return ExitCode; 432 } 433 434 } // namespace fuzzer 435diff --git a/tools/fuzzing/libfuzzer/FuzzerFork.h b/tools/fuzzing/libfuzzer/FuzzerFork.h 436index b29a43e13fbc..1352171ad49d 100644 437--- a/tools/fuzzing/libfuzzer/FuzzerFork.h 438+++ b/tools/fuzzing/libfuzzer/FuzzerFork.h 439@@ -16,7 +16,7 @@ 440 #include <string> 441 442 namespace fuzzer { 443-void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, 444+int FuzzWithFork(Random &Rand, const FuzzingOptions &Options, 445 const Vector<std::string> &Args, 446 const Vector<std::string> &CorpusDirs, int NumJobs); 447 } // namespace fuzzer 448diff --git a/tools/fuzzing/libfuzzer/FuzzerIO.cpp b/tools/fuzzing/libfuzzer/FuzzerIO.cpp 449index 0053ef39f2b9..6be2be67c691 100644 450--- a/tools/fuzzing/libfuzzer/FuzzerIO.cpp 451+++ b/tools/fuzzing/libfuzzer/FuzzerIO.cpp 452@@ -82,7 +82,9 @@ void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, 453 long *Epoch, size_t MaxSize, bool ExitOnError) { 454 long E = Epoch ? *Epoch : 0; 455 Vector<std::string> Files; 456- ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true); 457+ int Res = ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true); 458+ if (ExitOnError && Res != 0) 459+ exit(Res); 460 size_t NumLoaded = 0; 461 for (size_t i = 0; i < Files.size(); i++) { 462 auto &X = Files[i]; 463@@ -97,12 +99,15 @@ void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, 464 } 465 466 467-void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V) { 468+int GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V) { 469 Vector<std::string> Files; 470- ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/true); 471+ int Res = ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/true); 472+ if (Res != 0) 473+ return Res; 474 for (auto &File : Files) 475 if (size_t Size = FileSize(File)) 476 V->push_back({File, Size}); 477+ return 0; 478 } 479 480 std::string DirPlusFile(const std::string &DirPath, 481diff --git a/tools/fuzzing/libfuzzer/FuzzerIO.h b/tools/fuzzing/libfuzzer/FuzzerIO.h 482index 6e4368b971fa..6c90ba637322 100644 483--- a/tools/fuzzing/libfuzzer/FuzzerIO.h 484+++ b/tools/fuzzing/libfuzzer/FuzzerIO.h 485@@ -60,7 +60,7 @@ void RawPrint(const char *Str); 486 bool IsFile(const std::string &Path); 487 size_t FileSize(const std::string &Path); 488 489-void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, 490+int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, 491 Vector<std::string> *V, bool TopDir); 492 493 void RmDirRecursive(const std::string &Dir); 494@@ -79,7 +79,7 @@ struct SizedFile { 495 bool operator<(const SizedFile &B) const { return Size < B.Size; } 496 }; 497 498-void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V); 499+int GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V); 500 501 char GetSeparator(); 502 // Similar to the basename utility: returns the file name w/o the dir prefix. 503diff --git a/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp b/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp 504index 4b453d286c80..1a50295c010f 100644 505--- a/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp 506+++ b/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp 507@@ -53,16 +53,16 @@ std::string Basename(const std::string &Path) { 508 return Path.substr(Pos + 1); 509 } 510 511-void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, 512+int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, 513 Vector<std::string> *V, bool TopDir) { 514 auto E = GetEpoch(Dir); 515 if (Epoch) 516- if (E && *Epoch >= E) return; 517+ if (E && *Epoch >= E) return 0; 518 519 DIR *D = opendir(Dir.c_str()); 520 if (!D) { 521 Printf("%s: %s; exiting\n", strerror(errno), Dir.c_str()); 522- exit(1); 523+ return 1; 524 } 525 while (auto E = readdir(D)) { 526 std::string Path = DirPlusFile(Dir, E->d_name); 527@@ -71,12 +71,16 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, 528 V->push_back(Path); 529 else if ((E->d_type == DT_DIR || 530 (E->d_type == DT_UNKNOWN && IsDirectory(Path))) && 531- *E->d_name != '.') 532- ListFilesInDirRecursive(Path, Epoch, V, false); 533+ *E->d_name != '.') { 534+ int Res = ListFilesInDirRecursive(Path, Epoch, V, false); 535+ if (Res != 0) 536+ return Res; 537+ } 538 } 539 closedir(D); 540 if (Epoch && TopDir) 541 *Epoch = E; 542+ return 0; 543 } 544 545 546diff --git a/tools/fuzzing/libfuzzer/FuzzerIOWindows.cpp b/tools/fuzzing/libfuzzer/FuzzerIOWindows.cpp 547index 651283a551cf..0e977bd02557 100644 548--- a/tools/fuzzing/libfuzzer/FuzzerIOWindows.cpp 549+++ b/tools/fuzzing/libfuzzer/FuzzerIOWindows.cpp 550@@ -98,11 +98,12 @@ size_t FileSize(const std::string &Path) { 551 return size.QuadPart; 552 } 553 554-void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, 555+int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, 556 Vector<std::string> *V, bool TopDir) { 557+ int Res; 558 auto E = GetEpoch(Dir); 559 if (Epoch) 560- if (E && *Epoch >= E) return; 561+ if (E && *Epoch >= E) return 0; 562 563 std::string Path(Dir); 564 assert(!Path.empty()); 565@@ -116,9 +117,9 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, 566 if (FindHandle == INVALID_HANDLE_VALUE) 567 { 568 if (GetLastError() == ERROR_FILE_NOT_FOUND) 569- return; 570+ return 0; 571 Printf("No such file or directory: %s; exiting\n", Dir.c_str()); 572- exit(1); 573+ return 1; 574 } 575 576 do { 577@@ -131,7 +132,9 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, 578 FindInfo.cFileName[1] == '.')) 579 continue; 580 581- ListFilesInDirRecursive(FileName, Epoch, V, false); 582+ int Res = ListFilesInDirRecursive(FileName, Epoch, V, false); 583+ if (Res != 0) 584+ return Res; 585 } 586 else if (IsFile(FileName, FindInfo.dwFileAttributes)) 587 V->push_back(FileName); 588@@ -145,6 +148,7 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, 589 590 if (Epoch && TopDir) 591 *Epoch = E; 592+ return 0; 593 } 594 595 596diff --git a/tools/fuzzing/libfuzzer/FuzzerInternal.h b/tools/fuzzing/libfuzzer/FuzzerInternal.h 597index 1f7d671ed848..cc2650b58ef1 100644 598--- a/tools/fuzzing/libfuzzer/FuzzerInternal.h 599+++ b/tools/fuzzing/libfuzzer/FuzzerInternal.h 600@@ -35,8 +35,8 @@ public: 601 Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, 602 FuzzingOptions Options); 603 ~Fuzzer(); 604- void Loop(Vector<SizedFile> &CorporaFiles); 605- void ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles); 606+ int Loop(Vector<SizedFile> &CorporaFiles); 607+ int ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles); 608 void MinimizeCrashLoop(const Unit &U); 609 void RereadOutputCorpus(size_t MaxSize); 610 611@@ -65,13 +65,16 @@ public: 612 static void StaticFileSizeExceedCallback(); 613 static void StaticGracefulExitCallback(); 614 615+ static void GracefullyExit(); 616+ static bool isGracefulExitRequested(); 617+ 618 int ExecuteCallback(const uint8_t *Data, size_t Size); 619 bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false, 620 InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr); 621 622 // Merge Corpora[1:] into Corpora[0]. 623 void Merge(const Vector<std::string> &Corpora); 624- void CrashResistantMergeInternalStep(const std::string &ControlFilePath); 625+ int CrashResistantMergeInternalStep(const std::string &ControlFilePath); 626 MutationDispatcher &GetMD() { return MD; } 627 void PrintFinalStats(); 628 void SetMaxInputLen(size_t MaxInputLen); 629@@ -84,7 +87,7 @@ public: 630 bool DuringInitialCorpusExecution); 631 632 void HandleMalloc(size_t Size); 633- static void MaybeExitGracefully(); 634+ static bool MaybeExitGracefully(); 635 std::string WriteToOutputCorpus(const Unit &U); 636 637 private: 638@@ -93,7 +96,7 @@ private: 639 void ExitCallback(); 640 void CrashOnOverwrittenData(); 641 void InterruptCallback(); 642- void MutateAndTestOne(); 643+ bool MutateAndTestOne(); 644 void PurgeAllocator(); 645 void ReportNewCoverage(InputInfo *II, const Unit &U); 646 void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size); 647diff --git a/tools/fuzzing/libfuzzer/FuzzerLoop.cpp b/tools/fuzzing/libfuzzer/FuzzerLoop.cpp 648index 4c4e8c271b1f..e7dfc187dbfe 100644 649--- a/tools/fuzzing/libfuzzer/FuzzerLoop.cpp 650+++ b/tools/fuzzing/libfuzzer/FuzzerLoop.cpp 651@@ -254,12 +254,20 @@ void Fuzzer::ExitCallback() { 652 _Exit(Options.ErrorExitCode); 653 } 654 655-void Fuzzer::MaybeExitGracefully() { 656- if (!F->GracefulExitRequested) return; 657+bool Fuzzer::MaybeExitGracefully() { 658+ if (!F->GracefulExitRequested) return false; 659 Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid()); 660 RmDirRecursive(TempPath("FuzzWithFork", ".dir")); 661 F->PrintFinalStats(); 662- _Exit(0); 663+ return true; 664+} 665+ 666+void Fuzzer::GracefullyExit() { 667+ F->GracefulExitRequested = true; 668+} 669+ 670+bool Fuzzer::isGracefulExitRequested() { 671+ return F->GracefulExitRequested; 672 } 673 674 void Fuzzer::InterruptCallback() { 675@@ -663,7 +671,7 @@ void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, 676 } 677 } 678 679-void Fuzzer::MutateAndTestOne() { 680+bool Fuzzer::MutateAndTestOne() { 681 MD.StartMutationSequence(); 682 683 auto &II = Corpus.ChooseUnitToMutate(MD.GetRand()); 684@@ -685,7 +693,7 @@ void Fuzzer::MutateAndTestOne() { 685 for (int i = 0; i < Options.MutateDepth; i++) { 686 if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) 687 break; 688- MaybeExitGracefully(); 689+ if (MaybeExitGracefully()) return true; 690 size_t NewSize = 0; 691 if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() && 692 Size <= CurrentMaxMutationLen) 693@@ -719,6 +727,7 @@ void Fuzzer::MutateAndTestOne() { 694 } 695 696 II.NeedsEnergyUpdate = true; 697+ return false; 698 } 699 700 void Fuzzer::PurgeAllocator() { 701@@ -736,7 +745,7 @@ void Fuzzer::PurgeAllocator() { 702 LastAllocatorPurgeAttemptTime = system_clock::now(); 703 } 704 705-void Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) { 706+int Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) { 707 const size_t kMaxSaneLen = 1 << 20; 708 const size_t kMinDefaultLen = 4096; 709 size_t MaxSize = 0; 710@@ -795,16 +804,23 @@ void Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) { 711 if (Corpus.empty() && Options.MaxNumberOfRuns) { 712 Printf("ERROR: no interesting inputs were found. " 713 "Is the code instrumented for coverage? Exiting.\n"); 714- exit(1); 715+ return 1; 716 } 717+ return 0; 718 } 719 720-void Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) { 721+int Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) { 722 auto FocusFunctionOrAuto = Options.FocusFunction; 723- DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles, 724+ int Res = DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles, 725 MD.GetRand()); 726- TPC.SetFocusFunction(FocusFunctionOrAuto); 727- ReadAndExecuteSeedCorpora(CorporaFiles); 728+ if (Res != 0) 729+ return Res; 730+ Res = TPC.SetFocusFunction(FocusFunctionOrAuto); 731+ if (Res != 0) 732+ return Res; 733+ Res = ReadAndExecuteSeedCorpora(CorporaFiles); 734+ if (Res != 0) 735+ return Res; 736 DFT.Clear(); // No need for DFT any more. 737 TPC.SetPrintNewPCs(Options.PrintNewCovPcs); 738 TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs); 739@@ -842,13 +858,15 @@ void Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) { 740 } 741 742 // Perform several mutations and runs. 743- MutateAndTestOne(); 744+ if (MutateAndTestOne()) 745+ return 0; 746 747 PurgeAllocator(); 748 } 749 750 PrintStats("DONE ", "\n"); 751 MD.PrintRecommendedDictionary(); 752+ return 0; 753 } 754 755 void Fuzzer::MinimizeCrashLoop(const Unit &U) { 756diff --git a/tools/fuzzing/libfuzzer/FuzzerMerge.cpp b/tools/fuzzing/libfuzzer/FuzzerMerge.cpp 757index 919eea848580..0a185c7325bb 100644 758--- a/tools/fuzzing/libfuzzer/FuzzerMerge.cpp 759+++ b/tools/fuzzing/libfuzzer/FuzzerMerge.cpp 760@@ -28,11 +28,12 @@ bool Merger::Parse(const std::string &Str, bool ParseCoverage) { 761 return Parse(SS, ParseCoverage); 762 } 763 764-void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) { 765+int Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) { 766 if (!Parse(IS, ParseCoverage)) { 767 Printf("MERGE: failed to parse the control file (unexpected error)\n"); 768- exit(1); 769+ return 1; 770 } 771+ return 0; 772 } 773 774 // The control file example: 775@@ -194,11 +195,13 @@ Set<uint32_t> Merger::AllFeatures() const { 776 } 777 778 // Inner process. May crash if the target crashes. 779-void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { 780+int Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { 781 Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str()); 782 Merger M; 783 std::ifstream IF(CFPath); 784- M.ParseOrExit(IF, false); 785+ int Res = M.ParseOrExit(IF, false); 786+ if (Res != 0) 787+ return Res; 788 IF.close(); 789 if (!M.LastFailure.empty()) 790 Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n", 791@@ -216,7 +219,8 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { 792 }; 793 Set<const TracePC::PCTableEntry *> AllPCs; 794 for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) { 795- Fuzzer::MaybeExitGracefully(); 796+ if (Fuzzer::MaybeExitGracefully()) 797+ return 0; 798 auto U = FileToVector(M.Files[i].Name); 799 if (U.size() > MaxInputLen) { 800 U.resize(MaxInputLen); 801@@ -261,12 +265,14 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { 802 OF.flush(); 803 } 804 PrintStatsWrapper("DONE "); 805+ return 0; 806 } 807 808-static size_t WriteNewControlFile(const std::string &CFPath, 809+static int WriteNewControlFile(const std::string &CFPath, 810 const Vector<SizedFile> &OldCorpus, 811 const Vector<SizedFile> &NewCorpus, 812- const Vector<MergeFileInfo> &KnownFiles) { 813+ const Vector<MergeFileInfo> &KnownFiles, 814+ size_t &NumFiles) { 815 std::unordered_set<std::string> FilesToSkip; 816 for (auto &SF: KnownFiles) 817 FilesToSkip.insert(SF.Name); 818@@ -292,14 +298,15 @@ static size_t WriteNewControlFile(const std::string &CFPath, 819 if (!ControlFile) { 820 Printf("MERGE-OUTER: failed to write to the control file: %s\n", 821 CFPath.c_str()); 822- exit(1); 823+ return 1; 824 } 825 826- return FilesToUse.size(); 827+ NumFiles = FilesToUse.size(); 828+ return 0; 829 } 830 831 // Outer process. Does not call the target code and thus should not fail. 832-void CrashResistantMerge(const Vector<std::string> &Args, 833+int CrashResistantMerge(const Vector<std::string> &Args, 834 const Vector<SizedFile> &OldCorpus, 835 const Vector<SizedFile> &NewCorpus, 836 Vector<std::string> *NewFiles, 837@@ -309,8 +316,9 @@ void CrashResistantMerge(const Vector<std::string> &Args, 838 Set<uint32_t> *NewCov, 839 const std::string &CFPath, 840 bool V /*Verbose*/) { 841- if (NewCorpus.empty() && OldCorpus.empty()) return; // Nothing to merge. 842+ if (NewCorpus.empty() && OldCorpus.empty()) return 0; // Nothing to merge. 843 size_t NumAttempts = 0; 844+ int Res; 845 Vector<MergeFileInfo> KnownFiles; 846 if (FileSize(CFPath)) { 847 VPrintf(V, "MERGE-OUTER: non-empty control file provided: '%s'\n", 848@@ -331,7 +339,8 @@ void CrashResistantMerge(const Vector<std::string> &Args, 849 VPrintf( 850 V, 851 "MERGE-OUTER: nothing to do, merge has been completed before\n"); 852- exit(0); 853+ Fuzzer::GracefullyExit(); 854+ return 0; 855 } 856 857 // Number of input files likely changed, start merge from scratch, but 858@@ -356,7 +365,9 @@ void CrashResistantMerge(const Vector<std::string> &Args, 859 "%zd files, %zd in the initial corpus, %zd processed earlier\n", 860 OldCorpus.size() + NewCorpus.size(), OldCorpus.size(), 861 KnownFiles.size()); 862- NumAttempts = WriteNewControlFile(CFPath, OldCorpus, NewCorpus, KnownFiles); 863+ Res = WriteNewControlFile(CFPath, OldCorpus, NewCorpus, KnownFiles, NumAttempts); 864+ if (Res != 0) 865+ return Res; 866 } 867 868 // Execute the inner process until it passes. 869@@ -366,7 +377,8 @@ void CrashResistantMerge(const Vector<std::string> &Args, 870 BaseCmd.removeFlag("fork"); 871 BaseCmd.removeFlag("collect_data_flow"); 872 for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) { 873- Fuzzer::MaybeExitGracefully(); 874+ if (Fuzzer::MaybeExitGracefully()) 875+ return 0; 876 VPrintf(V, "MERGE-OUTER: attempt %zd\n", Attempt); 877 Command Cmd(BaseCmd); 878 Cmd.addFlag("merge_control_file", CFPath); 879@@ -388,7 +400,9 @@ void CrashResistantMerge(const Vector<std::string> &Args, 880 VPrintf(V, "MERGE-OUTER: the control file has %zd bytes\n", 881 (size_t)IF.tellg()); 882 IF.seekg(0, IF.beg); 883- M.ParseOrExit(IF, true); 884+ Res = M.ParseOrExit(IF, true); 885+ if (Res != 0) 886+ return Res; 887 IF.close(); 888 VPrintf(V, 889 "MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n", 890@@ -399,6 +413,7 @@ void CrashResistantMerge(const Vector<std::string> &Args, 891 VPrintf(V, "MERGE-OUTER: %zd new files with %zd new features added; " 892 "%zd new coverage edges\n", 893 NewFiles->size(), NewFeatures->size(), NewCov->size()); 894+ return 0; 895 } 896 897 } // namespace fuzzer 898diff --git a/tools/fuzzing/libfuzzer/FuzzerMerge.h b/tools/fuzzing/libfuzzer/FuzzerMerge.h 899index e0c6bc539bdb..6dc1c4c45abf 100644 900--- a/tools/fuzzing/libfuzzer/FuzzerMerge.h 901+++ b/tools/fuzzing/libfuzzer/FuzzerMerge.h 902@@ -63,7 +63,7 @@ struct Merger { 903 904 bool Parse(std::istream &IS, bool ParseCoverage); 905 bool Parse(const std::string &Str, bool ParseCoverage); 906- void ParseOrExit(std::istream &IS, bool ParseCoverage); 907+ int ParseOrExit(std::istream &IS, bool ParseCoverage); 908 size_t Merge(const Set<uint32_t> &InitialFeatures, Set<uint32_t> *NewFeatures, 909 const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov, 910 Vector<std::string> *NewFiles); 911@@ -71,7 +71,7 @@ struct Merger { 912 Set<uint32_t> AllFeatures() const; 913 }; 914 915-void CrashResistantMerge(const Vector<std::string> &Args, 916+int CrashResistantMerge(const Vector<std::string> &Args, 917 const Vector<SizedFile> &OldCorpus, 918 const Vector<SizedFile> &NewCorpus, 919 Vector<std::string> *NewFiles, 920diff --git a/tools/fuzzing/libfuzzer/FuzzerTracePC.cpp b/tools/fuzzing/libfuzzer/FuzzerTracePC.cpp 921index b2ca7693e540..fbceda39bc22 100644 922--- a/tools/fuzzing/libfuzzer/FuzzerTracePC.cpp 923+++ b/tools/fuzzing/libfuzzer/FuzzerTracePC.cpp 924@@ -238,13 +238,13 @@ void TracePC::IterateCoveredFunctions(CallBack CB) { 925 } 926 } 927 928-void TracePC::SetFocusFunction(const std::string &FuncName) { 929+int TracePC::SetFocusFunction(const std::string &FuncName) { 930 // This function should be called once. 931 assert(!FocusFunctionCounterPtr); 932 // "auto" is not a valid function name. If this function is called with "auto" 933 // that means the auto focus functionality failed. 934 if (FuncName.empty() || FuncName == "auto") 935- return; 936+ return 0; 937 for (size_t M = 0; M < NumModules; M++) { 938 auto &PCTE = ModulePCTable[M]; 939 size_t N = PCTE.Stop - PCTE.Start; 940@@ -256,13 +256,13 @@ void TracePC::SetFocusFunction(const std::string &FuncName) { 941 if (FuncName != Name) continue; 942 Printf("INFO: Focus function is set to '%s'\n", Name.c_str()); 943 FocusFunctionCounterPtr = Modules[M].Start() + I; 944- return; 945+ return 0; 946 } 947 } 948 949 Printf("ERROR: Failed to set focus function. Make sure the function name is " 950 "valid (%s) and symbolization is enabled.\n", FuncName.c_str()); 951- exit(1); 952+ return 1; 953 } 954 955 bool TracePC::ObservedFocusFunction() { 956diff --git a/tools/fuzzing/libfuzzer/FuzzerTracePC.h b/tools/fuzzing/libfuzzer/FuzzerTracePC.h 957index 501f3b544971..b46ebb909dbf 100644 958--- a/tools/fuzzing/libfuzzer/FuzzerTracePC.h 959+++ b/tools/fuzzing/libfuzzer/FuzzerTracePC.h 960@@ -116,7 +116,7 @@ class TracePC { 961 CB(PC); 962 } 963 964- void SetFocusFunction(const std::string &FuncName); 965+ int SetFocusFunction(const std::string &FuncName); 966 bool ObservedFocusFunction(); 967 968 struct PCTableEntry { 969