diff options
author | vanhauser-thc <vh@thc.org> | 2022-05-31 09:24:28 +0200 |
---|---|---|
committer | vanhauser-thc <vh@thc.org> | 2022-05-31 09:24:28 +0200 |
commit | 50c6031cc3350b3fea486774ddef89eaf2cab5c3 (patch) | |
tree | bb356bd13ba568200194353ecfb700953ef6e91d /utils/optimin/src/OptiMin.cpp | |
parent | 066d65d8469ca504ab86771bd8e5e608efec9517 (diff) | |
download | afl++-50c6031cc3350b3fea486774ddef89eaf2cab5c3.tar.gz |
remove optimin
Diffstat (limited to 'utils/optimin/src/OptiMin.cpp')
-rw-r--r-- | utils/optimin/src/OptiMin.cpp | 702 |
1 files changed, 0 insertions, 702 deletions
diff --git a/utils/optimin/src/OptiMin.cpp b/utils/optimin/src/OptiMin.cpp deleted file mode 100644 index b0082d14..00000000 --- a/utils/optimin/src/OptiMin.cpp +++ /dev/null @@ -1,702 +0,0 @@ -/* - * OptiMin, an optimal fuzzing corpus minimizer. - * - * Author: Adrian Herrera - */ - -#include <cstdint> -#include <cstdlib> -#include <vector> - -#include <llvm/ADT/DenseSet.h> -#include <llvm/ADT/DenseMap.h> -#include <llvm/ADT/SmallVector.h> -#include <llvm/ADT/StringExtras.h> -#include <llvm/ADT/StringMap.h> -#include <llvm/Support/Chrono.h> -#include <llvm/Support/CommandLine.h> -#include <llvm/Support/Error.h> -#include <llvm/Support/FileSystem.h> -#include <llvm/Support/MemoryBuffer.h> -#include <llvm/Support/Path.h> -#include <llvm/Support/Program.h> -#include <llvm/Support/WithColor.h> - -#include "EvalMaxSAT.h" - -using namespace llvm; - -namespace { - -// -------------------------------------------------------------------------- // -// Classes -// -------------------------------------------------------------------------- // - -/// Ensure seed weights default to 1 -class Weight { - - public: - Weight() : Weight(1){}; - Weight(uint32_t V) : Value(V){}; - - operator unsigned() const { - - return Value; - - } - - private: - const unsigned Value; - -}; - -// -------------------------------------------------------------------------- // -// Typedefs -// -------------------------------------------------------------------------- // - -/// AFL tuple (edge) ID -using AFLTupleID = uint32_t; - -/// Pair of tuple ID and hit count -using AFLTuple = std::pair<AFLTupleID, /* Frequency */ unsigned>; - -/// Coverage for a given seed file -using AFLCoverageVector = std::vector<AFLTuple>; - -/// Map seed file paths to its coverage vector -using AFLCoverageMap = StringMap<AFLCoverageVector>; - -/// Map seed file paths to a weight -using WeightsMap = StringMap<Weight>; - -/// A seed identifier in the MaxSAT solver -using SeedID = int; - -/// Associates seed identifiers to seed files -using MaxSATSeeds = - SmallVector<std::pair<SeedID, /* Seed file */ std::string>, 0>; - -/// Set of literal identifiers -using MaxSATSeedSet = DenseSet<SeedID>; - -/// Maps tuple IDs to the literal identifiers that "cover" that tuple -using MaxSATCoverageMap = DenseMap<AFLTupleID, MaxSATSeedSet>; - -// -------------------------------------------------------------------------- // -// Global variables -// -------------------------------------------------------------------------- // - -// This is based on the human class count in `count_class_human[256]` in -// `afl-showmap.c` -static constexpr uint32_t MAX_EDGE_FREQ = 8; - -// The maximum number of failures allowed when parsing a weights file -static constexpr unsigned MAX_WEIGHT_FAILURES = 5; - -static sys::TimePoint<> StartTime, EndTime; -static std::chrono::seconds Duration; - -static std::string ShowmapPath; -static bool TargetArgsHasAtAt = false; -static bool KeepTraces = false; -static bool SkipBinCheck = false; - -static const auto ErrMsg = [] { - - return WithColor(errs(), raw_ostream::RED, /*Bold=*/true) << "[-] "; - -}; - -static const auto WarnMsg = [] { - - return WithColor(errs(), raw_ostream::MAGENTA, /*Bold=*/true) << "[-] "; - -}; - -static const auto SuccMsg = [] { - - return WithColor(outs(), raw_ostream::GREEN, /*Bold=*/true) << "[+] "; - -}; - -static const auto StatMsg = [] { - - return WithColor(outs(), raw_ostream::BLUE, /*Bold=*/true) << "[*] "; - -}; - -static cl::opt<std::string> InputDir("i", cl::desc("Input directory"), - cl::value_desc("dir"), cl::Required); -static cl::opt<std::string> OutputDir("o", cl::desc("Output directory"), - cl::value_desc("dir"), cl::Required); - -static cl::opt<bool> EdgesOnly("f", cl::desc("Include edge hit counts"), - cl::init(true)); -static cl::opt<std::string> WeightsFile("w", cl::desc("Weights file"), - cl::value_desc("csv")); - -static cl::opt<std::string> TargetProg(cl::Positional, - cl::desc("<target program>"), - cl::Required); -static cl::list<std::string> TargetArgs(cl::ConsumeAfter, - cl::desc("[target args...]")); - -static cl::opt<std::string> MemLimit( - "m", cl::desc("Memory limit for child process (default=none)"), - cl::value_desc("megs"), cl::init("none")); -static cl::opt<std::string> Timeout( - "t", cl::desc("Run time limit for child process (default=5000)"), - cl::value_desc("msec"), cl::init("5000")); - -static cl::opt<bool> CrashMode( - "C", cl::desc("Keep crashing inputs, reject everything else")); -static cl::opt<bool> FridaMode( - "O", cl::desc("Use binary-only instrumentation (FRIDA mode)")); -static cl::opt<bool> QemuMode( - "Q", cl::desc("Use binary-only instrumentation (QEMU mode)")); -static cl::opt<bool> UnicornMode( - "U", cl::desc("Use unicorn-based instrumentation (unicorn mode)")); - -} // anonymous namespace - -// -------------------------------------------------------------------------- // -// Helper functions -// -------------------------------------------------------------------------- // - -static void GetWeights(const MemoryBuffer &MB, WeightsMap &Weights) { - - SmallVector<StringRef, 0> Lines; - MB.getBuffer().trim().split(Lines, '\n'); - - unsigned FailureCount = 0; - unsigned Weight = 0; - - for (const auto &Line : Lines) { - - const auto &[Seed, WeightStr] = Line.split(','); - - if (to_integer(WeightStr, Weight, 10)) { - - Weights.try_emplace(Seed, Weight); - - } else { - - if (FailureCount >= MAX_WEIGHT_FAILURES) { - ErrMsg() << "Too many failures. Aborting\n"; - std::exit(1); - } - - WarnMsg() << "Failed to read weight for '" << Seed << "'. Skipping...\n"; - FailureCount++; - - } - - } - -} - -static std::error_code readCov(const StringRef Trace, AFLCoverageVector &Cov) { - - const auto CovOrErr = MemoryBuffer::getFile(Trace); - if (const auto EC = CovOrErr.getError()) return EC; - - SmallVector<StringRef, 0> Lines; - CovOrErr.get()->getBuffer().trim().split(Lines, '\n'); - - AFLTupleID Edge = 0; - unsigned Freq = 0; - - for (const auto &Line : Lines) { - - const auto &[EdgeStr, FreqStr] = Line.split(':'); - - to_integer(EdgeStr, Edge, 10); - to_integer(FreqStr, Freq, 10); - Cov.push_back({Edge, Freq}); - - } - - return std::error_code(); - -} - -static Error runShowmap(AFLCoverageMap &CovMap, const StringRef Input, - bool BinCheck = false) { - - const bool InputIsFile = !sys::fs::is_directory(Input); - Optional<StringRef> Redirects[] = {None, None, None}; - - SmallString<32> TraceDir{OutputDir}; - sys::path::append(TraceDir, ".traces"); - - SmallString<32> Output{TraceDir}; - SmallString<32> StdinFile{TraceDir}; - - // ------------------------------------------------------------------------ // - // Prepare afl-showmap arguments - // - // If the given input is a file, then feed this directly into stdin. - // Otherwise, if it is a directory, specify this on the afl-showmap command - // line. - // ------------------------------------------------------------------------ // - - SmallVector<StringRef, 12> ShowmapArgs{ShowmapPath, "-q", - "-m", MemLimit, - "-t", Timeout}; - - if (InputIsFile) { - - StdinFile = Input; - sys::path::append(Output, - BinCheck ? ".run_test" : sys::path::filename(Input)); - - } else { - - sys::path::append(StdinFile, ".cur_input"); - ShowmapArgs.append({"-i", Input}); - - } - - - if (TargetArgsHasAtAt) { - - ShowmapArgs.append({"-H", StdinFile}); - Redirects[/* stdin */ 0] = "/dev/null"; - - } else if (InputIsFile) { - - Redirects[/* stdin */ 0] = Input; - - } - - if (FridaMode) ShowmapArgs.push_back("-O"); - if (QemuMode) ShowmapArgs.push_back("-Q"); - if (UnicornMode) ShowmapArgs.push_back("-U"); - - ShowmapArgs.append({"-o", Output, "--", TargetProg}); - ShowmapArgs.append(TargetArgs.begin(), TargetArgs.end()); - - // ------------------------------------------------------------------------ // - // Run afl-showmap - // ------------------------------------------------------------------------ // - - const int RC = sys::ExecuteAndWait(ShowmapPath, ShowmapArgs, - /*env=*/None, Redirects); - if (RC && !CrashMode) { - - ErrMsg() << "Exit code " << RC << " != 0 received from afl-showmap\n"; - return createStringError(inconvertibleErrorCode(), "afl-showmap failed"); - - } - - // ------------------------------------------------------------------------ // - // Parse afl-showmap output - // ------------------------------------------------------------------------ // - - AFLCoverageVector Cov; - std::error_code EC; - sys::fs::file_status Status; - - if (InputIsFile) { - - // Read a single output coverage file - if ((EC = readCov(Output, Cov))) { - - sys::fs::remove(Output); - return errorCodeToError(EC); - - } - - CovMap.try_emplace(sys::path::filename(Input), Cov); - if (!KeepTraces) sys::fs::remove(Output); - - } else { - - // Read a directory of output coverage files - for (sys::fs::recursive_directory_iterator Dir(TraceDir, EC), DirEnd; - Dir != DirEnd && !EC; Dir.increment(EC)) { - - if (EC) return errorCodeToError(EC); - - const auto &Path = Dir->path(); - if ((EC = sys::fs::status(Path, Status))) return errorCodeToError(EC); - - switch (Status.type()) { - - case sys::fs::file_type::regular_file: - case sys::fs::file_type::symlink_file: - case sys::fs::file_type::type_unknown: - Cov.clear(); - if ((EC = readCov(Path, Cov))) { - - sys::fs::remove(Path); - return errorCodeToError(EC); - - } - - CovMap.try_emplace(sys::path::filename(Path), Cov); - default: - // Ignore - break; - - } - - } - - if (!KeepTraces) sys::fs::remove_directories(TraceDir); - - } - - return Error::success(); - -} - -static inline void StartTimer() { - - StartTime = std::chrono::system_clock::now(); - -} - -static inline void EndTimer() { - - EndTime = std::chrono::system_clock::now(); - Duration = - std::chrono::duration_cast<std::chrono::seconds>(EndTime - StartTime); - - SuccMsg() << " Completed in " << Duration.count() << " s\n"; - -} - -// -------------------------------------------------------------------------- // -// Main function -// -------------------------------------------------------------------------- // - -int main(int argc, char *argv[]) { - - WeightsMap Weights; - std::error_code EC; - - // ------------------------------------------------------------------------ // - // Parse command-line options and environment variables - // - // Also check the target arguments, as this determines how we run afl-showmap. - // ------------------------------------------------------------------------ // - - cl::ParseCommandLineOptions(argc, argv, "Optimal corpus minimizer"); - - KeepTraces = !!std::getenv("AFL_KEEP_TRACES"); - SkipBinCheck = !!std::getenv("AFL_SKIP_BIN_CHECK"); - const auto AFLPath = std::getenv("AFL_PATH"); - - if (CrashMode) ::setenv("AFL_CMIN_CRASHES_ONLY", "1", /*overwrite=*/true); - - for (const auto &Arg : TargetArgs) - if (Arg == "@@") TargetArgsHasAtAt = true; - - // ------------------------------------------------------------------------ // - // Find afl-showmap - // ------------------------------------------------------------------------ // - - SmallVector<StringRef, 16> EnvPaths; - - if (const char *PathEnv = std::getenv("PATH")) - SplitString(PathEnv, EnvPaths, ":"); - if (AFLPath) EnvPaths.push_back(AFLPath); - - const auto ShowmapOrErr = sys::findProgramByName("afl-showmap", EnvPaths); - if (ShowmapOrErr.getError()) { - - ErrMsg() << "Failed to find afl-showmap. Check your PATH\n"; - return 1; - - } - - ShowmapPath = *ShowmapOrErr; - - // ------------------------------------------------------------------------ // - // Parse weights - // - // Weights are stored in CSV file mapping a seed file name to an integer - // greater than zero. - // ------------------------------------------------------------------------ // - - if (WeightsFile != "") { - - StatMsg() << "Reading weights from '" << WeightsFile << "'...\n"; - StartTimer(); - - const auto WeightsOrErr = MemoryBuffer::getFile(WeightsFile); - if ((EC = WeightsOrErr.getError())) { - - ErrMsg() << "Failed to read weights from '" << WeightsFile - << "': " << EC.message() << '\n'; - return 1; - - } - - GetWeights(*WeightsOrErr.get(), Weights); - - EndTimer(); - - } - - // ------------------------------------------------------------------------ // - // Traverse input directory - // - // Find the seed files inside this directory (and subdirectories). - // ------------------------------------------------------------------------ // - - StatMsg() << "Locating seeds in '" << InputDir << "'...\n"; - StartTimer(); - - bool IsDirResult; - if ((EC = sys::fs::is_directory(InputDir, IsDirResult))) { - - ErrMsg() << "Invalid input directory '" << InputDir << "': " << EC.message() - << '\n'; - return 1; - - } - - sys::fs::file_status Status; - StringMap<std::string> SeedFiles; - - for (sys::fs::recursive_directory_iterator Dir(InputDir, EC), DirEnd; - Dir != DirEnd && !EC; Dir.increment(EC)) { - - if (EC) { - - ErrMsg() << "Failed to traverse input directory '" << InputDir - << "': " << EC.message() << '\n'; - return 1; - - } - - const auto &Path = Dir->path(); - if ((EC = sys::fs::status(Path, Status))) { - - ErrMsg() << "Failed to access '" << Path << "': " << EC.message() << '\n'; - return 1; - - } - - switch (Status.type()) { - - case sys::fs::file_type::regular_file: - case sys::fs::file_type::symlink_file: - case sys::fs::file_type::type_unknown: - SeedFiles.try_emplace(sys::path::filename(Path), - sys::path::parent_path(Path)); - default: - /* Ignore */ - break; - - } - - } - - EndTimer(); - - if (SeedFiles.empty()) { - - ErrMsg() << "Failed to find any seed files in '" << InputDir << "'\n"; - return 1; - - } - - // ------------------------------------------------------------------------ // - // Setup output directory - // ------------------------------------------------------------------------ // - - SmallString<32> TraceDir{OutputDir}; - sys::path::append(TraceDir, ".traces"); - - if ((EC = sys::fs::remove_directories(TraceDir))) { - - ErrMsg() << "Failed to remove existing trace directory in '" << OutputDir - << "': " << EC.message() << '\n'; - return 1; - - } - - if ((EC = sys::fs::create_directories(TraceDir))) { - - ErrMsg() << "Failed to create output directory '" << OutputDir - << "': " << EC.message() << '\n'; - return 1; - - } - - // ------------------------------------------------------------------------ // - // Test the target binary - // ------------------------------------------------------------------------ // - - AFLCoverageMap CovMap; - - if (!SkipBinCheck) { - - const auto It = SeedFiles.begin(); - SmallString<32> TestSeed{It->second}; - sys::path::append(TestSeed, It->first()); - - StatMsg() << "Testing the target binary with '" << TestSeed << "`...\n"; - StartTimer(); - - if (auto Err = runShowmap(CovMap, TestSeed, /*BinCheck=*/true)) { - - ErrMsg() << "No instrumentation output detected \n"; - return 1; - - } - - EndTimer(); - SuccMsg() << "OK, " << CovMap.begin()->second.size() - << " tuples recorded\n"; - - } - - // ------------------------------------------------------------------------ // - // Generate seed coverage - // - // Iterate over the corpus directory, which should contain seed files. Execute - // these seeds in the target program to generate coverage information, and - // then store this coverage information in the appropriate data structures. - // ------------------------------------------------------------------------ // - - StatMsg() << "Running afl-showmap on " << SeedFiles.size() << " seeds...\n"; - StartTimer(); - - MaxSATSeeds SeedVars; - MaxSATCoverageMap SeedCoverage; - EvalMaxSAT Solver(/*nbMinimizeThread=*/0); - - CovMap.clear(); - if (auto Err = runShowmap(CovMap, InputDir)) { - - ErrMsg() << "Failed to generate coverage: " << Err << '\n'; - return 1; - - } - - for (const auto &SeedCov : CovMap) { - - // Create a variable to represent the seed - const SeedID Var = Solver.newVar(); - SeedVars.emplace_back(Var, SeedCov.first()); - - // Record the set of seeds that cover a particular edge - for (auto &[Edge, Freq] : SeedCov.second) { - - if (EdgesOnly) { - - // Ignore edge frequency - SeedCoverage[Edge].insert(Var); - - } else { - - // Executing edge `E` `N` times means that it was executed `N - 1` times - for (unsigned I = 0; I < Freq; ++I) - SeedCoverage[MAX_EDGE_FREQ * Edge + I].insert(Var); - - } - - } - - } - - EndTimer(); - - // ------------------------------------------------------------------------ // - // Set the hard and soft constraints in the solver - // ------------------------------------------------------------------------ // - - StatMsg() << "Generating constraints...\n"; - StartTimer(); - - size_t SeedCount = 0; - - // Ensure that at least one seed is selected that covers a particular edge - // (hard constraint) - std::vector<SeedID> Clauses; - for (const auto &[_, Seeds] : SeedCoverage) { - - if (Seeds.empty()) continue; - - Clauses.clear(); - for (const auto &Seed : Seeds) - Clauses.push_back(Seed); - - Solver.addClause(Clauses); - - } - - // Select the minimum number of seeds that cover a particular set of edges - // (soft constraint) - for (const auto &[Var, Seed] : SeedVars) - Solver.addWeightedClause({-Var}, Weights[sys::path::filename(Seed)]); - - EndTimer(); - - // ------------------------------------------------------------------------ // - // Generate a solution - // ------------------------------------------------------------------------ // - - StatMsg() << "Solving...\n"; - StartTimer(); - - const bool Solved = Solver.solve(); - - EndTimer(); - - // ------------------------------------------------------------------------ // - // Save the solution - // - // This will copy the selected seeds to the given output directory. - // ------------------------------------------------------------------------ // - - SmallVector<StringRef, 64> Solution; - SmallString<32> InputSeed, OutputSeed; - - if (Solved) { - - for (const auto &[Var, Seed] : SeedVars) - if (Solver.getValue(Var) > 0) Solution.push_back(Seed); - - } else { - - ErrMsg() << "Failed to find an optimal solution for '" << InputDir << "'\n"; - return 1; - - } - - StatMsg() << "Copying " << Solution.size() << " seeds to '" << OutputDir - << "'...\n"; - StartTimer(); - - SeedCount = 0; - - for (const auto &Seed : Solution) { - - InputSeed = SeedFiles[Seed]; - sys::path::append(InputSeed, Seed); - - OutputSeed = OutputDir; - sys::path::append(OutputSeed, Seed); - - if ((EC = sys::fs::copy_file(InputSeed, OutputSeed))) { - - ErrMsg() << "Failed to copy '" << Seed << "' to '" << OutputDir - << "': " << EC.message() << '\n'; - return 1; - - } - - } - - EndTimer(); - SuccMsg() << "Done!\n"; - - return 0; - -} - |