summaryrefslogtreecommitdiffstats
path: root/tools/fuzzing/libfuzzer/patches
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /tools/fuzzing/libfuzzer/patches
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/fuzzing/libfuzzer/patches')
-rw-r--r--tools/fuzzing/libfuzzer/patches/10-ef-runtime.patch31
-rw-r--r--tools/fuzzing/libfuzzer/patches/11-callback-rv.patch132
-rw-r--r--tools/fuzzing/libfuzzer/patches/12-custom-mutator-fail.patch53
-rw-r--r--tools/fuzzing/libfuzzer/patches/13-unused-write.patch88
-rw-r--r--tools/fuzzing/libfuzzer/patches/14-explicit-allocator.patch30
-rw-r--r--tools/fuzzing/libfuzzer/patches/15-return-to-exit.patch968
6 files changed, 1302 insertions, 0 deletions
diff --git a/tools/fuzzing/libfuzzer/patches/10-ef-runtime.patch b/tools/fuzzing/libfuzzer/patches/10-ef-runtime.patch
new file mode 100644
index 0000000000..34562e27a5
--- /dev/null
+++ b/tools/fuzzing/libfuzzer/patches/10-ef-runtime.patch
@@ -0,0 +1,31 @@
+# HG changeset patch
+# User Christian Holler <choller@mozilla.com>
+# Date 1596126054 -7200
+# Thu Jul 30 18:20:54 2020 +0200
+# Node ID 8a2a26b33d516c43c366b2f24d731d27d9843349
+# Parent 997c4109edd112695097fd8c55cbacd976cab24a
+[libFuzzer] Allow external functions to be defined at runtime
+
+diff --git a/tools/fuzzing/libfuzzer/FuzzerDriver.cpp b/tools/fuzzing/libfuzzer/FuzzerDriver.cpp
+--- a/tools/fuzzing/libfuzzer/FuzzerDriver.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerDriver.cpp
+@@ -608,17 +608,18 @@ static Vector<SizedFile> ReadCorpora(con
+ SizedFiles.push_back({File, Size});
+ return SizedFiles;
+ }
+
+ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
+ using namespace fuzzer;
+ assert(argc && argv && "Argument pointers cannot be nullptr");
+ std::string Argv0((*argv)[0]);
+- EF = new ExternalFunctions();
++ if (!EF)
++ EF = new ExternalFunctions();
+ if (EF->LLVMFuzzerInitialize)
+ EF->LLVMFuzzerInitialize(argc, argv);
+ if (EF->__msan_scoped_disable_interceptor_checks)
+ EF->__msan_scoped_disable_interceptor_checks();
+ const Vector<std::string> Args(*argv, *argv + *argc);
+ assert(!Args.empty());
+ ProgName = new std::string(Args[0]);
+ if (Argv0 != *ProgName) {
diff --git a/tools/fuzzing/libfuzzer/patches/11-callback-rv.patch b/tools/fuzzing/libfuzzer/patches/11-callback-rv.patch
new file mode 100644
index 0000000000..650444442c
--- /dev/null
+++ b/tools/fuzzing/libfuzzer/patches/11-callback-rv.patch
@@ -0,0 +1,132 @@
+# HG changeset patch
+# User Christian Holler <choller@mozilla.com>
+# Date 1596126448 -7200
+# Thu Jul 30 18:27:28 2020 +0200
+# Node ID ea198a0331a6db043cb5978512226977514104db
+# Parent 8a2a26b33d516c43c366b2f24d731d27d9843349
+[libFuzzer] Change libFuzzer callback contract to allow positive return values
+
+diff --git a/tools/fuzzing/libfuzzer/FuzzerInternal.h b/tools/fuzzing/libfuzzer/FuzzerInternal.h
+--- a/tools/fuzzing/libfuzzer/FuzzerInternal.h
++++ b/tools/fuzzing/libfuzzer/FuzzerInternal.h
+@@ -60,17 +60,17 @@ public:
+
+ static void StaticAlarmCallback();
+ static void StaticCrashSignalCallback();
+ static void StaticExitCallback();
+ static void StaticInterruptCallback();
+ static void StaticFileSizeExceedCallback();
+ static void StaticGracefulExitCallback();
+
+- void ExecuteCallback(const uint8_t *Data, size_t Size);
++ int ExecuteCallback(const uint8_t *Data, size_t Size);
+ bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false,
+ InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr);
+
+ // Merge Corpora[1:] into Corpora[0].
+ void Merge(const Vector<std::string> &Corpora);
+ void CrashResistantMergeInternalStep(const std::string &ControlFilePath);
+ MutationDispatcher &GetMD() { return MD; }
+ void PrintFinalStats();
+diff --git a/tools/fuzzing/libfuzzer/FuzzerLoop.cpp b/tools/fuzzing/libfuzzer/FuzzerLoop.cpp
+--- a/tools/fuzzing/libfuzzer/FuzzerLoop.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerLoop.cpp
+@@ -463,17 +463,19 @@ static void RenameFeatureSetFile(const s
+ DirPlusFile(FeaturesDir, NewFile));
+ }
+
+ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
+ InputInfo *II, bool *FoundUniqFeatures) {
+ if (!Size)
+ return false;
+
+- ExecuteCallback(Data, Size);
++ if (ExecuteCallback(Data, Size) > 0) {
++ return false;
++ }
+
+ UniqFeatureSetTmp.clear();
+ size_t FoundUniqFeaturesOfII = 0;
+ size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();
+ TPC.CollectFeatures([&](size_t Feature) {
+ if (Corpus.AddFeature(Feature, Size, Options.Shrink))
+ UniqFeatureSetTmp.push_back(Feature);
+ if (Options.Entropic)
+@@ -530,48 +532,49 @@ static bool LooseMemeq(const uint8_t *A,
+ const size_t Limit = 64;
+ if (Size <= 64)
+ return !memcmp(A, B, Size);
+ // Compare first and last Limit/2 bytes.
+ return !memcmp(A, B, Limit / 2) &&
+ !memcmp(A + Size - Limit / 2, B + Size - Limit / 2, Limit / 2);
+ }
+
+-void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
++int Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
+ TPC.RecordInitialStack();
+ TotalNumberOfRuns++;
+ assert(InFuzzingThread());
+ // We copy the contents of Unit into a separate heap buffer
+ // so that we reliably find buffer overflows in it.
+ uint8_t *DataCopy = new uint8_t[Size];
+ memcpy(DataCopy, Data, Size);
+ if (EF->__msan_unpoison)
+ EF->__msan_unpoison(DataCopy, Size);
+ if (EF->__msan_unpoison_param)
+ EF->__msan_unpoison_param(2);
+ if (CurrentUnitData && CurrentUnitData != Data)
+ memcpy(CurrentUnitData, Data, Size);
+ CurrentUnitSize = Size;
++ int Res = 0;
+ {
+ ScopedEnableMsanInterceptorChecks S;
+ AllocTracer.Start(Options.TraceMalloc);
+ UnitStartTime = system_clock::now();
+ TPC.ResetMaps();
+ RunningUserCallback = true;
+- int Res = CB(DataCopy, Size);
++ Res = CB(DataCopy, Size);
+ RunningUserCallback = false;
+ UnitStopTime = system_clock::now();
+- (void)Res;
+- assert(Res == 0);
++ assert(Res >= 0);
+ HasMoreMallocsThanFrees = AllocTracer.Stop();
+ }
+ if (!LooseMemeq(DataCopy, Data, Size))
+ CrashOnOverwrittenData();
+ CurrentUnitSize = 0;
+ delete[] DataCopy;
++ return Res;
+ }
+
+ std::string Fuzzer::WriteToOutputCorpus(const Unit &U) {
+ if (Options.OnlyASCII)
+ assert(IsASCII(U));
+ if (Options.OutputCorpus.empty())
+ return "";
+ std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U));
+diff --git a/tools/fuzzing/libfuzzer/FuzzerMerge.cpp b/tools/fuzzing/libfuzzer/FuzzerMerge.cpp
+--- a/tools/fuzzing/libfuzzer/FuzzerMerge.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerMerge.cpp
+@@ -223,17 +223,19 @@ void Fuzzer::CrashResistantMergeInternal
+ U.shrink_to_fit();
+ }
+
+ // Write the pre-run marker.
+ OF << "STARTED " << i << " " << U.size() << "\n";
+ OF.flush(); // Flush is important since Command::Execute may crash.
+ // Run.
+ TPC.ResetMaps();
+- ExecuteCallback(U.data(), U.size());
++ if (ExecuteCallback(U.data(), U.size()) > 0) {
++ continue;
++ }
+ // Collect coverage. We are iterating over the files in this order:
+ // * First, files in the initial corpus ordered by size, smallest first.
+ // * Then, all other files, smallest first.
+ // So it makes no sense to record all features for all files, instead we
+ // only record features that were not seen before.
+ Set<size_t> UniqFeatures;
+ TPC.CollectFeatures([&](size_t Feature) {
+ if (AllFeatures.insert(Feature).second)
diff --git a/tools/fuzzing/libfuzzer/patches/12-custom-mutator-fail.patch b/tools/fuzzing/libfuzzer/patches/12-custom-mutator-fail.patch
new file mode 100644
index 0000000000..2457c1f046
--- /dev/null
+++ b/tools/fuzzing/libfuzzer/patches/12-custom-mutator-fail.patch
@@ -0,0 +1,53 @@
+# HG changeset patch
+# User Christian Holler <choller@mozilla.com>
+# Date 1596126768 -7200
+# Thu Jul 30 18:32:48 2020 +0200
+# Node ID 64e7d096fa77a62b71a306b2c5383b8f75ac4945
+# Parent ea198a0331a6db043cb5978512226977514104db
+[libFuzzer] Allow custom mutators to fail
+
+diff --git a/tools/fuzzing/libfuzzer/FuzzerLoop.cpp b/tools/fuzzing/libfuzzer/FuzzerLoop.cpp
+--- a/tools/fuzzing/libfuzzer/FuzzerLoop.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerLoop.cpp
+@@ -690,16 +690,20 @@ void Fuzzer::MutateAndTestOne() {
+ if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() &&
+ Size <= CurrentMaxMutationLen)
+ NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size,
+ II.DataFlowTraceForFocusFunction);
+
+ // If MutateWithMask either failed or wasn't called, call default Mutate.
+ if (!NewSize)
+ NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen);
++
++ if (!NewSize)
++ continue;
++
+ assert(NewSize > 0 && "Mutator returned empty unit");
+ assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit");
+ Size = NewSize;
+ II.NumExecutedMutations++;
+ Corpus.IncrementNumExecutedMutations();
+
+ bool FoundUniqFeatures = false;
+ bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II,
+@@ -850,17 +854,19 @@ void Fuzzer::Loop(Vector<SizedFile> &Cor
+ void Fuzzer::MinimizeCrashLoop(const Unit &U) {
+ if (U.size() <= 1)
+ return;
+ while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) {
+ MD.StartMutationSequence();
+ memcpy(CurrentUnitData, U.data(), U.size());
+ for (int i = 0; i < Options.MutateDepth; i++) {
+ size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen);
+- assert(NewSize > 0 && NewSize <= MaxMutationLen);
++ assert(NewSize <= MaxMutationLen);
++ if (!NewSize)
++ continue;
+ ExecuteCallback(CurrentUnitData, NewSize);
+ PrintPulseAndReportSlowInput(CurrentUnitData, NewSize);
+ TryDetectingAMemoryLeak(CurrentUnitData, NewSize,
+ /*DuringInitialCorpusExecution*/ false);
+ }
+ }
+ }
+
diff --git a/tools/fuzzing/libfuzzer/patches/13-unused-write.patch b/tools/fuzzing/libfuzzer/patches/13-unused-write.patch
new file mode 100644
index 0000000000..f8ef7a5ac9
--- /dev/null
+++ b/tools/fuzzing/libfuzzer/patches/13-unused-write.patch
@@ -0,0 +1,88 @@
+# HG changeset patch
+# User Christian Holler <choller@mozilla.com>
+# Date 1596126946 -7200
+# Thu Jul 30 18:35:46 2020 +0200
+# Node ID 6c779ec81530b6784a714063af66085681ab7318
+# Parent 64e7d096fa77a62b71a306b2c5383b8f75ac4945
+[libFuzzer] Suppress warnings about unused return values
+
+diff --git a/tools/fuzzing/libfuzzer/FuzzerIO.cpp b/tools/fuzzing/libfuzzer/FuzzerIO.cpp
+--- a/tools/fuzzing/libfuzzer/FuzzerIO.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerIO.cpp
+@@ -3,16 +3,17 @@
+ // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+ // See https://llvm.org/LICENSE.txt for license information.
+ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ //
+ //===----------------------------------------------------------------------===//
+ // IO functions.
+ //===----------------------------------------------------------------------===//
+
++#include "mozilla/Unused.h"
+ #include "FuzzerDefs.h"
+ #include "FuzzerExtFunctions.h"
+ #include "FuzzerIO.h"
+ #include "FuzzerUtil.h"
+ #include <algorithm>
+ #include <cstdarg>
+ #include <fstream>
+ #include <iterator>
+@@ -68,17 +69,17 @@ void WriteToFile(const std::string &Data
+ WriteToFile(reinterpret_cast<const uint8_t *>(Data.c_str()), Data.size(),
+ Path);
+ }
+
+ void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path) {
+ // Use raw C interface because this function may be called from a sig handler.
+ FILE *Out = fopen(Path.c_str(), "wb");
+ if (!Out) return;
+- fwrite(Data, sizeof(Data[0]), Size, Out);
++ mozilla::Unused << fwrite(Data, sizeof(Data[0]), Size, Out);
+ fclose(Out);
+ }
+
+ void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V,
+ long *Epoch, size_t MaxSize, bool ExitOnError) {
+ long E = Epoch ? *Epoch : 0;
+ Vector<std::string> Files;
+ ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true);
+diff --git a/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp b/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp
+--- a/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp
+@@ -2,16 +2,17 @@
+ //
+ // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+ // See https://llvm.org/LICENSE.txt for license information.
+ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ //
+ //===----------------------------------------------------------------------===//
+ // IO functions implementation using Posix API.
+ //===----------------------------------------------------------------------===//
++#include "mozilla/Unused.h"
+ #include "FuzzerPlatform.h"
+ #if LIBFUZZER_POSIX || LIBFUZZER_FUCHSIA
+
+ #include "FuzzerExtFunctions.h"
+ #include "FuzzerIO.h"
+ #include <cstdarg>
+ #include <cstdio>
+ #include <dirent.h>
+@@ -150,17 +151,17 @@ bool IsInterestingCoverageFile(const std
+ if (FileName.find("/usr/include/") != std::string::npos)
+ return false;
+ if (FileName == "<null>")
+ return false;
+ return true;
+ }
+
+ void RawPrint(const char *Str) {
+- write(2, Str, strlen(Str));
++ mozilla::Unused << write(2, Str, strlen(Str));
+ }
+
+ void MkDir(const std::string &Path) {
+ mkdir(Path.c_str(), 0700);
+ }
+
+ void RmDir(const std::string &Path) {
+ rmdir(Path.c_str());
diff --git a/tools/fuzzing/libfuzzer/patches/14-explicit-allocator.patch b/tools/fuzzing/libfuzzer/patches/14-explicit-allocator.patch
new file mode 100644
index 0000000000..8e02273193
--- /dev/null
+++ b/tools/fuzzing/libfuzzer/patches/14-explicit-allocator.patch
@@ -0,0 +1,30 @@
+# HG changeset patch
+# User Christian Holler <choller@mozilla.com>
+# Date 1596126981 -7200
+# Thu Jul 30 18:36:21 2020 +0200
+# Node ID 069dfa3715b1d30905ff0ea1c0f66db88ce146f9
+# Parent 6c779ec81530b6784a714063af66085681ab7318
+[libFuzzer] Make fuzzer_allocator explicit
+
+diff --git a/tools/fuzzing/libfuzzer/FuzzerDefs.h b/tools/fuzzing/libfuzzer/FuzzerDefs.h
+--- a/tools/fuzzing/libfuzzer/FuzzerDefs.h
++++ b/tools/fuzzing/libfuzzer/FuzzerDefs.h
+@@ -41,17 +41,17 @@ extern ExternalFunctions *EF;
+ // We are using a custom allocator to give a different symbol name to STL
+ // containers in order to avoid ODR violations.
+ template<typename T>
+ class fuzzer_allocator: public std::allocator<T> {
+ public:
+ fuzzer_allocator() = default;
+
+ template<class U>
+- fuzzer_allocator(const fuzzer_allocator<U>&) {}
++ explicit fuzzer_allocator(const fuzzer_allocator<U>&) {}
+
+ template<class Other>
+ struct rebind { typedef fuzzer_allocator<Other> other; };
+ };
+
+ template<typename T>
+ using Vector = std::vector<T, fuzzer_allocator<T>>;
+
diff --git a/tools/fuzzing/libfuzzer/patches/15-return-to-exit.patch b/tools/fuzzing/libfuzzer/patches/15-return-to-exit.patch
new file mode 100644
index 0000000000..4adb9f59a9
--- /dev/null
+++ b/tools/fuzzing/libfuzzer/patches/15-return-to-exit.patch
@@ -0,0 +1,968 @@
+commit f80733b3b1b5e05e7dfd7a071f60050fe20108c3
+Author: Jesse Schwartzentruber <truber@mozilla.com>
+Date: Mon Mar 1 15:47:38 2021 -0500
+
+ [libfuzzer] In most cases, return instead of exit().
+
+diff --git a/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.cpp b/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.cpp
+index 0e9cdf7e66b1..06ea287a3cfe 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.cpp
+@@ -102,9 +102,11 @@ Vector<double> BlockCoverage::FunctionWeights(size_t NumFunctions) const {
+ return Res;
+ }
+
+-void DataFlowTrace::ReadCoverage(const std::string &DirPath) {
++int DataFlowTrace::ReadCoverage(const std::string &DirPath) {
+ Vector<SizedFile> Files;
+- GetSizedFilesFromDir(DirPath, &Files);
++ int Res = GetSizedFilesFromDir(DirPath, &Files);
++ if (Res != 0)
++ return Res;
+ for (auto &SF : Files) {
+ auto Name = Basename(SF.File);
+ if (Name == kFunctionsTxt) continue;
+@@ -112,6 +114,7 @@ void DataFlowTrace::ReadCoverage(const std::string &DirPath) {
+ std::ifstream IF(SF.File);
+ Coverage.AppendCoverage(IF);
+ }
++ return 0;
+ }
+
+ static void DFTStringAppendToVector(Vector<uint8_t> *DFT,
+@@ -157,12 +160,14 @@ static bool ParseDFTLine(const std::string &Line, size_t *FunctionNum,
+ return true;
+ }
+
+-bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction,
++int DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction,
+ Vector<SizedFile> &CorporaFiles, Random &Rand) {
+- if (DirPath.empty()) return false;
++ if (DirPath.empty()) return 0;
+ Printf("INFO: DataFlowTrace: reading from '%s'\n", DirPath.c_str());
+ Vector<SizedFile> Files;
+- GetSizedFilesFromDir(DirPath, &Files);
++ int Res = GetSizedFilesFromDir(DirPath, &Files);
++ if (Res != 0)
++ return Res;
+ std::string L;
+ size_t FocusFuncIdx = SIZE_MAX;
+ Vector<std::string> FunctionNames;
+@@ -181,14 +186,16 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction,
+ FocusFuncIdx = NumFunctions - 1;
+ }
+ if (!NumFunctions)
+- return false;
++ return 0;
+
+ if (*FocusFunction == "auto") {
+ // AUTOFOCUS works like this:
+ // * reads the coverage data from the DFT files.
+ // * assigns weights to functions based on coverage.
+ // * chooses a random function according to the weights.
+- ReadCoverage(DirPath);
++ Res = ReadCoverage(DirPath);
++ if (Res != 0)
++ return Res;
+ auto Weights = Coverage.FunctionWeights(NumFunctions);
+ Vector<double> Intervals(NumFunctions + 1);
+ std::iota(Intervals.begin(), Intervals.end(), 0);
+@@ -209,7 +216,7 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction,
+ }
+
+ if (!NumFunctions || FocusFuncIdx == SIZE_MAX || Files.size() <= 1)
+- return false;
++ return 0;
+
+ // Read traces.
+ size_t NumTraceFiles = 0;
+@@ -228,8 +235,10 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction,
+ FunctionNum == FocusFuncIdx) {
+ NumTracesWithFocusFunction++;
+
+- if (FunctionNum >= NumFunctions)
+- return ParseError("N is greater than the number of functions", L);
++ if (FunctionNum >= NumFunctions) {
++ ParseError("N is greater than the number of functions", L);
++ return 0;
++ }
+ Traces[Name] = DFTStringToVector(DFTString);
+ // Print just a few small traces.
+ if (NumTracesWithFocusFunction <= 3 && DFTString.size() <= 16)
+@@ -241,7 +250,7 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction,
+ Printf("INFO: DataFlowTrace: %zd trace files, %zd functions, "
+ "%zd traces with focus function\n",
+ NumTraceFiles, NumFunctions, NumTracesWithFocusFunction);
+- return NumTraceFiles > 0;
++ return 0;
+ }
+
+ int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath,
+diff --git a/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.h b/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.h
+index d6e3de30a4ef..767bad24f1d0 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.h
++++ b/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.h
+@@ -113,8 +113,8 @@ class BlockCoverage {
+
+ class DataFlowTrace {
+ public:
+- void ReadCoverage(const std::string &DirPath);
+- bool Init(const std::string &DirPath, std::string *FocusFunction,
++ int ReadCoverage(const std::string &DirPath);
++ int Init(const std::string &DirPath, std::string *FocusFunction,
+ Vector<SizedFile> &CorporaFiles, Random &Rand);
+ void Clear() { Traces.clear(); }
+ const Vector<uint8_t> *Get(const std::string &InputSha1) const {
+diff --git a/tools/fuzzing/libfuzzer/FuzzerDriver.cpp b/tools/fuzzing/libfuzzer/FuzzerDriver.cpp
+index cd720200848b..bedad16efa7b 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerDriver.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerDriver.cpp
+@@ -326,7 +326,7 @@ int CleanseCrashInput(const Vector<std::string> &Args,
+ if (Inputs->size() != 1 || !Flags.exact_artifact_path) {
+ Printf("ERROR: -cleanse_crash should be given one input file and"
+ " -exact_artifact_path\n");
+- exit(1);
++ return 1;
+ }
+ std::string InputFilePath = Inputs->at(0);
+ std::string OutputFilePath = Flags.exact_artifact_path;
+@@ -380,7 +380,7 @@ int MinimizeCrashInput(const Vector<std::string> &Args,
+ const FuzzingOptions &Options) {
+ if (Inputs->size() != 1) {
+ Printf("ERROR: -minimize_crash should be given one input file\n");
+- exit(1);
++ return 1;
+ }
+ std::string InputFilePath = Inputs->at(0);
+ Command BaseCmd(Args);
+@@ -411,7 +411,7 @@ int MinimizeCrashInput(const Vector<std::string> &Args,
+ bool Success = ExecuteCommand(Cmd, &CmdOutput);
+ if (Success) {
+ Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str());
+- exit(1);
++ return 1;
+ }
+ Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize "
+ "it further\n",
+@@ -466,42 +466,51 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
+ Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size());
+ if (U.size() < 2) {
+ Printf("INFO: The input is small enough, exiting\n");
+- exit(0);
++ return 0;
+ }
+ F->SetMaxInputLen(U.size());
+ F->SetMaxMutationLen(U.size() - 1);
+ F->MinimizeCrashLoop(U);
+ Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n");
+- exit(0);
+ return 0;
+ }
+
+-void Merge(Fuzzer *F, FuzzingOptions &Options, const Vector<std::string> &Args,
++int Merge(Fuzzer *F, FuzzingOptions &Options, const Vector<std::string> &Args,
+ const Vector<std::string> &Corpora, const char *CFPathOrNull) {
+ if (Corpora.size() < 2) {
+ Printf("INFO: Merge requires two or more corpus dirs\n");
+- exit(0);
++ return 0;
+ }
+
+ Vector<SizedFile> OldCorpus, NewCorpus;
+- GetSizedFilesFromDir(Corpora[0], &OldCorpus);
+- for (size_t i = 1; i < Corpora.size(); i++)
+- GetSizedFilesFromDir(Corpora[i], &NewCorpus);
++ int Res = GetSizedFilesFromDir(Corpora[0], &OldCorpus);
++ if (Res != 0)
++ return Res;
++ for (size_t i = 1; i < Corpora.size(); i++) {
++ Res = GetSizedFilesFromDir(Corpora[i], &NewCorpus);
++ if (Res != 0)
++ return Res;
++ }
+ std::sort(OldCorpus.begin(), OldCorpus.end());
+ std::sort(NewCorpus.begin(), NewCorpus.end());
+
+ std::string CFPath = CFPathOrNull ? CFPathOrNull : TempPath("Merge", ".txt");
+ Vector<std::string> NewFiles;
+ Set<uint32_t> NewFeatures, NewCov;
+- CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures,
++ Res = CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures,
+ {}, &NewCov, CFPath, true);
++ if (Res != 0)
++ return Res;
++
++ if (F->isGracefulExitRequested())
++ return 0;
+ for (auto &Path : NewFiles)
+ F->WriteToOutputCorpus(FileToVector(Path, Options.MaxLen));
+ // We are done, delete the control file if it was a temporary one.
+ if (!Flags.merge_control_file)
+ RemoveFile(CFPath);
+
+- exit(0);
++ return 0;
+ }
+
+ int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict,
+@@ -570,10 +579,9 @@ int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict,
+ return 0;
+ }
+
+-Vector<std::string> ParseSeedInuts(const char *seed_inputs) {
++int ParseSeedInuts(const char *seed_inputs, Vector<std::string> &Files) {
+ // Parse -seed_inputs=file1,file2,... or -seed_inputs=@seed_inputs_file
+- Vector<std::string> Files;
+- if (!seed_inputs) return Files;
++ if (!seed_inputs) return 0;
+ std::string SeedInputs;
+ if (Flags.seed_inputs[0] == '@')
+ SeedInputs = FileToString(Flags.seed_inputs + 1); // File contains list.
+@@ -581,7 +589,7 @@ Vector<std::string> ParseSeedInuts(const char *seed_inputs) {
+ SeedInputs = Flags.seed_inputs; // seed_inputs contains the list.
+ if (SeedInputs.empty()) {
+ Printf("seed_inputs is empty or @file does not exist.\n");
+- exit(1);
++ return 1;
+ }
+ // Parse SeedInputs.
+ size_t comma_pos = 0;
+@@ -590,7 +598,7 @@ Vector<std::string> ParseSeedInuts(const char *seed_inputs) {
+ SeedInputs = SeedInputs.substr(0, comma_pos);
+ }
+ Files.push_back(SeedInputs);
+- return Files;
++ return 0;
+ }
+
+ static Vector<SizedFile> ReadCorpora(const Vector<std::string> &CorpusDirs,
+@@ -624,7 +632,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
+ ProgName = new std::string(Args[0]);
+ if (Argv0 != *ProgName) {
+ Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n");
+- exit(1);
++ return 1;
+ }
+ ParseFlags(Args, EF);
+ if (Flags.help) {
+@@ -723,7 +731,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
+ if (!Options.FocusFunction.empty()) {
+ Printf("ERROR: The parameters `--entropic` and `--focus_function` cannot "
+ "be used together.\n");
+- exit(1);
++ return 1;
+ }
+ Printf("INFO: Running with entropic power schedule (0x%X, %d).\n",
+ Options.EntropicFeatureFrequencyThreshold,
+@@ -809,22 +817,21 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
+ "*** executed the target code on a fixed set of inputs.\n"
+ "***\n");
+ F->PrintFinalStats();
+- exit(0);
++ return 0;
+ }
+
+ if (Flags.fork)
+- FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork);
++ return FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork);
+
+ if (Flags.merge)
+- Merge(F, Options, Args, *Inputs, Flags.merge_control_file);
++ return Merge(F, Options, Args, *Inputs, Flags.merge_control_file);
+
+ if (Flags.merge_inner) {
+ const size_t kDefaultMaxMergeLen = 1 << 20;
+ if (Options.MaxLen == 0)
+ F->SetMaxInputLen(kDefaultMaxMergeLen);
+ assert(Flags.merge_control_file);
+- F->CrashResistantMergeInternalStep(Flags.merge_control_file);
+- exit(0);
++ return F->CrashResistantMergeInternalStep(Flags.merge_control_file);
+ }
+
+ if (Flags.analyze_dict) {
+@@ -842,21 +849,31 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
+ }
+ if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) {
+ Printf("Dictionary analysis failed\n");
+- exit(1);
++ return 1;
+ }
+ Printf("Dictionary analysis succeeded\n");
+- exit(0);
++ return 0;
+ }
+
+- auto CorporaFiles = ReadCorpora(*Inputs, ParseSeedInuts(Flags.seed_inputs));
+- F->Loop(CorporaFiles);
++ {
++ Vector<std::string> Files;
++ int Res = ParseSeedInuts(Flags.seed_inputs, Files);
++ if (Res != 0)
++ return Res;
++ auto CorporaFiles = ReadCorpora(*Inputs, Files);
++ Res = F->Loop(CorporaFiles);
++ if (Res != 0)
++ return Res;
++ if (F->isGracefulExitRequested())
++ return 0;
++ }
+
+ if (Flags.verbosity)
+ Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(),
+ F->secondsSinceProcessStartUp());
+ F->PrintFinalStats();
+
+- exit(0); // Don't let F destroy itself.
++ return 0; // Don't let F destroy itself.
+ }
+
+ extern "C" ATTRIBUTE_INTERFACE int
+diff --git a/tools/fuzzing/libfuzzer/FuzzerFork.cpp b/tools/fuzzing/libfuzzer/FuzzerFork.cpp
+index d9e6b79443e0..ee2a99a250c1 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerFork.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerFork.cpp
+@@ -177,14 +177,16 @@ struct GlobalEnv {
+ return Job;
+ }
+
+- void RunOneMergeJob(FuzzJob *Job) {
++ int RunOneMergeJob(FuzzJob *Job) {
+ auto Stats = ParseFinalStatsFromLog(Job->LogPath);
+ NumRuns += Stats.number_of_executed_units;
+
+ Vector<SizedFile> TempFiles, MergeCandidates;
+ // Read all newly created inputs and their feature sets.
+ // Choose only those inputs that have new features.
+- GetSizedFilesFromDir(Job->CorpusDir, &TempFiles);
++ int Res = GetSizedFilesFromDir(Job->CorpusDir, &TempFiles);
++ if (Res != 0)
++ return Res;
+ std::sort(TempFiles.begin(), TempFiles.end());
+ for (auto &F : TempFiles) {
+ auto FeatureFile = F.File;
+@@ -207,12 +209,14 @@ struct GlobalEnv {
+ Stats.average_exec_per_sec, NumOOMs, NumTimeouts, NumCrashes,
+ secondsSinceProcessStartUp(), Job->JobId, Job->DftTimeInSeconds);
+
+- if (MergeCandidates.empty()) return;
++ if (MergeCandidates.empty()) return 0;
+
+ Vector<std::string> FilesToAdd;
+ Set<uint32_t> NewFeatures, NewCov;
+ CrashResistantMerge(Args, {}, MergeCandidates, &FilesToAdd, Features,
+ &NewFeatures, Cov, &NewCov, Job->CFPath, false);
++ if (Fuzzer::isGracefulExitRequested())
++ return 0;
+ for (auto &Path : FilesToAdd) {
+ auto U = FileToVector(Path);
+ auto NewPath = DirPlusFile(MainCorpusDir, Hash(U));
+@@ -226,7 +230,7 @@ struct GlobalEnv {
+ if (TPC.PcIsFuncEntry(TE))
+ PrintPC(" NEW_FUNC: %p %F %L\n", "",
+ TPC.GetNextInstructionPc(TE->PC));
+-
++ return 0;
+ }
+
+
+@@ -280,7 +284,7 @@ void WorkerThread(JobQueue *FuzzQ, JobQueue *MergeQ) {
+ }
+
+ // This is just a skeleton of an experimental -fork=1 feature.
+-void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
++int FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
+ const Vector<std::string> &Args,
+ const Vector<std::string> &CorpusDirs, int NumJobs) {
+ Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs);
+@@ -294,8 +298,12 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
+ Env.DataFlowBinary = Options.CollectDataFlow;
+
+ Vector<SizedFile> SeedFiles;
+- for (auto &Dir : CorpusDirs)
+- GetSizedFilesFromDir(Dir, &SeedFiles);
++ int Res;
++ for (auto &Dir : CorpusDirs) {
++ Res = GetSizedFilesFromDir(Dir, &SeedFiles);
++ if (Res != 0)
++ return Res;
++ }
+ std::sort(SeedFiles.begin(), SeedFiles.end());
+ Env.TempDir = TempPath("FuzzWithFork", ".dir");
+ Env.DFTDir = DirPlusFile(Env.TempDir, "DFT");
+@@ -310,9 +318,14 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
+ Env.MainCorpusDir = CorpusDirs[0];
+
+ auto CFPath = DirPlusFile(Env.TempDir, "merge.txt");
+- CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features,
++ Res = CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features,
+ {}, &Env.Cov,
+ CFPath, false);
++ if (Res != 0)
++ return Res;
++ if (Fuzzer::isGracefulExitRequested())
++ return 0;
++
+ RemoveFile(CFPath);
+ Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs,
+ Env.Files.size(), Env.TempDir.c_str());
+@@ -345,9 +358,14 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
+ StopJobs();
+ break;
+ }
+- Fuzzer::MaybeExitGracefully();
++ if (Fuzzer::MaybeExitGracefully())
++ return 0;
+
+- Env.RunOneMergeJob(Job.get());
++ Res = Env.RunOneMergeJob(Job.get());
++ if (Res != 0)
++ return Res;
++ if (Fuzzer::isGracefulExitRequested())
++ return 0;
+
+ // Continue if our crash is one of the ignorred ones.
+ if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode)
+@@ -403,7 +421,7 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
+ // Use the exit code from the last child process.
+ Printf("INFO: exiting: %d time: %zds\n", ExitCode,
+ Env.secondsSinceProcessStartUp());
+- exit(ExitCode);
++ return ExitCode;
+ }
+
+ } // namespace fuzzer
+diff --git a/tools/fuzzing/libfuzzer/FuzzerFork.h b/tools/fuzzing/libfuzzer/FuzzerFork.h
+index b29a43e13fbc..1352171ad49d 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerFork.h
++++ b/tools/fuzzing/libfuzzer/FuzzerFork.h
+@@ -16,7 +16,7 @@
+ #include <string>
+
+ namespace fuzzer {
+-void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
++int FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
+ const Vector<std::string> &Args,
+ const Vector<std::string> &CorpusDirs, int NumJobs);
+ } // namespace fuzzer
+diff --git a/tools/fuzzing/libfuzzer/FuzzerIO.cpp b/tools/fuzzing/libfuzzer/FuzzerIO.cpp
+index 0053ef39f2b9..6be2be67c691 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerIO.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerIO.cpp
+@@ -82,7 +82,9 @@ void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V,
+ long *Epoch, size_t MaxSize, bool ExitOnError) {
+ long E = Epoch ? *Epoch : 0;
+ Vector<std::string> Files;
+- ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true);
++ int Res = ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true);
++ if (ExitOnError && Res != 0)
++ exit(Res);
+ size_t NumLoaded = 0;
+ for (size_t i = 0; i < Files.size(); i++) {
+ auto &X = Files[i];
+@@ -97,12 +99,15 @@ void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V,
+ }
+
+
+-void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V) {
++int GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V) {
+ Vector<std::string> Files;
+- ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/true);
++ int Res = ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/true);
++ if (Res != 0)
++ return Res;
+ for (auto &File : Files)
+ if (size_t Size = FileSize(File))
+ V->push_back({File, Size});
++ return 0;
+ }
+
+ std::string DirPlusFile(const std::string &DirPath,
+diff --git a/tools/fuzzing/libfuzzer/FuzzerIO.h b/tools/fuzzing/libfuzzer/FuzzerIO.h
+index 6e4368b971fa..6c90ba637322 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerIO.h
++++ b/tools/fuzzing/libfuzzer/FuzzerIO.h
+@@ -60,7 +60,7 @@ void RawPrint(const char *Str);
+ bool IsFile(const std::string &Path);
+ size_t FileSize(const std::string &Path);
+
+-void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
++int ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
+ Vector<std::string> *V, bool TopDir);
+
+ void RmDirRecursive(const std::string &Dir);
+@@ -79,7 +79,7 @@ struct SizedFile {
+ bool operator<(const SizedFile &B) const { return Size < B.Size; }
+ };
+
+-void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V);
++int GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V);
+
+ char GetSeparator();
+ // Similar to the basename utility: returns the file name w/o the dir prefix.
+diff --git a/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp b/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp
+index 4b453d286c80..1a50295c010f 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp
+@@ -53,16 +53,16 @@ std::string Basename(const std::string &Path) {
+ return Path.substr(Pos + 1);
+ }
+
+-void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
++int ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
+ Vector<std::string> *V, bool TopDir) {
+ auto E = GetEpoch(Dir);
+ if (Epoch)
+- if (E && *Epoch >= E) return;
++ if (E && *Epoch >= E) return 0;
+
+ DIR *D = opendir(Dir.c_str());
+ if (!D) {
+ Printf("%s: %s; exiting\n", strerror(errno), Dir.c_str());
+- exit(1);
++ return 1;
+ }
+ while (auto E = readdir(D)) {
+ std::string Path = DirPlusFile(Dir, E->d_name);
+@@ -71,12 +71,16 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
+ V->push_back(Path);
+ else if ((E->d_type == DT_DIR ||
+ (E->d_type == DT_UNKNOWN && IsDirectory(Path))) &&
+- *E->d_name != '.')
+- ListFilesInDirRecursive(Path, Epoch, V, false);
++ *E->d_name != '.') {
++ int Res = ListFilesInDirRecursive(Path, Epoch, V, false);
++ if (Res != 0)
++ return Res;
++ }
+ }
+ closedir(D);
+ if (Epoch && TopDir)
+ *Epoch = E;
++ return 0;
+ }
+
+
+diff --git a/tools/fuzzing/libfuzzer/FuzzerIOWindows.cpp b/tools/fuzzing/libfuzzer/FuzzerIOWindows.cpp
+index 651283a551cf..0e977bd02557 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerIOWindows.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerIOWindows.cpp
+@@ -98,11 +98,12 @@ size_t FileSize(const std::string &Path) {
+ return size.QuadPart;
+ }
+
+-void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
++int ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
+ Vector<std::string> *V, bool TopDir) {
++ int Res;
+ auto E = GetEpoch(Dir);
+ if (Epoch)
+- if (E && *Epoch >= E) return;
++ if (E && *Epoch >= E) return 0;
+
+ std::string Path(Dir);
+ assert(!Path.empty());
+@@ -116,9 +117,9 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
+ if (FindHandle == INVALID_HANDLE_VALUE)
+ {
+ if (GetLastError() == ERROR_FILE_NOT_FOUND)
+- return;
++ return 0;
+ Printf("No such file or directory: %s; exiting\n", Dir.c_str());
+- exit(1);
++ return 1;
+ }
+
+ do {
+@@ -131,7 +132,9 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
+ FindInfo.cFileName[1] == '.'))
+ continue;
+
+- ListFilesInDirRecursive(FileName, Epoch, V, false);
++ int Res = ListFilesInDirRecursive(FileName, Epoch, V, false);
++ if (Res != 0)
++ return Res;
+ }
+ else if (IsFile(FileName, FindInfo.dwFileAttributes))
+ V->push_back(FileName);
+@@ -145,6 +148,7 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
+
+ if (Epoch && TopDir)
+ *Epoch = E;
++ return 0;
+ }
+
+
+diff --git a/tools/fuzzing/libfuzzer/FuzzerInternal.h b/tools/fuzzing/libfuzzer/FuzzerInternal.h
+index 1f7d671ed848..cc2650b58ef1 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerInternal.h
++++ b/tools/fuzzing/libfuzzer/FuzzerInternal.h
+@@ -35,8 +35,8 @@ public:
+ Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
+ FuzzingOptions Options);
+ ~Fuzzer();
+- void Loop(Vector<SizedFile> &CorporaFiles);
+- void ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles);
++ int Loop(Vector<SizedFile> &CorporaFiles);
++ int ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles);
+ void MinimizeCrashLoop(const Unit &U);
+ void RereadOutputCorpus(size_t MaxSize);
+
+@@ -65,13 +65,16 @@ public:
+ static void StaticFileSizeExceedCallback();
+ static void StaticGracefulExitCallback();
+
++ static void GracefullyExit();
++ static bool isGracefulExitRequested();
++
+ int ExecuteCallback(const uint8_t *Data, size_t Size);
+ bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false,
+ InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr);
+
+ // Merge Corpora[1:] into Corpora[0].
+ void Merge(const Vector<std::string> &Corpora);
+- void CrashResistantMergeInternalStep(const std::string &ControlFilePath);
++ int CrashResistantMergeInternalStep(const std::string &ControlFilePath);
+ MutationDispatcher &GetMD() { return MD; }
+ void PrintFinalStats();
+ void SetMaxInputLen(size_t MaxInputLen);
+@@ -84,7 +87,7 @@ public:
+ bool DuringInitialCorpusExecution);
+
+ void HandleMalloc(size_t Size);
+- static void MaybeExitGracefully();
++ static bool MaybeExitGracefully();
+ std::string WriteToOutputCorpus(const Unit &U);
+
+ private:
+@@ -93,7 +96,7 @@ private:
+ void ExitCallback();
+ void CrashOnOverwrittenData();
+ void InterruptCallback();
+- void MutateAndTestOne();
++ bool MutateAndTestOne();
+ void PurgeAllocator();
+ void ReportNewCoverage(InputInfo *II, const Unit &U);
+ void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size);
+diff --git a/tools/fuzzing/libfuzzer/FuzzerLoop.cpp b/tools/fuzzing/libfuzzer/FuzzerLoop.cpp
+index 4c4e8c271b1f..e7dfc187dbfe 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerLoop.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerLoop.cpp
+@@ -254,12 +254,20 @@ void Fuzzer::ExitCallback() {
+ _Exit(Options.ErrorExitCode);
+ }
+
+-void Fuzzer::MaybeExitGracefully() {
+- if (!F->GracefulExitRequested) return;
++bool Fuzzer::MaybeExitGracefully() {
++ if (!F->GracefulExitRequested) return false;
+ Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid());
+ RmDirRecursive(TempPath("FuzzWithFork", ".dir"));
+ F->PrintFinalStats();
+- _Exit(0);
++ return true;
++}
++
++void Fuzzer::GracefullyExit() {
++ F->GracefulExitRequested = true;
++}
++
++bool Fuzzer::isGracefulExitRequested() {
++ return F->GracefulExitRequested;
+ }
+
+ void Fuzzer::InterruptCallback() {
+@@ -663,7 +671,7 @@ void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
+ }
+ }
+
+-void Fuzzer::MutateAndTestOne() {
++bool Fuzzer::MutateAndTestOne() {
+ MD.StartMutationSequence();
+
+ auto &II = Corpus.ChooseUnitToMutate(MD.GetRand());
+@@ -685,7 +693,7 @@ void Fuzzer::MutateAndTestOne() {
+ for (int i = 0; i < Options.MutateDepth; i++) {
+ if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
+ break;
+- MaybeExitGracefully();
++ if (MaybeExitGracefully()) return true;
+ size_t NewSize = 0;
+ if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() &&
+ Size <= CurrentMaxMutationLen)
+@@ -719,6 +727,7 @@ void Fuzzer::MutateAndTestOne() {
+ }
+
+ II.NeedsEnergyUpdate = true;
++ return false;
+ }
+
+ void Fuzzer::PurgeAllocator() {
+@@ -736,7 +745,7 @@ void Fuzzer::PurgeAllocator() {
+ LastAllocatorPurgeAttemptTime = system_clock::now();
+ }
+
+-void Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) {
++int Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) {
+ const size_t kMaxSaneLen = 1 << 20;
+ const size_t kMinDefaultLen = 4096;
+ size_t MaxSize = 0;
+@@ -795,16 +804,23 @@ void Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) {
+ if (Corpus.empty() && Options.MaxNumberOfRuns) {
+ Printf("ERROR: no interesting inputs were found. "
+ "Is the code instrumented for coverage? Exiting.\n");
+- exit(1);
++ return 1;
+ }
++ return 0;
+ }
+
+-void Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) {
++int Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) {
+ auto FocusFunctionOrAuto = Options.FocusFunction;
+- DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles,
++ int Res = DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles,
+ MD.GetRand());
+- TPC.SetFocusFunction(FocusFunctionOrAuto);
+- ReadAndExecuteSeedCorpora(CorporaFiles);
++ if (Res != 0)
++ return Res;
++ Res = TPC.SetFocusFunction(FocusFunctionOrAuto);
++ if (Res != 0)
++ return Res;
++ Res = ReadAndExecuteSeedCorpora(CorporaFiles);
++ if (Res != 0)
++ return Res;
+ DFT.Clear(); // No need for DFT any more.
+ TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
+ TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs);
+@@ -842,13 +858,15 @@ void Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) {
+ }
+
+ // Perform several mutations and runs.
+- MutateAndTestOne();
++ if (MutateAndTestOne())
++ return 0;
+
+ PurgeAllocator();
+ }
+
+ PrintStats("DONE ", "\n");
+ MD.PrintRecommendedDictionary();
++ return 0;
+ }
+
+ void Fuzzer::MinimizeCrashLoop(const Unit &U) {
+diff --git a/tools/fuzzing/libfuzzer/FuzzerMerge.cpp b/tools/fuzzing/libfuzzer/FuzzerMerge.cpp
+index 919eea848580..0a185c7325bb 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerMerge.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerMerge.cpp
+@@ -28,11 +28,12 @@ bool Merger::Parse(const std::string &Str, bool ParseCoverage) {
+ return Parse(SS, ParseCoverage);
+ }
+
+-void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) {
++int Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) {
+ if (!Parse(IS, ParseCoverage)) {
+ Printf("MERGE: failed to parse the control file (unexpected error)\n");
+- exit(1);
++ return 1;
+ }
++ return 0;
+ }
+
+ // The control file example:
+@@ -194,11 +195,13 @@ Set<uint32_t> Merger::AllFeatures() const {
+ }
+
+ // Inner process. May crash if the target crashes.
+-void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
++int Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
+ Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str());
+ Merger M;
+ std::ifstream IF(CFPath);
+- M.ParseOrExit(IF, false);
++ int Res = M.ParseOrExit(IF, false);
++ if (Res != 0)
++ return Res;
+ IF.close();
+ if (!M.LastFailure.empty())
+ Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n",
+@@ -216,7 +219,8 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
+ };
+ Set<const TracePC::PCTableEntry *> AllPCs;
+ for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) {
+- Fuzzer::MaybeExitGracefully();
++ if (Fuzzer::MaybeExitGracefully())
++ return 0;
+ auto U = FileToVector(M.Files[i].Name);
+ if (U.size() > MaxInputLen) {
+ U.resize(MaxInputLen);
+@@ -261,12 +265,14 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
+ OF.flush();
+ }
+ PrintStatsWrapper("DONE ");
++ return 0;
+ }
+
+-static size_t WriteNewControlFile(const std::string &CFPath,
++static int WriteNewControlFile(const std::string &CFPath,
+ const Vector<SizedFile> &OldCorpus,
+ const Vector<SizedFile> &NewCorpus,
+- const Vector<MergeFileInfo> &KnownFiles) {
++ const Vector<MergeFileInfo> &KnownFiles,
++ size_t &NumFiles) {
+ std::unordered_set<std::string> FilesToSkip;
+ for (auto &SF: KnownFiles)
+ FilesToSkip.insert(SF.Name);
+@@ -292,14 +298,15 @@ static size_t WriteNewControlFile(const std::string &CFPath,
+ if (!ControlFile) {
+ Printf("MERGE-OUTER: failed to write to the control file: %s\n",
+ CFPath.c_str());
+- exit(1);
++ return 1;
+ }
+
+- return FilesToUse.size();
++ NumFiles = FilesToUse.size();
++ return 0;
+ }
+
+ // Outer process. Does not call the target code and thus should not fail.
+-void CrashResistantMerge(const Vector<std::string> &Args,
++int CrashResistantMerge(const Vector<std::string> &Args,
+ const Vector<SizedFile> &OldCorpus,
+ const Vector<SizedFile> &NewCorpus,
+ Vector<std::string> *NewFiles,
+@@ -309,8 +316,9 @@ void CrashResistantMerge(const Vector<std::string> &Args,
+ Set<uint32_t> *NewCov,
+ const std::string &CFPath,
+ bool V /*Verbose*/) {
+- if (NewCorpus.empty() && OldCorpus.empty()) return; // Nothing to merge.
++ if (NewCorpus.empty() && OldCorpus.empty()) return 0; // Nothing to merge.
+ size_t NumAttempts = 0;
++ int Res;
+ Vector<MergeFileInfo> KnownFiles;
+ if (FileSize(CFPath)) {
+ VPrintf(V, "MERGE-OUTER: non-empty control file provided: '%s'\n",
+@@ -331,7 +339,8 @@ void CrashResistantMerge(const Vector<std::string> &Args,
+ VPrintf(
+ V,
+ "MERGE-OUTER: nothing to do, merge has been completed before\n");
+- exit(0);
++ Fuzzer::GracefullyExit();
++ return 0;
+ }
+
+ // Number of input files likely changed, start merge from scratch, but
+@@ -356,7 +365,9 @@ void CrashResistantMerge(const Vector<std::string> &Args,
+ "%zd files, %zd in the initial corpus, %zd processed earlier\n",
+ OldCorpus.size() + NewCorpus.size(), OldCorpus.size(),
+ KnownFiles.size());
+- NumAttempts = WriteNewControlFile(CFPath, OldCorpus, NewCorpus, KnownFiles);
++ Res = WriteNewControlFile(CFPath, OldCorpus, NewCorpus, KnownFiles, NumAttempts);
++ if (Res != 0)
++ return Res;
+ }
+
+ // Execute the inner process until it passes.
+@@ -366,7 +377,8 @@ void CrashResistantMerge(const Vector<std::string> &Args,
+ BaseCmd.removeFlag("fork");
+ BaseCmd.removeFlag("collect_data_flow");
+ for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) {
+- Fuzzer::MaybeExitGracefully();
++ if (Fuzzer::MaybeExitGracefully())
++ return 0;
+ VPrintf(V, "MERGE-OUTER: attempt %zd\n", Attempt);
+ Command Cmd(BaseCmd);
+ Cmd.addFlag("merge_control_file", CFPath);
+@@ -388,7 +400,9 @@ void CrashResistantMerge(const Vector<std::string> &Args,
+ VPrintf(V, "MERGE-OUTER: the control file has %zd bytes\n",
+ (size_t)IF.tellg());
+ IF.seekg(0, IF.beg);
+- M.ParseOrExit(IF, true);
++ Res = M.ParseOrExit(IF, true);
++ if (Res != 0)
++ return Res;
+ IF.close();
+ VPrintf(V,
+ "MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n",
+@@ -399,6 +413,7 @@ void CrashResistantMerge(const Vector<std::string> &Args,
+ VPrintf(V, "MERGE-OUTER: %zd new files with %zd new features added; "
+ "%zd new coverage edges\n",
+ NewFiles->size(), NewFeatures->size(), NewCov->size());
++ return 0;
+ }
+
+ } // namespace fuzzer
+diff --git a/tools/fuzzing/libfuzzer/FuzzerMerge.h b/tools/fuzzing/libfuzzer/FuzzerMerge.h
+index e0c6bc539bdb..6dc1c4c45abf 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerMerge.h
++++ b/tools/fuzzing/libfuzzer/FuzzerMerge.h
+@@ -63,7 +63,7 @@ struct Merger {
+
+ bool Parse(std::istream &IS, bool ParseCoverage);
+ bool Parse(const std::string &Str, bool ParseCoverage);
+- void ParseOrExit(std::istream &IS, bool ParseCoverage);
++ int ParseOrExit(std::istream &IS, bool ParseCoverage);
+ size_t Merge(const Set<uint32_t> &InitialFeatures, Set<uint32_t> *NewFeatures,
+ const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov,
+ Vector<std::string> *NewFiles);
+@@ -71,7 +71,7 @@ struct Merger {
+ Set<uint32_t> AllFeatures() const;
+ };
+
+-void CrashResistantMerge(const Vector<std::string> &Args,
++int CrashResistantMerge(const Vector<std::string> &Args,
+ const Vector<SizedFile> &OldCorpus,
+ const Vector<SizedFile> &NewCorpus,
+ Vector<std::string> *NewFiles,
+diff --git a/tools/fuzzing/libfuzzer/FuzzerTracePC.cpp b/tools/fuzzing/libfuzzer/FuzzerTracePC.cpp
+index b2ca7693e540..fbceda39bc22 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerTracePC.cpp
++++ b/tools/fuzzing/libfuzzer/FuzzerTracePC.cpp
+@@ -238,13 +238,13 @@ void TracePC::IterateCoveredFunctions(CallBack CB) {
+ }
+ }
+
+-void TracePC::SetFocusFunction(const std::string &FuncName) {
++int TracePC::SetFocusFunction(const std::string &FuncName) {
+ // This function should be called once.
+ assert(!FocusFunctionCounterPtr);
+ // "auto" is not a valid function name. If this function is called with "auto"
+ // that means the auto focus functionality failed.
+ if (FuncName.empty() || FuncName == "auto")
+- return;
++ return 0;
+ for (size_t M = 0; M < NumModules; M++) {
+ auto &PCTE = ModulePCTable[M];
+ size_t N = PCTE.Stop - PCTE.Start;
+@@ -256,13 +256,13 @@ void TracePC::SetFocusFunction(const std::string &FuncName) {
+ if (FuncName != Name) continue;
+ Printf("INFO: Focus function is set to '%s'\n", Name.c_str());
+ FocusFunctionCounterPtr = Modules[M].Start() + I;
+- return;
++ return 0;
+ }
+ }
+
+ Printf("ERROR: Failed to set focus function. Make sure the function name is "
+ "valid (%s) and symbolization is enabled.\n", FuncName.c_str());
+- exit(1);
++ return 1;
+ }
+
+ bool TracePC::ObservedFocusFunction() {
+diff --git a/tools/fuzzing/libfuzzer/FuzzerTracePC.h b/tools/fuzzing/libfuzzer/FuzzerTracePC.h
+index 501f3b544971..b46ebb909dbf 100644
+--- a/tools/fuzzing/libfuzzer/FuzzerTracePC.h
++++ b/tools/fuzzing/libfuzzer/FuzzerTracePC.h
+@@ -116,7 +116,7 @@ class TracePC {
+ CB(PC);
+ }
+
+- void SetFocusFunction(const std::string &FuncName);
++ int SetFocusFunction(const std::string &FuncName);
+ bool ObservedFocusFunction();
+
+ struct PCTableEntry {