//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===// // // 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 // //===----------------------------------------------------------------------===// // FuzzerDriver and flag parsing. //===----------------------------------------------------------------------===//
// This function should be present in the libFuzzer so that the client // binary can test for its existence. #if LIBFUZZER_MSVC extern"C"void __libfuzzer_is_present() {} #ifdefined(_M_IX86) || defined(__i386__) #pragma comment(linker, "/include:___libfuzzer_is_present") #else #pragma comment(linker, "/include:__libfuzzer_is_present") #endif #else extern"C" __attribute__((used)) void __libfuzzer_is_present() {} #endif// LIBFUZZER_MSVC
namespace fuzzer {
// Program arguments. struct FlagDescription { constchar *Name; constchar *Description; intDefault; int *IntFlag; constchar **StrFlag; unsignedint *UIntFlag;
};
staticvoid PrintHelp() {
Printf("Usage:\n"); auto Prog = ProgName->c_str();
Printf("\nTo run fuzzing pass 0 or more directories.\n");
Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog);
Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n");
Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog);
Printf("\nFlags: (strictly in form -flag=value)\n");
size_t MaxFlagLen = 0; for (size_t F = 0; F < kNumFlags; F++)
MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen);
for (size_t F = 0; F < kNumFlags; F++) { constauto &D = FlagDescriptions[F]; if (strstr(D.Description, "internal flag") == D.Description) continue;
Printf(" %s", D.Name); for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++)
Printf(" ");
Printf("\t");
Printf("%d\t%s\n", D.Default, D.Description);
}
Printf("\nFlags starting with '--' will be ignored and " "will be passed verbatim to subprocesses.\n");
}
// Avoid calling stol as it triggers a bug in clang/glibc build. staticlong MyStol(constchar *Str) { long Res = 0; long Sign = 1; if (*Str == '-') {
Str++;
Sign = -1;
} for (size_t i = 0; Str[i]; i++) { char Ch = Str[i]; if (Ch < '0' || Ch > '9') return Res;
Res = Res * 10 + (Ch - '0');
} return Res * Sign;
}
staticbool ParseOneFlag(constchar *Param) { if (Param[0] != '-') returnfalse; if (Param[1] == '-') { staticbool PrintedWarning = false; if (!PrintedWarning) {
PrintedWarning = true;
Printf("INFO: libFuzzer ignores flags that start with '--'\n");
} for (size_t F = 0; F < kNumFlags; F++) if (FlagValue(Param + 1, FlagDescriptions[F].Name))
Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1); returntrue;
} for (size_t F = 0; F < kNumFlags; F++) { constchar *Name = FlagDescriptions[F].Name; constchar *Str = FlagValue(Param, Name); if (Str) { if (FlagDescriptions[F].IntFlag) { int Val = MyStol(Str);
*FlagDescriptions[F].IntFlag = Val; if (Flags.verbosity >= 2)
Printf("Flag: %s %d\n", Name, Val); returntrue;
} elseif (FlagDescriptions[F].UIntFlag) { unsignedint Val = std::stoul(Str);
*FlagDescriptions[F].UIntFlag = Val; if (Flags.verbosity >= 2)
Printf("Flag: %s %u\n", Name, Val); returntrue;
} elseif (FlagDescriptions[F].StrFlag) {
*FlagDescriptions[F].StrFlag = Str; if (Flags.verbosity >= 2)
Printf("Flag: %s %s\n", Name, Str); returntrue;
} else { // Deprecated flag.
Printf("Flag: %s: deprecated, don't use\n", Name); returntrue;
}
}
}
Printf("\n\nWARNING: unrecognized flag '%s'; " "use -help=1 to list all flags\n\n", Param); returntrue;
}
// We don't use any library to minimize dependencies. staticvoid ParseFlags(const Vector<std::string> &Args, const ExternalFunctions *EF) { for (size_t F = 0; F < kNumFlags; F++) { if (FlagDescriptions[F].IntFlag)
*FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default; if (FlagDescriptions[F].UIntFlag)
*FlagDescriptions[F].UIntFlag = static_cast<unsignedint>(FlagDescriptions[F].Default); if (FlagDescriptions[F].StrFlag)
*FlagDescriptions[F].StrFlag = nullptr;
}
// Disable len_control by default, if LLVMFuzzerCustomMutator is used. if (EF->LLVMFuzzerCustomMutator) {
Flags.len_control = 0;
Printf("INFO: found LLVMFuzzerCustomMutator (%p). " "Disabling -len_control by default.\n", EF->LLVMFuzzerCustomMutator);
}
Inputs = new Vector<std::string>; for (size_t A = 1; A < Args.size(); A++) { if (ParseOneFlag(Args[A].c_str())) { if (Flags.ignore_remaining_args) break; continue;
}
Inputs->push_back(Args[A]);
}
}
int RunOneTest(Fuzzer *F, constchar *InputFilePath, size_t MaxLen) {
Unit U = FileToVector(InputFilePath); if (MaxLen && MaxLen < U.size())
U.resize(MaxLen);
F->ExecuteCallback(U.data(), U.size());
F->TryDetectingAMemoryLeak(U.data(), U.size(), true); return 0;
}
staticbool AllInputsAreFiles() { if (Inputs->empty()) returnfalse; for (auto &Path : *Inputs) if (!IsFile(Path)) returnfalse; returntrue;
}
static std::string GetDedupTokenFromCmdOutput(const std::string &S) { 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 CleanseCrashInput(const Vector<std::string> &Args, const FuzzingOptions &Options) { if (Inputs->size() != 1 || !Flags.exact_artifact_path) {
Printf("ERROR: -cleanse_crash should be given one input file and" " -exact_artifact_path\n"); return 1;
}
std::string InputFilePath = Inputs->at(0);
std::string OutputFilePath = Flags.exact_artifact_path;
Command Cmd(Args);
Cmd.removeFlag("cleanse_crash");
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"); return 1;
}
std::string InputFilePath = Inputs->at(0);
Command BaseCmd(Args);
BaseCmd.removeFlag("minimize_crash");
BaseCmd.removeFlag("exact_artifact_path");
assert(BaseCmd.hasArgument(InputFilePath));
BaseCmd.removeArgument(InputFilePath); if (Flags.runs <= 0 && Flags.max_total_time == 0) {
Printf("INFO: you need to specify -runs=N or " "-max_total_time=N with -minimize_crash=1\n" "INFO: defaulting to -max_total_time=600\n");
BaseCmd.addFlag("max_total_time", "600");
}
BaseCmd.combineOutAndErr();
std::string CurrentFilePath = InputFilePath; while (true) {
Unit U = FileToVector(CurrentFilePath);
Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n",
CurrentFilePath.c_str(), U.size());
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;
}
int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
assert(Inputs->size() == 1);
std::string InputFilePath = Inputs->at(0);
Unit U = FileToVector(InputFilePath);
Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size()); if (U.size() < 2) {
Printf("INFO: The input is small enough, exiting\n"); return 0;
}
F->SetMaxInputLen(U.size());
F->SetMaxMutationLen(U.size() - 1);
F->MinimizeCrashLoop(U);
Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n"); return 0;
}
int Merge(Fuzzer *F, FuzzingOptions &Options, const Vector<std::string> &Args, const Vector<std::string> &Corpora, constchar *CFPathOrNull) { if (Corpora.size() < 2) {
Printf("INFO: Merge requires two or more corpus dirs\n"); return 0;
}
Vector<SizedFile> OldCorpus, 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());
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);
// Scores and usage count for each dictionary unit.
Vector<int> Scores(Dict.size());
Vector<int> Usages(Dict.size());
Vector<size_t> InitialFeatures;
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) {
InitialFeatures.push_back(Feature);
});
for (size_t i = 0; i < Dict.size(); ++i) {
Vector<uint8_t> 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;
// Get coverage for testcase with masked occurrences of dictionary unit.
F->ExecuteCallback(Data.data(), Data.size());
ModifiedFeatures.clear();
TPC.CollectFeatures([&](size_t Feature) {
ModifiedFeatures.push_back(Feature);
});
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;
Random Rand(Seed); auto *MD = new MutationDispatcher(Rand, Options); auto *Corpus = new InputCorpus(Options.OutputCorpus, Entropic); auto *F = new Fuzzer(Callback, *Corpus, *MD, Options);
for (auto &U: Dictionary) if (U.size() <= Word::GetMaxSize())
MD->AddWordToManualDictionary(Word(U.data(), U.size()));
// Threads are only supported by Chrome. Don't use them with emscripten // for now. #if !LIBFUZZER_EMSCRIPTEN
StartRssThread(F, Flags.rss_limit_mb); #endif// LIBFUZZER_EMSCRIPTEN
if (Flags.minimize_crash) return MinimizeCrashInput(Args, Options);
if (Flags.minimize_crash_internal_step) return MinimizeCrashInputInternalStep(F, Corpus);
if (Flags.cleanse_crash) return CleanseCrashInput(Args, Options);
if (RunIndividualFiles) {
Options.SaveArtifacts = false; int Runs = std::max(1, Flags.runs);
Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(),
Inputs->size(), Runs); for (auto &Path : *Inputs) { auto StartTime = system_clock::now();
Printf("Running: %s\n", Path.c_str()); for (int Iter = 0; Iter < Runs; Iter++)
RunOneTest(F, Path.c_str(), Options.MaxLen); auto StopTime = system_clock::now(); auto MS = duration_cast<milliseconds>(StopTime - StartTime).count();
Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS);
}
Printf("***\n" "*** NOTE: fuzzing was not performed, you have only\n" "*** executed the target code on a fixed set of inputs.\n" "***\n");
F->PrintFinalStats(); return 0;
}
if (Flags.fork) return FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork);
if (Flags.merge) 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); return F->CrashResistantMergeInternalStep(Flags.merge_control_file);
}
if (Flags.analyze_dict) {
size_t MaxLen = INT_MAX; // Large max length.
UnitVector InitialCorpus; for (auto &Inp : *Inputs) {
Printf("Loading corpus dir: %s\n", Inp.c_str());
ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr,
MaxLen, /*ExitOnError=*/false);
}
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"); return 1;
}
Printf("Dictionary analysis succeeded\n"); return 0;
}
{
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();
return 0; // Don't let F destroy itself.
}
extern"C" ATTRIBUTE_INTERFACE int
LLVMFuzzerRunDriver(int *argc, char ***argv, int (*UserCb)(const uint8_t *Data, size_t Size)) { return FuzzerDriver(argc, argv, UserCb);
}
// Storage for global ExternalFunctions object.
ExternalFunctions *EF = nullptr;
} // namespace fuzzer
¤ Dauer der Verarbeitung: 0.42 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.