diff options
-rw-r--r-- | llvm/lib/Fuzzer/FuzzerDriver.cpp | 107 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/FuzzerFlags.def | 4 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/test/minimize_crash.test | 6 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/test/value-profile-div.test | 3 | ||||
-rw-r--r-- | llvm/lib/Fuzzer/test/value-profile-load.test | 3 |
5 files changed, 115 insertions, 8 deletions
diff --git a/llvm/lib/Fuzzer/FuzzerDriver.cpp b/llvm/lib/Fuzzer/FuzzerDriver.cpp index 10db673eda8..5d0c5a5496b 100644 --- a/llvm/lib/Fuzzer/FuzzerDriver.cpp +++ b/llvm/lib/Fuzzer/FuzzerDriver.cpp @@ -215,16 +215,27 @@ static void WorkerThread(const std::string &Cmd, std::atomic<int> *Counter, } } -static int RunInMultipleProcesses(const std::vector<std::string> &Args, - int NumWorkers, int NumJobs) { - std::atomic<int> Counter(0); - std::atomic<bool> HasErrors(false); +static std::string CloneArgsWithoutX(const std::vector<std::string> &Args, + const char *X1, const char *X2) { std::string Cmd; for (auto &S : Args) { - if (FlagValue(S.c_str(), "jobs") || FlagValue(S.c_str(), "workers")) + if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2)) continue; Cmd += S + " "; } + return Cmd; +} + +static std::string CloneArgsWithoutX(const std::vector<std::string> &Args, + const char *X) { + return CloneArgsWithoutX(Args, X, X); +} + +static int RunInMultipleProcesses(const std::vector<std::string> &Args, + int NumWorkers, int NumJobs) { + std::atomic<int> Counter(0); + std::atomic<bool> HasErrors(false); + std::string Cmd = CloneArgsWithoutX(Args, "jobs", "workers"); std::vector<std::thread> V; std::thread Pulse(PulseThread); Pulse.detach(); @@ -266,6 +277,80 @@ static bool AllInputsAreFiles() { return true; } +int MinimizeCrashInput(const std::vector<std::string> &Args) { + if (Inputs->size() != 1) { + Printf("ERROR: -minimize_crash should be given one input file\n"); + exit(1); + } + if (Flags.runs <= 0 && Flags.max_total_time == 0) { + Printf("ERROR: you need to use -runs=N or " + "-max_total_time=N with -minimize_crash=1\n" ); + exit(1); + } + std::string InputFilePath = Inputs->at(0); + std::string BaseCmd = CloneArgsWithoutX(Args, "minimize_crash"); + auto InputPos = BaseCmd.find(" " + InputFilePath + " "); + assert(InputPos != std::string::npos); + BaseCmd.erase(InputPos, InputFilePath.size() + 1); + // BaseCmd += " > /dev/null 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; + + Printf("CRASH_MIN: executing: %s\n", Cmd.c_str()); + int ExitCode = ExecuteCommand(Cmd); + if (ExitCode == 0) { + Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str()); + exit(1); + } + 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); + Cmd += " -minimize_crash_internal_step=1 -exact_artifact_path=" + + ArtifactPath; + Printf("CRASH_MIN: executing: %s\n", Cmd.c_str()); + ExitCode = ExecuteCommand(Cmd); + if (ExitCode == 0) { + Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n", + CurrentFilePath.c_str(), U.size()); + return 0; + } + CurrentFilePath = ArtifactPath; + Printf("\n\n\n\n\n\n*********************************\n"); + } + return 0; +} + +int MinimizeCrashInputInternalStep(Fuzzer *F) { + 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()); + Unit X(U.size() - 1); + for (size_t I = 0; I < U.size(); I++) { + std::copy(U.begin(), U.begin() + I, X.begin()); + std::copy(U.begin() + I + 1, U.end(), X.begin() + I); + F->AddToCorpus(X); + } + F->SetMaxLen(U.size() - 1); + F->Loop(); + Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n"); + exit(0); + return 0; +} + int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { using namespace fuzzer; assert(argc && argv && "Argument pointers cannot be nullptr"); @@ -281,6 +366,9 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { return 0; } + if (Flags.minimize_crash) + return MinimizeCrashInput(Args); + if (Flags.close_fd_mask & 2) DupAndCloseStderr(); if (Flags.close_fd_mask & 1) @@ -319,7 +407,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.RssLimitMb = Flags.rss_limit_mb; if (Flags.runs >= 0) Options.MaxNumberOfRuns = Flags.runs; - if (!Inputs->empty()) + if (!Inputs->empty() && !Flags.minimize_crash_internal_step) Options.OutputCorpus = (*Inputs)[0]; Options.ReportSlowUnits = Flags.report_slow_units; if (Flags.artifact_prefix) @@ -333,7 +421,8 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { if (Flags.verbosity > 0 && !Dictionary.empty()) Printf("Dictionary: %zd entries\n", Dictionary.size()); bool DoPlainRun = AllInputsAreFiles(); - Options.SaveArtifacts = !DoPlainRun; + Options.SaveArtifacts = + !DoPlainRun || Flags.minimize_crash_internal_step; Options.PrintNewCovPcs = Flags.print_pcs; Options.PrintFinalStats = Flags.print_final_stats; Options.TruncateUnits = Flags.truncate_units; @@ -371,6 +460,9 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { if (Flags.handle_int) SetSigIntHandler(); if (Flags.handle_term) SetSigTermHandler(); + if (Flags.minimize_crash_internal_step) + return MinimizeCrashInputInternalStep(&F); + if (DoPlainRun) { Options.SaveArtifacts = false; int Runs = std::max(1, Flags.runs); @@ -393,7 +485,6 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { exit(0); } - if (Flags.merge) { if (Options.MaxLen == 0) F.SetMaxLen(kMaxSaneLen); diff --git a/llvm/lib/Fuzzer/FuzzerFlags.def b/llvm/lib/Fuzzer/FuzzerFlags.def index c89e75c9f71..bae00ff9ce2 100644 --- a/llvm/lib/Fuzzer/FuzzerFlags.def +++ b/llvm/lib/Fuzzer/FuzzerFlags.def @@ -37,6 +37,10 @@ FUZZER_FLAG_INT(help, 0, "Print help.") FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be " "merged into the 1-st corpus. Only interesting units will be taken. " "This flag can be used to minimize a corpus.") +FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided" + " crash input. Use with -runs=N or -max_total_time=N to limit " + "the number attempts") +FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag") FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters") FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters") FUZZER_FLAG_INT(use_traces, 0, "Experimental: use instruction traces") diff --git a/llvm/lib/Fuzzer/test/minimize_crash.test b/llvm/lib/Fuzzer/test/minimize_crash.test new file mode 100644 index 00000000000..390f4eaab79 --- /dev/null +++ b/llvm/lib/Fuzzer/test/minimize_crash.test @@ -0,0 +1,6 @@ +CHECK: CRASH_MIN: failed to minimize beyond minimized-from-{{.*}} (3 bytes), exiting +RUN: echo 'Hi!rv349f34t3gg' > not_minimal_crash +RUN: LLVMFuzzer-NullDerefTest -minimize_crash=1 not_minimal_crash -max_total_time=2 2>&1 | FileCheck %s +RUN: rm not_minimal_crash minimized-from-* + + diff --git a/llvm/lib/Fuzzer/test/value-profile-div.test b/llvm/lib/Fuzzer/test/value-profile-div.test new file mode 100644 index 00000000000..ba45e4129d3 --- /dev/null +++ b/llvm/lib/Fuzzer/test/value-profile-div.test @@ -0,0 +1,3 @@ +CHECK: AddressSanitizer: FPE +RUN: not LLVMFuzzer-DivTest -seed=1 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s + diff --git a/llvm/lib/Fuzzer/test/value-profile-load.test b/llvm/lib/Fuzzer/test/value-profile-load.test new file mode 100644 index 00000000000..05a13821fce --- /dev/null +++ b/llvm/lib/Fuzzer/test/value-profile-load.test @@ -0,0 +1,3 @@ +CHECK: AddressSanitizer: global-buffer-overflow +RUN: not LLVMFuzzer-LoadTest -seed=1 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s + |