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