diff options
Diffstat (limited to 'lib/fuzzer')
-rw-r--r-- | lib/fuzzer/FuzzerBuiltinsMsvc.h | 22 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerDefs.h | 5 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerDriver.cpp | 1 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerExtFunctions.def | 11 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerFlags.def | 3 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerInternal.h | 3 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerLoop.cpp | 19 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerMerge.cpp | 82 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerOptions.h | 1 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerTracePC.cpp | 41 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerTracePC.h | 3 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerUtil.h | 2 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerUtilFuchsia.cpp | 26 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerUtilPosix.cpp | 6 | ||||
-rw-r--r-- | lib/fuzzer/FuzzerUtilWindows.cpp | 4 | ||||
-rw-r--r-- | lib/fuzzer/utils/FuzzedDataProvider.h | 245 |
16 files changed, 117 insertions, 357 deletions
diff --git a/lib/fuzzer/FuzzerBuiltinsMsvc.h b/lib/fuzzer/FuzzerBuiltinsMsvc.h index 82709cfe7b40..bc65c60098be 100644 --- a/lib/fuzzer/FuzzerBuiltinsMsvc.h +++ b/lib/fuzzer/FuzzerBuiltinsMsvc.h @@ -15,9 +15,6 @@ #include "FuzzerDefs.h" #if LIBFUZZER_MSVC -#if !defined(_M_ARM) && !defined(_M_X64) -#error "_BitScanReverse64 unavailable on this platform so MSVC is unsupported." -#endif #include <intrin.h> #include <cstdint> #include <cstdlib> @@ -40,7 +37,18 @@ inline uint64_t Bswap(uint64_t x) { return _byteswap_uint64(x); } // outside of Windows. inline uint32_t Clzll(uint64_t X) { unsigned long LeadZeroIdx = 0; + +#if !defined(_M_ARM) && !defined(_M_X64) + // Scan the high 32 bits. + if (_BitScanReverse(&LeadZeroIdx, static_cast<unsigned long>(X >> 32))) + return static_cast<int>(63 - (LeadZeroIdx + 32)); // Create a bit offset from the MSB. + // Scan the low 32 bits. + if (_BitScanReverse(&LeadZeroIdx, static_cast<unsigned long>(X))) + return static_cast<int>(63 - LeadZeroIdx); + +#else if (_BitScanReverse64(&LeadZeroIdx, X)) return 63 - LeadZeroIdx; +#endif return 64; } @@ -50,7 +58,13 @@ inline uint32_t Clz(uint32_t X) { return 32; } -inline int Popcountll(unsigned long long X) { return __popcnt64(X); } +inline int Popcountll(unsigned long long X) { +#if !defined(_M_ARM) && !defined(_M_X64) + return __popcnt(X) + __popcnt(X >> 32); +#else + return __popcnt64(X); +#endif +} } // namespace fuzzer diff --git a/lib/fuzzer/FuzzerDefs.h b/lib/fuzzer/FuzzerDefs.h index 320b37d5f8e3..5dc2d8e1ac09 100644 --- a/lib/fuzzer/FuzzerDefs.h +++ b/lib/fuzzer/FuzzerDefs.h @@ -15,10 +15,11 @@ #include <cstddef> #include <cstdint> #include <cstring> +#include <memory> +#include <set> #include <string> #include <vector> -#include <set> -#include <memory> + // Platform detection. #ifdef __linux__ diff --git a/lib/fuzzer/FuzzerDriver.cpp b/lib/fuzzer/FuzzerDriver.cpp index 54c7ff079585..44c90655b932 100644 --- a/lib/fuzzer/FuzzerDriver.cpp +++ b/lib/fuzzer/FuzzerDriver.cpp @@ -708,7 +708,6 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.FeaturesDir = Flags.features_dir; if (Flags.collect_data_flow) Options.CollectDataFlow = Flags.collect_data_flow; - Options.LazyCounters = Flags.lazy_counters; if (Flags.stop_file) Options.StopFile = Flags.stop_file; diff --git a/lib/fuzzer/FuzzerExtFunctions.def b/lib/fuzzer/FuzzerExtFunctions.def index 41fa0fd2b748..51edf8444e94 100644 --- a/lib/fuzzer/FuzzerExtFunctions.def +++ b/lib/fuzzer/FuzzerExtFunctions.def @@ -16,12 +16,12 @@ // Optional user functions EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false); EXT_FUNC(LLVMFuzzerCustomMutator, size_t, - (uint8_t * Data, size_t Size, size_t MaxSize, unsigned int Seed), + (uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed), false); EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t, - (const uint8_t * Data1, size_t Size1, - const uint8_t * Data2, size_t Size2, - uint8_t * Out, size_t MaxOutSize, unsigned int Seed), + (const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize, unsigned int Seed), false); // Sanitizer functions @@ -33,8 +33,9 @@ EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int, (void (*malloc_hook)(const volatile void *, size_t), void (*free_hook)(const volatile void *)), false); +EXT_FUNC(__sanitizer_log_write, void, (const char *buf, size_t len), false); EXT_FUNC(__sanitizer_purge_allocator, void, (), false); -EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t, size_t), false); +EXT_FUNC(__sanitizer_print_memory_profile, void, (size_t, size_t), false); EXT_FUNC(__sanitizer_print_stack_trace, void, (), true); EXT_FUNC(__sanitizer_symbolize_pc, void, (void *, const char *fmt, char *out_buf, size_t out_buf_size), false); diff --git a/lib/fuzzer/FuzzerFlags.def b/lib/fuzzer/FuzzerFlags.def index a11cfe4405f3..0e19a9cde6ca 100644 --- a/lib/fuzzer/FuzzerFlags.def +++ b/lib/fuzzer/FuzzerFlags.def @@ -123,9 +123,6 @@ FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.") FUZZER_FLAG_INT(handle_xfsz, 1, "If 1, try to intercept SIGXFSZ.") FUZZER_FLAG_INT(handle_usr1, 1, "If 1, try to intercept SIGUSR1.") FUZZER_FLAG_INT(handle_usr2, 1, "If 1, try to intercept SIGUSR2.") -FUZZER_FLAG_INT(lazy_counters, 0, "If 1, a performance optimization is" - "enabled for the 8bit inline counters. " - "Requires that libFuzzer successfully installs its SEGV handler") FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; " "if 2, close stderr; if 3, close both. " "Be careful, this will also close e.g. stderr of asan.") diff --git a/lib/fuzzer/FuzzerInternal.h b/lib/fuzzer/FuzzerInternal.h index f2a4c437de38..31096ce804bc 100644 --- a/lib/fuzzer/FuzzerInternal.h +++ b/lib/fuzzer/FuzzerInternal.h @@ -98,7 +98,8 @@ private: void ReportNewCoverage(InputInfo *II, const Unit &U); void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size); void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix); - void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0); + void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0, + size_t Features = 0); void PrintStatusForNewUnit(const Unit &U, const char *Text); void CheckExitOnSrcPosOrItem(); diff --git a/lib/fuzzer/FuzzerLoop.cpp b/lib/fuzzer/FuzzerLoop.cpp index f773f9a13398..451a4c173167 100644 --- a/lib/fuzzer/FuzzerLoop.cpp +++ b/lib/fuzzer/FuzzerLoop.cpp @@ -273,9 +273,9 @@ void Fuzzer::InterruptCallback() { NO_SANITIZE_MEMORY void Fuzzer::AlarmCallback() { assert(Options.UnitTimeoutSec > 0); - // In Windows Alarm callback is executed by a different thread. + // In Windows and Fuchsia, Alarm callback is executed by a different thread. // NetBSD's current behavior needs this change too. -#if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD +#if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD && !LIBFUZZER_FUCHSIA if (!InFuzzingThread()) return; #endif @@ -319,14 +319,15 @@ void Fuzzer::RssLimitCallback() { _Exit(Options.OOMExitCode); // Stop right now. } -void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) { +void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units, + size_t Features) { size_t ExecPerSec = execPerSec(); if (!Options.Verbosity) return; Printf("#%zd\t%s", TotalNumberOfRuns, Where); if (size_t N = TPC.GetTotalPCCoverage()) Printf(" cov: %zd", N); - if (size_t N = Corpus.NumFeatures()) + if (size_t N = Features ? Features : Corpus.NumFeatures()) Printf(" ft: %zd", N); if (!Corpus.empty()) { Printf(" corp: %zd", Corpus.NumActiveUnits()); @@ -512,10 +513,12 @@ size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { } void Fuzzer::CrashOnOverwrittenData() { - Printf("==%d== ERROR: libFuzzer: fuzz target overwrites it's const input\n", + Printf("==%d== ERROR: libFuzzer: fuzz target overwrites its const input\n", GetPid()); + PrintStackTrace(); + Printf("SUMMARY: libFuzzer: overwrites-const-input\n"); DumpCurrentUnit("crash-"); - Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); _Exit(Options.ErrorExitCode); // Stop right now. } @@ -739,10 +742,6 @@ void Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) { uint8_t dummy = 0; ExecuteCallback(&dummy, 0); - // Protect lazy counters here, after the once-init code has been executed. - if (Options.LazyCounters) - TPC.ProtectLazyCounters(); - if (CorporaFiles.empty()) { Printf("INFO: A corpus is not provided, starting from an empty corpus\n"); Unit U({'\n'}); // Valid ASCII input. diff --git a/lib/fuzzer/FuzzerMerge.cpp b/lib/fuzzer/FuzzerMerge.cpp index 75b2b5d59b9c..e3ad8b3851e7 100644 --- a/lib/fuzzer/FuzzerMerge.cpp +++ b/lib/fuzzer/FuzzerMerge.cpp @@ -19,6 +19,7 @@ #include <iterator> #include <set> #include <sstream> +#include <unordered_set> namespace fuzzer { @@ -210,6 +211,9 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app); Set<size_t> AllFeatures; + auto PrintStatsWrapper = [this, &AllFeatures](const char* Where) { + this->PrintStats(Where, "\n", 0, AllFeatures.size()); + }; Set<const TracePC::PCTableEntry *> AllPCs; for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) { Fuzzer::MaybeExitGracefully(); @@ -218,7 +222,7 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { U.resize(MaxInputLen); U.shrink_to_fit(); } - std::ostringstream StartedLine; + // Write the pre-run marker. OF << "STARTED " << i << " " << U.size() << "\n"; OF.flush(); // Flush is important since Command::Execute may crash. @@ -238,7 +242,9 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { TPC.UpdateObservedPCs(); // Show stats. if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1))) - PrintStats("pulse "); + PrintStatsWrapper("pulse "); + if (TotalNumberOfRuns == M.NumFilesInFirstCorpus) + PrintStatsWrapper("LOADED"); // Write the post-run marker and the coverage. OF << "FT " << i; for (size_t F : UniqFeatures) @@ -252,25 +258,42 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { OF << "\n"; OF.flush(); } - PrintStats("DONE "); + PrintStatsWrapper("DONE "); } -static void WriteNewControlFile(const std::string &CFPath, - const Vector<SizedFile> &OldCorpus, - const Vector<SizedFile> &NewCorpus) { - RemoveFile(CFPath); - std::ofstream ControlFile(CFPath); - ControlFile << (OldCorpus.size() + NewCorpus.size()) << "\n"; - ControlFile << OldCorpus.size() << "\n"; +static size_t WriteNewControlFile(const std::string &CFPath, + const Vector<SizedFile> &OldCorpus, + const Vector<SizedFile> &NewCorpus, + const Vector<MergeFileInfo> &KnownFiles) { + std::unordered_set<std::string> FilesToSkip; + for (auto &SF: KnownFiles) + FilesToSkip.insert(SF.Name); + + Vector<std::string> FilesToUse; + auto MaybeUseFile = [=, &FilesToUse](std::string Name) { + if (FilesToSkip.find(Name) == FilesToSkip.end()) + FilesToUse.push_back(Name); + }; for (auto &SF: OldCorpus) - ControlFile << SF.File << "\n"; + MaybeUseFile(SF.File); + auto FilesToUseFromOldCorpus = FilesToUse.size(); for (auto &SF: NewCorpus) - ControlFile << SF.File << "\n"; + MaybeUseFile(SF.File); + + RemoveFile(CFPath); + std::ofstream ControlFile(CFPath); + ControlFile << FilesToUse.size() << "\n"; + ControlFile << FilesToUseFromOldCorpus << "\n"; + for (auto &FN: FilesToUse) + ControlFile << FN << "\n"; + if (!ControlFile) { Printf("MERGE-OUTER: failed to write to the control file: %s\n", CFPath.c_str()); exit(1); } + + return FilesToUse.size(); } // Outer process. Does not call the target code and thus should not fail. @@ -286,12 +309,13 @@ void CrashResistantMerge(const Vector<std::string> &Args, bool V /*Verbose*/) { if (NewCorpus.empty() && OldCorpus.empty()) return; // Nothing to merge. size_t NumAttempts = 0; + Vector<MergeFileInfo> KnownFiles; if (FileSize(CFPath)) { VPrintf(V, "MERGE-OUTER: non-empty control file provided: '%s'\n", CFPath.c_str()); Merger M; std::ifstream IF(CFPath); - if (M.Parse(IF, /*ParseCoverage=*/false)) { + if (M.Parse(IF, /*ParseCoverage=*/true)) { VPrintf(V, "MERGE-OUTER: control file ok, %zd files total," " first not processed file %zd\n", M.Files.size(), M.FirstNotProcessedFile); @@ -300,12 +324,25 @@ void CrashResistantMerge(const Vector<std::string> &Args, "(merge has stumbled on it the last time)\n", M.LastFailure.c_str()); if (M.FirstNotProcessedFile >= M.Files.size()) { + // Merge has already been completed with the given merge control file. + if (M.Files.size() == OldCorpus.size() + NewCorpus.size()) { + VPrintf( + V, + "MERGE-OUTER: nothing to do, merge has been completed before\n"); + exit(0); + } + + // Number of input files likely changed, start merge from scratch, but + // reuse coverage information from the given merge control file. VPrintf( - V, "MERGE-OUTER: nothing to do, merge has been completed before\n"); - exit(0); + V, + "MERGE-OUTER: starting merge from scratch, but reusing coverage " + "information from the given control file\n"); + KnownFiles = M.Files; + } else { + // There is a merge in progress, continue. + NumAttempts = M.Files.size() - M.FirstNotProcessedFile; } - - NumAttempts = M.Files.size() - M.FirstNotProcessedFile; } else { VPrintf(V, "MERGE-OUTER: bad control file, will overwrite it\n"); } @@ -313,10 +350,11 @@ void CrashResistantMerge(const Vector<std::string> &Args, if (!NumAttempts) { // The supplied control file is empty or bad, create a fresh one. - NumAttempts = OldCorpus.size() + NewCorpus.size(); - VPrintf(V, "MERGE-OUTER: %zd files, %zd in the initial corpus\n", - NumAttempts, OldCorpus.size()); - WriteNewControlFile(CFPath, OldCorpus, NewCorpus); + VPrintf(V, "MERGE-OUTER: " + "%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); } // Execute the inner process until it passes. @@ -353,6 +391,8 @@ void CrashResistantMerge(const Vector<std::string> &Args, VPrintf(V, "MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n", M.ApproximateMemoryConsumption() >> 20, GetPeakRSSMb()); + + M.Files.insert(M.Files.end(), KnownFiles.begin(), KnownFiles.end()); M.Merge(InitialFeatures, NewFeatures, InitialCov, NewCov, NewFiles); VPrintf(V, "MERGE-OUTER: %zd new files with %zd new features added; " "%zd new coverage edges\n", diff --git a/lib/fuzzer/FuzzerOptions.h b/lib/fuzzer/FuzzerOptions.h index ad3df015bc77..beecc980380b 100644 --- a/lib/fuzzer/FuzzerOptions.h +++ b/lib/fuzzer/FuzzerOptions.h @@ -75,7 +75,6 @@ struct FuzzingOptions { bool HandleXfsz = false; bool HandleUsr1 = false; bool HandleUsr2 = false; - bool LazyCounters = false; }; } // namespace fuzzer diff --git a/lib/fuzzer/FuzzerTracePC.cpp b/lib/fuzzer/FuzzerTracePC.cpp index 4a1308de5504..f03be7a39502 100644 --- a/lib/fuzzer/FuzzerTracePC.cpp +++ b/lib/fuzzer/FuzzerTracePC.cpp @@ -67,45 +67,6 @@ void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) { NumInline8bitCounters += M.Size(); } -// Mark all full page counter regions as PROT_NONE and set Enabled=false. -// The first time the instrumented code hits such a protected/disabled -// counter region we should catch a SEGV and call UnprotectLazyCounters, -// which will mark the page as PROT_READ|PROT_WRITE and set Enabled=true. -// -// Whenever other functions iterate over the counters they should ignore -// regions with Enabled=false. -void TracePC::ProtectLazyCounters() { - size_t NumPagesProtected = 0; - IterateCounterRegions([&](Module::Region &R) { - if (!R.OneFullPage) return; - if (Mprotect(R.Start, R.Stop - R.Start, false)) { - R.Enabled = false; - NumPagesProtected++; - } - }); - if (NumPagesProtected) - Printf("INFO: %zd pages of counters where protected;" - " libFuzzer's SEGV handler must be installed\n", - NumPagesProtected); -} - -bool TracePC::UnprotectLazyCounters(void *CounterPtr) { - // Printf("UnprotectLazyCounters: %p\n", CounterPtr); - if (!CounterPtr) - return false; - bool Done = false; - uint8_t *Addr = reinterpret_cast<uint8_t *>(CounterPtr); - IterateCounterRegions([&](Module::Region &R) { - if (!R.OneFullPage || R.Enabled || Done) return; - if (Addr >= R.Start && Addr < R.Stop) - if (Mprotect(R.Start, R.Stop - R.Start, true)) { - R.Enabled = true; - Done = true; - } - }); - return Done; -} - void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) { const PCTableEntry *B = reinterpret_cast<const PCTableEntry *>(Start); const PCTableEntry *E = reinterpret_cast<const PCTableEntry *>(Stop); @@ -173,7 +134,7 @@ inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) { } /// \return the address of the next instruction. -/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.cc` +/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.cpp` ALWAYS_INLINE uintptr_t TracePC::GetNextInstructionPc(uintptr_t PC) { #if defined(__mips__) return PC + 8; diff --git a/lib/fuzzer/FuzzerTracePC.h b/lib/fuzzer/FuzzerTracePC.h index 4f5ebeb047a1..501f3b544971 100644 --- a/lib/fuzzer/FuzzerTracePC.h +++ b/lib/fuzzer/FuzzerTracePC.h @@ -119,9 +119,6 @@ class TracePC { void SetFocusFunction(const std::string &FuncName); bool ObservedFocusFunction(); - void ProtectLazyCounters(); - bool UnprotectLazyCounters(void *CounterPtr); - struct PCTableEntry { uintptr_t PC, PCFlags; }; diff --git a/lib/fuzzer/FuzzerUtil.h b/lib/fuzzer/FuzzerUtil.h index 0a127911df3c..85c5571d684f 100644 --- a/lib/fuzzer/FuzzerUtil.h +++ b/lib/fuzzer/FuzzerUtil.h @@ -52,8 +52,6 @@ void SetSignalHandler(const FuzzingOptions& Options); void SleepSeconds(int Seconds); -bool Mprotect(void *Ptr, size_t Size, bool AllowReadWrite); - unsigned long GetPid(); size_t GetPeakRSSMb(); diff --git a/lib/fuzzer/FuzzerUtilFuchsia.cpp b/lib/fuzzer/FuzzerUtilFuchsia.cpp index 1f04b33c154e..79fd950bbf97 100644 --- a/lib/fuzzer/FuzzerUtilFuchsia.cpp +++ b/lib/fuzzer/FuzzerUtilFuchsia.cpp @@ -305,12 +305,19 @@ void CrashHandler(zx_handle_t *Event) { } // namespace -bool Mprotect(void *Ptr, size_t Size, bool AllowReadWrite) { - return false; // UNIMPLEMENTED -} - // Platform specific functions. void SetSignalHandler(const FuzzingOptions &Options) { + // Make sure information from libFuzzer and the sanitizers are easy to + // reassemble. `__sanitizer_log_write` has the added benefit of ensuring the + // DSO map is always available for the symbolizer. + // A uint64_t fits in 20 chars, so 64 is plenty. + char Buf[64]; + memset(Buf, 0, sizeof(Buf)); + snprintf(Buf, sizeof(Buf), "==%lu== INFO: libFuzzer starting.\n", GetPid()); + if (EF->__sanitizer_log_write) + __sanitizer_log_write(Buf, sizeof(Buf)); + Printf("%s", Buf); + // Set up alarm handler if needed. if (Options.UnitTimeoutSec > 0) { std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1); @@ -400,13 +407,14 @@ int ExecuteCommand(const Command &Cmd) { // that lacks a mutable working directory. Fortunately, when this is the case // a mutable output directory must be specified using "-artifact_prefix=...", // so write the log file(s) there. + // However, we don't want to apply this logic for absolute paths. int FdOut = STDOUT_FILENO; if (Cmd.hasOutputFile()) { - std::string Path; - if (Cmd.hasFlag("artifact_prefix")) - Path = Cmd.getFlagValue("artifact_prefix") + "/" + Cmd.getOutputFile(); - else - Path = Cmd.getOutputFile(); + std::string Path = Cmd.getOutputFile(); + bool IsAbsolutePath = Path.length() > 1 && Path[0] == '/'; + if (!IsAbsolutePath && Cmd.hasFlag("artifact_prefix")) + Path = Cmd.getFlagValue("artifact_prefix") + "/" + Path; + FdOut = open(Path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0); if (FdOut == -1) { Printf("libFuzzer: failed to open %s: %s\n", Path.c_str(), diff --git a/lib/fuzzer/FuzzerUtilPosix.cpp b/lib/fuzzer/FuzzerUtilPosix.cpp index 110785d87413..cefe7ae181e7 100644 --- a/lib/fuzzer/FuzzerUtilPosix.cpp +++ b/lib/fuzzer/FuzzerUtilPosix.cpp @@ -37,7 +37,6 @@ static void (*upstream_segv_handler)(int, siginfo_t *, void *); static void SegvHandler(int sig, siginfo_t *si, void *ucontext) { assert(si->si_signo == SIGSEGV); - if (TPC.UnprotectLazyCounters(si->si_addr)) return; if (upstream_segv_handler) return upstream_segv_handler(sig, si, ucontext); Fuzzer::StaticCrashSignalCallback(); @@ -98,11 +97,6 @@ void SetTimer(int Seconds) { SetSigaction(SIGALRM, AlarmHandler); } -bool Mprotect(void *Ptr, size_t Size, bool AllowReadWrite) { - return 0 == mprotect(Ptr, Size, - AllowReadWrite ? (PROT_READ | PROT_WRITE) : PROT_NONE); -} - void SetSignalHandler(const FuzzingOptions& Options) { if (Options.UnitTimeoutSec > 0) SetTimer(Options.UnitTimeoutSec / 2 + 1); diff --git a/lib/fuzzer/FuzzerUtilWindows.cpp b/lib/fuzzer/FuzzerUtilWindows.cpp index 074e1eb42309..ed90044c3f83 100644 --- a/lib/fuzzer/FuzzerUtilWindows.cpp +++ b/lib/fuzzer/FuzzerUtilWindows.cpp @@ -111,10 +111,6 @@ static TimerQ Timer; static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); } -bool Mprotect(void *Ptr, size_t Size, bool AllowReadWrite) { - return false; // UNIMPLEMENTED -} - void SetSignalHandler(const FuzzingOptions& Options) { HandlerOpt = &Options; diff --git a/lib/fuzzer/utils/FuzzedDataProvider.h b/lib/fuzzer/utils/FuzzedDataProvider.h deleted file mode 100644 index 1b5b4bb01269..000000000000 --- a/lib/fuzzer/utils/FuzzedDataProvider.h +++ /dev/null @@ -1,245 +0,0 @@ -//===- FuzzedDataProvider.h - Utility header for fuzz targets ---*- C++ -* ===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// A single header library providing an utility class to break up an array of -// bytes. Whenever run on the same input, provides the same output, as long as -// its methods are called in the same order, with the same arguments. -//===----------------------------------------------------------------------===// - -#ifndef LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ -#define LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ - -#include <limits.h> -#include <stddef.h> -#include <stdint.h> - -#include <algorithm> -#include <cstring> -#include <initializer_list> -#include <string> -#include <type_traits> -#include <utility> -#include <vector> - -class FuzzedDataProvider { -public: - // |data| is an array of length |size| that the FuzzedDataProvider wraps to - // provide more granular access. |data| must outlive the FuzzedDataProvider. - FuzzedDataProvider(const uint8_t *data, size_t size) - : data_ptr_(data), remaining_bytes_(size) {} - ~FuzzedDataProvider() = default; - - // Returns a std::vector containing |num_bytes| of input data. If fewer than - // |num_bytes| of data remain, returns a shorter std::vector containing all - // of the data that's left. Can be used with any byte sized type, such as - // char, unsigned char, uint8_t, etc. - template <typename T> std::vector<T> ConsumeBytes(size_t num_bytes) { - num_bytes = std::min(num_bytes, remaining_bytes_); - return ConsumeBytes<T>(num_bytes, num_bytes); - } - - // Similar to |ConsumeBytes|, but also appends the terminator value at the end - // of the resulting vector. Useful, when a mutable null-terminated C-string is - // needed, for example. But that is a rare case. Better avoid it, if possible, - // and prefer using |ConsumeBytes| or |ConsumeBytesAsString| methods. - template <typename T> - std::vector<T> ConsumeBytesWithTerminator(size_t num_bytes, - T terminator = 0) { - num_bytes = std::min(num_bytes, remaining_bytes_); - std::vector<T> result = ConsumeBytes<T>(num_bytes + 1, num_bytes); - result.back() = terminator; - return result; - } - - // Returns a std::string containing |num_bytes| of input data. Using this and - // |.c_str()| on the resulting string is the best way to get an immutable - // null-terminated C string. If fewer than |num_bytes| of data remain, returns - // a shorter std::string containing all of the data that's left. - std::string ConsumeBytesAsString(size_t num_bytes) { - static_assert(sizeof(std::string::value_type) == sizeof(uint8_t), - "ConsumeBytesAsString cannot convert the data to a string."); - - num_bytes = std::min(num_bytes, remaining_bytes_); - std::string result( - reinterpret_cast<const std::string::value_type *>(data_ptr_), - num_bytes); - Advance(num_bytes); - return result; - } - - // Returns a number in the range [min, max] by consuming bytes from the - // input data. The value might not be uniformly distributed in the given - // range. If there's no input data left, always returns |min|. |min| must - // be less than or equal to |max|. - template <typename T> T ConsumeIntegralInRange(T min, T max) { - static_assert(std::is_integral<T>::value, "An integral type is required."); - static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type."); - - if (min > max) - abort(); - - // Use the biggest type possible to hold the range and the result. - uint64_t range = static_cast<uint64_t>(max) - min; - uint64_t result = 0; - size_t offset = 0; - - while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 && - remaining_bytes_ != 0) { - // Pull bytes off the end of the seed data. Experimentally, this seems to - // allow the fuzzer to more easily explore the input space. This makes - // sense, since it works by modifying inputs that caused new code to run, - // and this data is often used to encode length of data read by - // |ConsumeBytes|. Separating out read lengths makes it easier modify the - // contents of the data that is actually read. - --remaining_bytes_; - result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_]; - offset += CHAR_BIT; - } - - // Avoid division by 0, in case |range + 1| results in overflow. - if (range != std::numeric_limits<decltype(range)>::max()) - result = result % (range + 1); - - return static_cast<T>(min + result); - } - - // Returns a std::string of length from 0 to |max_length|. When it runs out of - // input data, returns what remains of the input. Designed to be more stable - // with respect to a fuzzer inserting characters than just picking a random - // length and then consuming that many bytes with |ConsumeBytes|. - std::string ConsumeRandomLengthString(size_t max_length) { - // Reads bytes from the start of |data_ptr_|. Maps "\\" to "\", and maps "\" - // followed by anything else to the end of the string. As a result of this - // logic, a fuzzer can insert characters into the string, and the string - // will be lengthened to include those new characters, resulting in a more - // stable fuzzer than picking the length of a string independently from - // picking its contents. - std::string result; - - // Reserve the anticipated capaticity to prevent several reallocations. - result.reserve(std::min(max_length, remaining_bytes_)); - for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) { - char next = ConvertUnsignedToSigned<char>(data_ptr_[0]); - Advance(1); - if (next == '\\' && remaining_bytes_ != 0) { - next = ConvertUnsignedToSigned<char>(data_ptr_[0]); - Advance(1); - if (next != '\\') - break; - } - result += next; - } - - result.shrink_to_fit(); - return result; - } - - // Returns a std::vector containing all remaining bytes of the input data. - template <typename T> std::vector<T> ConsumeRemainingBytes() { - return ConsumeBytes<T>(remaining_bytes_); - } - - // Prefer using |ConsumeRemainingBytes| unless you actually need a std::string - // object. - // Returns a std::vector containing all remaining bytes of the input data. - std::string ConsumeRemainingBytesAsString() { - return ConsumeBytesAsString(remaining_bytes_); - } - - // Returns a number in the range [Type's min, Type's max]. The value might - // not be uniformly distributed in the given range. If there's no input data - // left, always returns |min|. - template <typename T> T ConsumeIntegral() { - return ConsumeIntegralInRange(std::numeric_limits<T>::min(), - std::numeric_limits<T>::max()); - } - - // Reads one byte and returns a bool, or false when no data remains. - bool ConsumeBool() { return 1 & ConsumeIntegral<uint8_t>(); } - - // Returns a copy of a value selected from a fixed-size |array|. - template <typename T, size_t size> - T PickValueInArray(const T (&array)[size]) { - static_assert(size > 0, "The array must be non empty."); - return array[ConsumeIntegralInRange<size_t>(0, size - 1)]; - } - - template <typename T> - T PickValueInArray(std::initializer_list<const T> list) { - // static_assert(list.size() > 0, "The array must be non empty."); - return *(list.begin() + ConsumeIntegralInRange<size_t>(0, list.size() - 1)); - } - - // Return an enum value. The enum must start at 0 and be contiguous. It must - // also contain |kMaxValue| aliased to its largest (inclusive) value. Such as: - // enum class Foo { SomeValue, OtherValue, kMaxValue = OtherValue }; - template <typename T> T ConsumeEnum() { - static_assert(std::is_enum<T>::value, "|T| must be an enum type."); - return static_cast<T>(ConsumeIntegralInRange<uint32_t>( - 0, static_cast<uint32_t>(T::kMaxValue))); - } - - // Reports the remaining bytes available for fuzzed input. - size_t remaining_bytes() { return remaining_bytes_; } - -private: - FuzzedDataProvider(const FuzzedDataProvider &) = delete; - FuzzedDataProvider &operator=(const FuzzedDataProvider &) = delete; - - void Advance(size_t num_bytes) { - if (num_bytes > remaining_bytes_) - abort(); - - data_ptr_ += num_bytes; - remaining_bytes_ -= num_bytes; - } - - template <typename T> - std::vector<T> ConsumeBytes(size_t size, size_t num_bytes_to_consume) { - static_assert(sizeof(T) == sizeof(uint8_t), "Incompatible data type."); - - // The point of using the size-based constructor below is to increase the - // odds of having a vector object with capacity being equal to the length. - // That part is always implementation specific, but at least both libc++ and - // libstdc++ allocate the requested number of bytes in that constructor, - // which seems to be a natural choice for other implementations as well. - // To increase the odds even more, we also call |shrink_to_fit| below. - std::vector<T> result(size); - std::memcpy(result.data(), data_ptr_, num_bytes_to_consume); - Advance(num_bytes_to_consume); - - // Even though |shrink_to_fit| is also implementation specific, we expect it - // to provide an additional assurance in case vector's constructor allocated - // a buffer which is larger than the actual amount of data we put inside it. - result.shrink_to_fit(); - return result; - } - - template <typename TS, typename TU> TS ConvertUnsignedToSigned(TU value) { - static_assert(sizeof(TS) == sizeof(TU), "Incompatible data types."); - static_assert(!std::numeric_limits<TU>::is_signed, - "Source type must be unsigned."); - - // TODO(Dor1s): change to `if constexpr` once C++17 becomes mainstream. - if (std::numeric_limits<TS>::is_modulo) - return static_cast<TS>(value); - - // Avoid using implementation-defined unsigned to signer conversions. - // To learn more, see https://stackoverflow.com/questions/13150449. - if (value <= std::numeric_limits<TS>::max()) - return static_cast<TS>(value); - else { - constexpr auto TS_min = std::numeric_limits<TS>::min(); - return TS_min + static_cast<char>(value - TS_min); - } - } - - const uint8_t *data_ptr_; - size_t remaining_bytes_; -}; - -#endif // LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ |