aboutsummaryrefslogtreecommitdiff
path: root/lib/Fuzzer/FuzzerDriver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Fuzzer/FuzzerDriver.cpp')
-rw-r--r--lib/Fuzzer/FuzzerDriver.cpp195
1 files changed, 170 insertions, 25 deletions
diff --git a/lib/Fuzzer/FuzzerDriver.cpp b/lib/Fuzzer/FuzzerDriver.cpp
index 2bbcb25275e4..0fb83ca64de6 100644
--- a/lib/Fuzzer/FuzzerDriver.cpp
+++ b/lib/Fuzzer/FuzzerDriver.cpp
@@ -15,6 +15,7 @@
#include "FuzzerIO.h"
#include "FuzzerMutate.h"
#include "FuzzerRandom.h"
+#include "FuzzerShmem.h"
#include "FuzzerTracePC.h"
#include <algorithm>
#include <atomic>
@@ -277,7 +278,19 @@ static bool AllInputsAreFiles() {
return true;
}
-int MinimizeCrashInput(const std::vector<std::string> &Args) {
+static std::string GetDedupTokenFromFile(const std::string &Path) {
+ auto S = FileToString(Path);
+ auto Beg = S.find("DEDUP_TOKEN:");
+ if (Beg == std::string::npos)
+ return "";
+ auto End = S.find('\n', Beg);
+ if (End == std::string::npos)
+ return "";
+ return S.substr(Beg, End - Beg);
+}
+
+int MinimizeCrashInput(const std::vector<std::string> &Args,
+ const FuzzingOptions &Options) {
if (Inputs->size() != 1) {
Printf("ERROR: -minimize_crash should be given one input file\n");
exit(1);
@@ -294,19 +307,18 @@ int MinimizeCrashInput(const std::vector<std::string> &Args) {
"INFO: defaulting to -max_total_time=600\n");
BaseCmd += " -max_total_time=600";
}
- // BaseCmd += " > /dev/null 2>&1 ";
+
+ auto LogFilePath = DirPlusFile(
+ TmpDir(), "libFuzzerTemp." + std::to_string(GetPid()) + ".txt");
+ auto LogFileRedirect = " > " + LogFilePath + " 2>&1 ";
std::string CurrentFilePath = InputFilePath;
while (true) {
Unit U = FileToVector(CurrentFilePath);
- if (U.size() < 2) {
- Printf("CRASH_MIN: '%s' is small enough\n", CurrentFilePath.c_str());
- return 0;
- }
Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n",
CurrentFilePath.c_str(), U.size());
- auto Cmd = BaseCmd + " " + CurrentFilePath;
+ auto Cmd = BaseCmd + " " + CurrentFilePath + LogFileRedirect;
Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
int ExitCode = ExecuteCommand(Cmd);
@@ -317,12 +329,19 @@ int MinimizeCrashInput(const std::vector<std::string> &Args) {
Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize "
"it further\n",
CurrentFilePath.c_str(), U.size());
-
- std::string ArtifactPath = "minimized-from-" + Hash(U);
+ auto DedupToken1 = GetDedupTokenFromFile(LogFilePath);
+ if (!DedupToken1.empty())
+ Printf("CRASH_MIN: DedupToken1: %s\n", DedupToken1.c_str());
+
+ std::string ArtifactPath =
+ Flags.exact_artifact_path
+ ? Flags.exact_artifact_path
+ : Options.ArtifactPrefix + "minimized-from-" + Hash(U);
Cmd += " -minimize_crash_internal_step=1 -exact_artifact_path=" +
ArtifactPath;
Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
ExitCode = ExecuteCommand(Cmd);
+ CopyFileToErr(LogFilePath);
if (ExitCode == 0) {
if (Flags.exact_artifact_path) {
CurrentFilePath = Flags.exact_artifact_path;
@@ -330,11 +349,26 @@ int MinimizeCrashInput(const std::vector<std::string> &Args) {
}
Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n",
CurrentFilePath.c_str(), U.size());
- return 0;
+ break;
}
+ auto DedupToken2 = GetDedupTokenFromFile(LogFilePath);
+ if (!DedupToken2.empty())
+ Printf("CRASH_MIN: DedupToken2: %s\n", DedupToken2.c_str());
+
+ if (DedupToken1 != DedupToken2) {
+ if (Flags.exact_artifact_path) {
+ CurrentFilePath = Flags.exact_artifact_path;
+ WriteToFile(U, CurrentFilePath);
+ }
+ Printf("CRASH_MIN: mismatch in dedup tokens"
+ " (looks like a different bug). Won't minimize further\n");
+ break;
+ }
+
CurrentFilePath = ArtifactPath;
- Printf("\n\n\n\n\n\n*********************************\n");
+ Printf("*********************************\n");
}
+ RemoveFile(LogFilePath);
return 0;
}
@@ -342,8 +376,11 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
assert(Inputs->size() == 1);
std::string InputFilePath = Inputs->at(0);
Unit U = FileToVector(InputFilePath);
- assert(U.size() > 2);
Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size());
+ if (U.size() < 2) {
+ Printf("INFO: The input is small enough, exiting\n");
+ exit(0);
+ }
Corpus->AddToCorpus(U, 0);
F->SetMaxInputLen(U.size());
F->SetMaxMutationLen(U.size() - 1);
@@ -353,24 +390,94 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
return 0;
}
+int AnalyzeDictionary(Fuzzer *F, const std::vector<Unit>& Dict,
+ UnitVector& Corpus) {
+ Printf("Started dictionary minimization (up to %d tests)\n",
+ Dict.size() * Corpus.size() * 2);
+
+ // Scores and usage count for each dictionary unit.
+ std::vector<int> Scores(Dict.size());
+ std::vector<int> Usages(Dict.size());
+
+ std::vector<size_t> InitialFeatures;
+ std::vector<size_t> ModifiedFeatures;
+ for (auto &C : Corpus) {
+ // Get coverage for the testcase without modifications.
+ F->ExecuteCallback(C.data(), C.size());
+ InitialFeatures.clear();
+ TPC.CollectFeatures([&](size_t Feature) -> bool {
+ InitialFeatures.push_back(Feature);
+ return true;
+ });
+
+ for (size_t i = 0; i < Dict.size(); ++i) {
+ auto Data = C;
+ auto StartPos = std::search(Data.begin(), Data.end(),
+ Dict[i].begin(), Dict[i].end());
+ // Skip dictionary unit, if the testcase does not contain it.
+ if (StartPos == Data.end())
+ continue;
+
+ ++Usages[i];
+ while (StartPos != Data.end()) {
+ // Replace all occurrences of dictionary unit in the testcase.
+ auto EndPos = StartPos + Dict[i].size();
+ for (auto It = StartPos; It != EndPos; ++It)
+ *It ^= 0xFF;
+
+ StartPos = std::search(EndPos, Data.end(),
+ Dict[i].begin(), Dict[i].end());
+ }
+
+ // Get coverage for testcase with masked occurrences of dictionary unit.
+ F->ExecuteCallback(Data.data(), Data.size());
+ ModifiedFeatures.clear();
+ TPC.CollectFeatures([&](size_t Feature) -> bool {
+ ModifiedFeatures.push_back(Feature);
+ return true;
+ });
+
+ if (InitialFeatures == ModifiedFeatures)
+ --Scores[i];
+ else
+ Scores[i] += 2;
+ }
+ }
+
+ Printf("###### Useless dictionary elements. ######\n");
+ for (size_t i = 0; i < Dict.size(); ++i) {
+ // Dictionary units with positive score are treated as useful ones.
+ if (Scores[i] > 0)
+ continue;
+
+ Printf("\"");
+ PrintASCII(Dict[i].data(), Dict[i].size(), "\"");
+ Printf(" # Score: %d, Used: %d\n", Scores[i], Usages[i]);
+ }
+ Printf("###### End of useless dictionary elements. ######\n");
+ return 0;
+}
+
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->LLVMFuzzerInitialize)
EF->LLVMFuzzerInitialize(argc, argv);
const std::vector<std::string> Args(*argv, *argv + *argc);
assert(!Args.empty());
ProgName = new std::string(Args[0]);
+ if (Argv0 != *ProgName) {
+ Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n");
+ exit(1);
+ }
ParseFlags(Args);
if (Flags.help) {
PrintHelp();
return 0;
}
- if (Flags.minimize_crash)
- return MinimizeCrashInput(Args);
-
if (Flags.close_fd_mask & 2)
DupAndCloseStderr();
if (Flags.close_fd_mask & 1)
@@ -401,7 +508,6 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
Options.MutateDepth = Flags.mutate_depth;
Options.UseCounters = Flags.use_counters;
Options.UseIndirCalls = Flags.use_indir_calls;
- Options.UseMemcmp = Flags.use_memcmp;
Options.UseMemmem = Flags.use_memmem;
Options.UseCmp = Flags.use_cmp;
Options.UseValueProfile = Flags.use_value_profile;
@@ -471,9 +577,37 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
Options.HandleXfsz = Flags.handle_xfsz;
SetSignalHandler(Options);
+ if (Flags.minimize_crash)
+ return MinimizeCrashInput(Args, Options);
+
if (Flags.minimize_crash_internal_step)
return MinimizeCrashInputInternalStep(F, Corpus);
+ if (auto Name = Flags.run_equivalence_server) {
+ SMR.Destroy(Name);
+ if (!SMR.Create(Name)) {
+ Printf("ERROR: can't create shared memory region\n");
+ return 1;
+ }
+ Printf("INFO: EQUIVALENCE SERVER UP\n");
+ while (true) {
+ SMR.WaitClient();
+ size_t Size = SMR.ReadByteArraySize();
+ SMR.WriteByteArray(nullptr, 0);
+ F->RunOne(SMR.GetByteArray(), Size);
+ SMR.PostServer();
+ }
+ return 0;
+ }
+
+ if (auto Name = Flags.use_equivalence_server) {
+ if (!SMR.Open(Name)) {
+ Printf("ERROR: can't open shared memory region\n");
+ return 1;
+ }
+ Printf("INFO: EQUIVALENCE CLIENT UP\n");
+ }
+
if (DoPlainRun) {
Options.SaveArtifacts = false;
int Runs = std::max(1, Flags.runs);
@@ -499,14 +633,12 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
if (Flags.merge) {
if (Options.MaxLen == 0)
F->SetMaxInputLen(kMaxSaneLen);
- if (TPC.UsingTracePcGuard()) {
- if (Flags.merge_control_file)
- F->CrashResistantMergeInternalStep(Flags.merge_control_file);
- else
- F->CrashResistantMerge(Args, *Inputs);
- } else {
- F->Merge(*Inputs);
- }
+ if (Flags.merge_control_file)
+ F->CrashResistantMergeInternalStep(Flags.merge_control_file);
+ else
+ F->CrashResistantMerge(Args, *Inputs,
+ Flags.load_coverage_summary,
+ Flags.save_coverage_summary);
exit(0);
}
@@ -519,6 +651,19 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
TemporaryMaxLen, /*ExitOnError=*/false);
}
+ if (Flags.analyze_dict) {
+ if (Dictionary.empty() || Inputs->empty()) {
+ Printf("ERROR: can't analyze dict without dict and corpus provided\n");
+ return 1;
+ }
+ if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) {
+ Printf("Dictionary analysis failed\n");
+ exit(1);
+ }
+ Printf("Dictionary analysis suceeded\n");
+ exit(0);
+ }
+
if (Options.MaxLen == 0) {
size_t MaxLen = 0;
for (auto &U : InitialCorpus)
@@ -536,7 +681,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
F->Loop();
if (Flags.verbosity)
- Printf("Done %d runs in %zd second(s)\n", F->getTotalNumberOfRuns(),
+ Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(),
F->secondsSinceProcessStartUp());
F->PrintFinalStats();