tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 1e0691112bac90a0b89f5539e9bfa5bc6b17bf80
parent d3884e1265fb5719ea1e6e86f74b753d492d0085
Author: pyoor <pyoor@users.noreply.github.com>
Date:   Tue,  4 Nov 2025 14:02:02 +0000

Bug 1996685 - Upgrade in-tree libFuzzer to latest version. r=truber

Differential Revision: https://phabricator.services.mozilla.com/D270669

Diffstat:
Atools/fuzzing/libfuzzer/FuzzedDataProvider.h | 397+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/fuzzing/libfuzzer/FuzzerBuiltins.h | 1-
Mtools/fuzzing/libfuzzer/FuzzerBuiltinsMsvc.h | 9++-------
Mtools/fuzzing/libfuzzer/FuzzerCommand.h | 15++++++++-------
Mtools/fuzzing/libfuzzer/FuzzerCorpus.h | 140++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mtools/fuzzing/libfuzzer/FuzzerDataFlowTrace.cpp | 69++++++++++++++++++++++++++++++---------------------------------------
Mtools/fuzzing/libfuzzer/FuzzerDataFlowTrace.h | 34++++++++++++++++++----------------
Mtools/fuzzing/libfuzzer/FuzzerDefs.h | 24++----------------------
Mtools/fuzzing/libfuzzer/FuzzerDictionary.h | 23+++++++++++++++--------
Mtools/fuzzing/libfuzzer/FuzzerDriver.cpp | 266+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mtools/fuzzing/libfuzzer/FuzzerExtFunctionsWeak.cpp | 2+-
Mtools/fuzzing/libfuzzer/FuzzerExtFunctionsWindows.cpp | 31++++++++++++++++---------------
Mtools/fuzzing/libfuzzer/FuzzerExtraCounters.cpp | 10+---------
Atools/fuzzing/libfuzzer/FuzzerExtraCountersDarwin.cpp | 22++++++++++++++++++++++
Atools/fuzzing/libfuzzer/FuzzerExtraCountersWindows.cpp | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/fuzzing/libfuzzer/FuzzerFlags.def | 54+++++++++++++++++++++++++++++++++++++++++++++++++-----
Mtools/fuzzing/libfuzzer/FuzzerFork.cpp | 179++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mtools/fuzzing/libfuzzer/FuzzerFork.h | 6+++---
Mtools/fuzzing/libfuzzer/FuzzerIO.cpp | 88++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mtools/fuzzing/libfuzzer/FuzzerIO.h | 22+++++++++++++++++-----
Mtools/fuzzing/libfuzzer/FuzzerIOPosix.cpp | 24++++++++++++------------
Mtools/fuzzing/libfuzzer/FuzzerIOWindows.cpp | 36+++++++++++++++++++++---------------
Mtools/fuzzing/libfuzzer/FuzzerInterceptors.cpp | 21++++++++++++++++++++-
Mtools/fuzzing/libfuzzer/FuzzerInternal.h | 35+++++++++++++++++++----------------
Mtools/fuzzing/libfuzzer/FuzzerLoop.cpp | 212++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mtools/fuzzing/libfuzzer/FuzzerMerge.cpp | 268+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mtools/fuzzing/libfuzzer/FuzzerMerge.h | 40+++++++++++++++++++++++-----------------
Mtools/fuzzing/libfuzzer/FuzzerMutate.cpp | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mtools/fuzzing/libfuzzer/FuzzerMutate.h | 32++++++++++++++++----------------
Mtools/fuzzing/libfuzzer/FuzzerOptions.h | 11++++++++++-
Mtools/fuzzing/libfuzzer/FuzzerPlatform.h | 18+-----------------
Mtools/fuzzing/libfuzzer/FuzzerRandom.h | 24+++++++++++++++++-------
Mtools/fuzzing/libfuzzer/FuzzerSHA1.cpp | 9+++++----
Mtools/fuzzing/libfuzzer/FuzzerTracePC.cpp | 77++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mtools/fuzzing/libfuzzer/FuzzerTracePC.h | 49+++++++++++++++++++++++++++++--------------------
Mtools/fuzzing/libfuzzer/FuzzerUtil.cpp | 13+++++++------
Mtools/fuzzing/libfuzzer/FuzzerUtil.h | 21++++++++++++++++-----
Mtools/fuzzing/libfuzzer/FuzzerUtilDarwin.cpp | 5+++++
Mtools/fuzzing/libfuzzer/FuzzerUtilFuchsia.cpp | 302+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mtools/fuzzing/libfuzzer/FuzzerUtilLinux.cpp | 14+++++++++++++-
Mtools/fuzzing/libfuzzer/FuzzerUtilPosix.cpp | 22++++++++++++++++------
Mtools/fuzzing/libfuzzer/FuzzerUtilWindows.cpp | 57+++++++++++++++++++++++++++++++++++++++++++++++++++------
Mtools/fuzzing/libfuzzer/moz.build | 89+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mtools/fuzzing/libfuzzer/moz.yaml | 9++++++---
Mtools/fuzzing/libfuzzer/patches/10-ef-runtime.patch | 25+++++++------------------
Mtools/fuzzing/libfuzzer/patches/11-callback-rv.patch | 122++++++++-----------------------------------------------------------------------
Mtools/fuzzing/libfuzzer/patches/12-custom-mutator-fail.patch | 37++++++++-----------------------------
Mtools/fuzzing/libfuzzer/patches/13-unused-write.patch | 82+++++++------------------------------------------------------------------------
Dtools/fuzzing/libfuzzer/patches/14-explicit-allocator.patch | 30------------------------------
Mtools/fuzzing/libfuzzer/patches/15-return-to-exit.patch | 440+++++++++++++++++++++++++++++++++++++------------------------------------------
Mtools/fuzzing/moz.build | 8+++-----
51 files changed, 2345 insertions(+), 1361 deletions(-)

diff --git a/tools/fuzzing/libfuzzer/FuzzedDataProvider.h b/tools/fuzzing/libfuzzer/FuzzedDataProvider.h @@ -0,0 +1,397 @@ +//===- 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 <algorithm> +#include <array> +#include <climits> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <initializer_list> +#include <limits> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +// In addition to the comments below, the API is also briefly documented at +// https://github.com/google/fuzzing/blob/master/docs/split-inputs.md#fuzzed-data-provider +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; + + // See the implementation below (after the class definition) for more verbose + // comments for each of the methods. + + // Methods returning std::vector of bytes. These are the most popular choice + // when splitting fuzzing input into pieces, as every piece is put into a + // separate buffer (i.e. ASan would catch any under-/overflow) and the memory + // will be released automatically. + template <typename T> std::vector<T> ConsumeBytes(size_t num_bytes); + template <typename T> + std::vector<T> ConsumeBytesWithTerminator(size_t num_bytes, T terminator = 0); + template <typename T> std::vector<T> ConsumeRemainingBytes(); + + // Methods returning strings. Use only when you need a std::string or a null + // terminated C-string. Otherwise, prefer the methods returning std::vector. + std::string ConsumeBytesAsString(size_t num_bytes); + std::string ConsumeRandomLengthString(size_t max_length); + std::string ConsumeRandomLengthString(); + std::string ConsumeRemainingBytesAsString(); + + // Methods returning integer values. + template <typename T> T ConsumeIntegral(); + template <typename T> T ConsumeIntegralInRange(T min, T max); + + // Methods returning floating point values. + template <typename T> T ConsumeFloatingPoint(); + template <typename T> T ConsumeFloatingPointInRange(T min, T max); + + // 0 <= return value <= 1. + template <typename T> T ConsumeProbability(); + + bool ConsumeBool(); + + // Returns a value chosen from the given enum. + template <typename T> T ConsumeEnum(); + + // Returns a value from the given array. + template <typename T, size_t size> T PickValueInArray(const T (&array)[size]); + template <typename T, size_t size> + T PickValueInArray(const std::array<T, size> &array); + template <typename T> T PickValueInArray(std::initializer_list<const T> list); + + // Writes data to the given destination and returns number of bytes written. + size_t ConsumeData(void *destination, size_t num_bytes); + + // 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 CopyAndAdvance(void *destination, size_t num_bytes); + + void Advance(size_t num_bytes); + + template <typename T> + std::vector<T> ConsumeBytes(size_t size, size_t num_bytes); + + template <typename TS, typename TU> TS ConvertUnsignedToSigned(TU value); + + const uint8_t *data_ptr_; + size_t remaining_bytes_; +}; + +// 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> FuzzedDataProvider::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> FuzzedDataProvider::ConsumeBytesWithTerminator(size_t num_bytes, + T terminator) { + 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::vector containing all remaining bytes of the input data. +template <typename T> +std::vector<T> FuzzedDataProvider::ConsumeRemainingBytes() { + return ConsumeBytes<T>(remaining_bytes_); +} + +// 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. +inline std::string FuzzedDataProvider::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 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|. +inline std::string +FuzzedDataProvider::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 capacity 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::string of length from 0 to |remaining_bytes_|. +inline std::string FuzzedDataProvider::ConsumeRandomLengthString() { + return ConsumeRandomLengthString(remaining_bytes_); +} + +// Returns a std::string containing all remaining bytes of the input data. +// Prefer using |ConsumeRemainingBytes| unless you actually need a std::string +// object. +inline std::string FuzzedDataProvider::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 FuzzedDataProvider::ConsumeIntegral() { + return ConsumeIntegralInRange(std::numeric_limits<T>::min(), + std::numeric_limits<T>::max()); +} + +// 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 FuzzedDataProvider::ConsumeIntegralInRange(T min, T max) { + static_assert(std::is_integral_v<T>, "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) - static_cast<uint64_t>(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>(static_cast<uint64_t>(min) + result); +} + +// Returns a floating point value in the range [Type's lowest, Type's max] by +// consuming bytes from the input data. If there's no input data left, always +// returns approximately 0. +template <typename T> T FuzzedDataProvider::ConsumeFloatingPoint() { + return ConsumeFloatingPointInRange<T>(std::numeric_limits<T>::lowest(), + std::numeric_limits<T>::max()); +} + +// Returns a floating point value in the given range by consuming bytes from +// the input data. If there's no input data left, returns |min|. Note that +// |min| must be less than or equal to |max|. +template <typename T> +T FuzzedDataProvider::ConsumeFloatingPointInRange(T min, T max) { + if (min > max) + abort(); + + T range = .0; + T result = min; + constexpr T zero(.0); + if (max > zero && min < zero && max > min + std::numeric_limits<T>::max()) { + // The diff |max - min| would overflow the given floating point type. Use + // the half of the diff as the range and consume a bool to decide whether + // the result is in the first of the second part of the diff. + range = (max / 2.0) - (min / 2.0); + if (ConsumeBool()) { + result += range; + } + } else { + range = max - min; + } + + return result + range * ConsumeProbability<T>(); +} + +// Returns a floating point number in the range [0.0, 1.0]. If there's no +// input data left, always returns 0. +template <typename T> T FuzzedDataProvider::ConsumeProbability() { + static_assert(std::is_floating_point_v<T>, + "A floating point type is required."); + + // Use different integral types for different floating point types in order + // to provide better density of the resulting values. + using IntegralType = + typename std::conditional_t<(sizeof(T) <= sizeof(uint32_t)), uint32_t, + uint64_t>; + + T result = static_cast<T>(ConsumeIntegral<IntegralType>()); + result /= static_cast<T>(std::numeric_limits<IntegralType>::max()); + return result; +} + +// Reads one byte and returns a bool, or false when no data remains. +inline bool FuzzedDataProvider::ConsumeBool() { + return 1 & ConsumeIntegral<uint8_t>(); +} + +// Returns 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 FuzzedDataProvider::ConsumeEnum() { + static_assert(std::is_enum_v<T>, "|T| must be an enum type."); + return static_cast<T>( + ConsumeIntegralInRange<uint32_t>(0, static_cast<uint32_t>(T::kMaxValue))); +} + +// Returns a copy of the value selected from the given fixed-size |array|. +template <typename T, size_t size> +T FuzzedDataProvider::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, size_t size> +T FuzzedDataProvider::PickValueInArray(const std::array<T, size> &array) { + static_assert(size > 0, "The array must be non empty."); + return array[ConsumeIntegralInRange<size_t>(0, size - 1)]; +} + +template <typename T> +T FuzzedDataProvider::PickValueInArray(std::initializer_list<const T> list) { + if (!list.size()) + abort(); + + return *(list.begin() + ConsumeIntegralInRange<size_t>(0, list.size() - 1)); +} + +// Writes |num_bytes| of input data to the given destination pointer. If there +// is not enough data left, writes all remaining bytes. Return value is the +// number of bytes written. +// In general, it's better to avoid using this function, but it may be useful +// in cases when it's necessary to fill a certain buffer or object with +// fuzzing data. +inline size_t FuzzedDataProvider::ConsumeData(void *destination, + size_t num_bytes) { + num_bytes = std::min(num_bytes, remaining_bytes_); + CopyAndAdvance(destination, num_bytes); + return num_bytes; +} + +// Private methods. +inline void FuzzedDataProvider::CopyAndAdvance(void *destination, + size_t num_bytes) { + std::memcpy(destination, data_ptr_, num_bytes); + Advance(num_bytes); +} + +inline void FuzzedDataProvider::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> FuzzedDataProvider::ConsumeBytes(size_t size, size_t num_bytes) { + 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); + if (size == 0) { + if (num_bytes != 0) + abort(); + return result; + } + + CopyAndAdvance(result.data(), num_bytes); + + // 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 FuzzedDataProvider::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 signed 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<TS>(value - TS_min); + } +} + +#endif // LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ diff --git a/tools/fuzzing/libfuzzer/FuzzerBuiltins.h b/tools/fuzzing/libfuzzer/FuzzerBuiltins.h @@ -26,7 +26,6 @@ inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); } inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); } inline uint32_t Clzll(unsigned long long X) { return __builtin_clzll(X); } -inline uint32_t Clz(unsigned long long X) { return __builtin_clz(X); } inline int Popcountll(unsigned long long X) { return __builtin_popcountll(X); } } // namespace fuzzer diff --git a/tools/fuzzing/libfuzzer/FuzzerBuiltinsMsvc.h b/tools/fuzzing/libfuzzer/FuzzerBuiltinsMsvc.h @@ -41,7 +41,8 @@ inline uint32_t Clzll(uint64_t X) { #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. + 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); @@ -52,12 +53,6 @@ inline uint32_t Clzll(uint64_t X) { return 64; } -inline uint32_t Clz(uint32_t X) { - unsigned long LeadZeroIdx = 0; - if (_BitScanReverse(&LeadZeroIdx, X)) return 31 - LeadZeroIdx; - return 32; -} - inline int Popcountll(unsigned long long X) { #if !defined(_M_ARM) && !defined(_M_X64) return __popcnt(X) + __popcnt(X >> 32); diff --git a/tools/fuzzing/libfuzzer/FuzzerCommand.h b/tools/fuzzing/libfuzzer/FuzzerCommand.h @@ -19,6 +19,7 @@ #include <sstream> #include <string> #include <vector> +#include <thread> namespace fuzzer { @@ -33,7 +34,7 @@ public: Command() : CombinedOutAndErr(false) {} - explicit Command(const Vector<std::string> &ArgsToAdd) + explicit Command(const std::vector<std::string> &ArgsToAdd) : Args(ArgsToAdd), CombinedOutAndErr(false) {} explicit Command(const Command &Other) @@ -58,7 +59,7 @@ public: // Gets all of the current command line arguments, **including** those after // "-ignore-remaining-args=1". - const Vector<std::string> &getArguments() const { return Args; } + const std::vector<std::string> &getArguments() const { return Args; } // Adds the given argument before "-ignore_remaining_args=1", or at the end // if that flag isn't present. @@ -68,7 +69,7 @@ public: // Adds all given arguments before "-ignore_remaining_args=1", or at the end // if that flag isn't present. - void addArguments(const Vector<std::string> &ArgsToAdd) { + void addArguments(const std::vector<std::string> &ArgsToAdd) { Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end()); } @@ -139,7 +140,7 @@ public: // be the equivalent command line. std::string toString() const { std::stringstream SS; - for (auto arg : getArguments()) + for (const auto &arg : getArguments()) SS << arg << " "; if (hasOutputFile()) SS << ">" << getOutputFile() << " "; @@ -155,16 +156,16 @@ private: Command(Command &&Other) = delete; Command &operator=(Command &&Other) = delete; - Vector<std::string>::iterator endMutableArgs() { + std::vector<std::string>::iterator endMutableArgs() { return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); } - Vector<std::string>::const_iterator endMutableArgs() const { + std::vector<std::string>::const_iterator endMutableArgs() const { return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); } // The command arguments. Args[0] is the command name. - Vector<std::string> Args; + std::vector<std::string> Args; // True indicates stderr is redirected to stdout. bool CombinedOutAndErr; diff --git a/tools/fuzzing/libfuzzer/FuzzerCorpus.h b/tools/fuzzing/libfuzzer/FuzzerCorpus.h @@ -18,6 +18,8 @@ #include "FuzzerSHA1.h" #include "FuzzerTracePC.h" #include <algorithm> +#include <bitset> +#include <chrono> #include <numeric> #include <random> #include <unordered_set> @@ -26,23 +28,25 @@ namespace fuzzer { struct InputInfo { Unit U; // The actual input data. + std::chrono::microseconds TimeOfUnit; uint8_t Sha1[kSHA1NumBytes]; // Checksum. // Number of features that this input has and no smaller input has. size_t NumFeatures = 0; size_t Tmp = 0; // Used by ValidateFeatureSet. // Stats. size_t NumExecutedMutations = 0; - size_t NumSuccessfullMutations = 0; + size_t NumSuccessfulMutations = 0; + bool NeverReduce = false; bool MayDeleteFile = false; bool Reduced = false; bool HasFocusFunction = false; - Vector<uint32_t> UniqFeatureSet; - Vector<uint8_t> DataFlowTraceForFocusFunction; + std::vector<uint32_t> UniqFeatureSet; + std::vector<uint8_t> DataFlowTraceForFocusFunction; // Power schedule. bool NeedsEnergyUpdate = false; double Energy = 0.0; - size_t SumIncidence = 0; - Vector<std::pair<uint32_t, uint16_t>> FeatureFreqs; + double SumIncidence = 0.0; + std::vector<std::pair<uint32_t, uint16_t>> FeatureFreqs; // Delete feature Idx and its frequency from FeatureFreqs. bool DeleteFeatureFreq(uint32_t Idx) { @@ -61,33 +65,59 @@ struct InputInfo { } // Assign more energy to a high-entropy seed, i.e., that reveals more - // information about the globally rare features in the neighborhood - // of the seed. Since we do not know the entropy of a seed that has - // never been executed we assign fresh seeds maximum entropy and - // let II->Energy approach the true entropy from above. - void UpdateEnergy(size_t GlobalNumberOfFeatures) { + // information about the globally rare features in the neighborhood of the + // seed. Since we do not know the entropy of a seed that has never been + // executed we assign fresh seeds maximum entropy and let II->Energy approach + // the true entropy from above. If ScalePerExecTime is true, the computed + // entropy is scaled based on how fast this input executes compared to the + // average execution time of inputs. The faster an input executes, the more + // energy gets assigned to the input. + void UpdateEnergy(size_t GlobalNumberOfFeatures, bool ScalePerExecTime, + std::chrono::microseconds AverageUnitExecutionTime) { Energy = 0.0; - SumIncidence = 0; + SumIncidence = 0.0; // Apply add-one smoothing to locally discovered features. - for (auto F : FeatureFreqs) { - size_t LocalIncidence = F.second + 1; - Energy -= LocalIncidence * logl(LocalIncidence); + for (const auto &F : FeatureFreqs) { + double LocalIncidence = F.second + 1; + Energy -= LocalIncidence * log(LocalIncidence); SumIncidence += LocalIncidence; } // Apply add-one smoothing to locally undiscovered features. - // PreciseEnergy -= 0; // since logl(1.0) == 0) - SumIncidence += (GlobalNumberOfFeatures - FeatureFreqs.size()); + // PreciseEnergy -= 0; // since log(1.0) == 0) + SumIncidence += + static_cast<double>(GlobalNumberOfFeatures - FeatureFreqs.size()); // Add a single locally abundant feature apply add-one smoothing. - size_t AbdIncidence = NumExecutedMutations + 1; - Energy -= AbdIncidence * logl(AbdIncidence); + double AbdIncidence = static_cast<double>(NumExecutedMutations + 1); + Energy -= AbdIncidence * log(AbdIncidence); SumIncidence += AbdIncidence; // Normalize. if (SumIncidence != 0) - Energy = (Energy / SumIncidence) + logl(SumIncidence); + Energy = Energy / SumIncidence + log(SumIncidence); + + if (ScalePerExecTime) { + // Scaling to favor inputs with lower execution time. + uint32_t PerfScore = 100; + if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 10) + PerfScore = 10; + else if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 4) + PerfScore = 25; + else if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 2) + PerfScore = 50; + else if (TimeOfUnit.count() * 3 > AverageUnitExecutionTime.count() * 4) + PerfScore = 75; + else if (TimeOfUnit.count() * 4 < AverageUnitExecutionTime.count()) + PerfScore = 300; + else if (TimeOfUnit.count() * 3 < AverageUnitExecutionTime.count()) + PerfScore = 200; + else if (TimeOfUnit.count() * 2 < AverageUnitExecutionTime.count()) + PerfScore = 150; + + Energy *= PerfScore; + } } // Increment the frequency of the feature Idx. @@ -120,6 +150,7 @@ struct EntropicOptions { bool Enabled; size_t NumberOfRarestFeatures; size_t FeatureFrequencyThreshold; + bool ScalePerExecTime; }; class InputCorpus { @@ -177,22 +208,27 @@ public: bool empty() const { return Inputs.empty(); } const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; } InputInfo *AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile, - bool HasFocusFunction, - const Vector<uint32_t> &FeatureSet, + bool HasFocusFunction, bool NeverReduce, + std::chrono::microseconds TimeOfUnit, + const std::vector<uint32_t> &FeatureSet, const DataFlowTrace &DFT, const InputInfo *BaseII) { assert(!U.empty()); if (FeatureDebug) Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures); + // Inputs.size() is cast to uint32_t below. + assert(Inputs.size() < std::numeric_limits<uint32_t>::max()); Inputs.push_back(new InputInfo()); InputInfo &II = *Inputs.back(); II.U = U; II.NumFeatures = NumFeatures; + II.NeverReduce = NeverReduce; + II.TimeOfUnit = TimeOfUnit; II.MayDeleteFile = MayDeleteFile; II.UniqFeatureSet = FeatureSet; II.HasFocusFunction = HasFocusFunction; // Assign maximal energy to the new seed. II.Energy = RareFeatures.empty() ? 1.0 : log(RareFeatures.size()); - II.SumIncidence = RareFeatures.size(); + II.SumIncidence = static_cast<double>(RareFeatures.size()); II.NeedsEnergyUpdate = false; std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end()); ComputeSHA1(U.data(), U.size(), II.Sha1); @@ -223,7 +259,7 @@ public: } // Debug-only - void PrintFeatureSet(const Vector<uint32_t> &FeatureSet) { + void PrintFeatureSet(const std::vector<uint32_t> &FeatureSet) { if (!FeatureDebug) return; Printf("{"); for (uint32_t Feature: FeatureSet) @@ -249,7 +285,8 @@ public: } } - void Replace(InputInfo *II, const Unit &U) { + void Replace(InputInfo *II, const Unit &U, + std::chrono::microseconds TimeOfUnit) { assert(II->U.size() > U.size()); Hashes.erase(Sha1ToString(II->Sha1)); DeleteFile(*II); @@ -257,6 +294,7 @@ public: Hashes.insert(Sha1ToString(II->Sha1)); II->U = U; II->Reduced = true; + II->TimeOfUnit = TimeOfUnit; DistributionNeedsUpdate = true; } @@ -268,6 +306,15 @@ public: return II; } + InputInfo &ChooseUnitToCrossOverWith(Random &Rand, bool UniformDist) { + if (!UniformDist) { + return ChooseUnitToMutate(Rand); + } + InputInfo &II = *Inputs[Rand(Inputs.size())]; + assert(!II.U.empty()); + return II; + } + // Returns an index of random unit from the corpus to mutate. size_t ChooseUnitIdxToMutate(Random &Rand) { UpdateCorpusDistribution(Rand); @@ -281,14 +328,16 @@ public: const auto &II = *Inputs[i]; Printf(" [% 3zd %s] sz: % 5zd runs: % 5zd succ: % 5zd focus: %d\n", i, Sha1ToString(II.Sha1).c_str(), II.U.size(), - II.NumExecutedMutations, II.NumSuccessfullMutations, II.HasFocusFunction); + II.NumExecutedMutations, II.NumSuccessfulMutations, + II.HasFocusFunction); } } void PrintFeatureSet() { for (size_t i = 0; i < kFeatureSetSize; i++) { if(size_t Sz = GetFeature(i)) - Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz); + Printf("[%zd: id %zd sz%zd] ", i, (size_t)SmallestElementPerFeature[i], + Sz); } Printf("\n\t"); for (size_t i = 0; i < Inputs.size(); i++) @@ -335,6 +384,7 @@ public: } // Remove most abundant rare feature. + IsRareFeature[Delete] = false; RareFeatures[Delete] = RareFeatures.back(); RareFeatures.pop_back(); @@ -350,6 +400,7 @@ public: // Add rare feature, handle collisions, and update energy. RareFeatures.push_back(Idx); + IsRareFeature[Idx] = true; GlobalFeatureFreqs[Idx] = 0; for (auto II : Inputs) { II->DeleteFeatureFreq(Idx); @@ -358,7 +409,7 @@ public: // Zero energy seeds will never be fuzzed and remain zero energy. if (II->Energy > 0.0) { II->SumIncidence += 1; - II->Energy += logl(II->SumIncidence) / II->SumIncidence; + II->Energy += log(II->SumIncidence) / II->SumIncidence; } } @@ -385,7 +436,8 @@ public: NumUpdatedFeatures++; if (FeatureDebug) Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize); - SmallestElementPerFeature[Idx] = Inputs.size(); + // Inputs.size() is guaranteed to be less than UINT32_MAX by AddToCorpus. + SmallestElementPerFeature[Idx] = static_cast<uint32_t>(Inputs.size()); InputSizesPerFeature[Idx] = NewSize; return true; } @@ -402,9 +454,7 @@ public: uint16_t Freq = GlobalFeatureFreqs[Idx32]++; // Skip if abundant. - if (Freq > FreqOfMostAbundantRareFeature || - std::find(RareFeatures.begin(), RareFeatures.end(), Idx32) == - RareFeatures.end()) + if (Freq > FreqOfMostAbundantRareFeature || !IsRareFeature[Idx32]) return; // Update global frequencies. @@ -423,7 +473,7 @@ private: static const bool FeatureDebug = false; - size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; } + uint32_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; } void ValidateFeatureSet() { if (FeatureDebug) @@ -460,12 +510,19 @@ private: Weights.resize(N); std::iota(Intervals.begin(), Intervals.end(), 0); + std::chrono::microseconds AverageUnitExecutionTime(0); + for (auto II : Inputs) { + AverageUnitExecutionTime += II->TimeOfUnit; + } + AverageUnitExecutionTime /= N; + bool VanillaSchedule = true; if (Entropic.Enabled) { for (auto II : Inputs) { if (II->NeedsEnergyUpdate && II->Energy != 0.0) { II->NeedsEnergyUpdate = false; - II->UpdateEnergy(RareFeatures.size()); + II->UpdateEnergy(RareFeatures.size(), Entropic.ScalePerExecTime, + AverageUnitExecutionTime); } } @@ -491,9 +548,11 @@ private: if (VanillaSchedule) { for (size_t i = 0; i < N; i++) - Weights[i] = Inputs[i]->NumFeatures - ? (i + 1) * (Inputs[i]->HasFocusFunction ? 1000 : 1) - : 0.; + Weights[i] = + Inputs[i]->NumFeatures + ? static_cast<double>((i + 1) * + (Inputs[i]->HasFocusFunction ? 1000 : 1)) + : 0.; } if (FeatureDebug) { @@ -509,11 +568,11 @@ private: } std::piecewise_constant_distribution<double> CorpusDistribution; - Vector<double> Intervals; - Vector<double> Weights; + std::vector<double> Intervals; + std::vector<double> Weights; std::unordered_set<std::string> Hashes; - Vector<InputInfo*> Inputs; + std::vector<InputInfo *> Inputs; size_t NumAddedFeatures = 0; size_t NumUpdatedFeatures = 0; @@ -523,7 +582,8 @@ private: bool DistributionNeedsUpdate = true; uint16_t FreqOfMostAbundantRareFeature = 0; uint16_t GlobalFeatureFreqs[kFeatureSetSize] = {}; - Vector<uint32_t> RareFeatures; + std::vector<uint32_t> RareFeatures; + std::bitset<kFeatureSetSize> IsRareFeature; std::string OutputCorpus; }; diff --git a/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.cpp b/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.cpp @@ -37,7 +37,7 @@ bool BlockCoverage::AppendCoverage(const std::string &S) { // Coverage lines have this form: // CN X Y Z T // where N is the number of the function, T is the total number of instrumented -// BBs, and X,Y,Z, if present, are the indecies of covered BB. +// BBs, and X,Y,Z, if present, are the indices of covered BB. // BB #0, which is the entry block, is not explicitly listed. bool BlockCoverage::AppendCoverage(std::istream &IN) { std::string L; @@ -52,7 +52,7 @@ bool BlockCoverage::AppendCoverage(std::istream &IN) { continue; } if (L[0] != 'C') continue; - Vector<uint32_t> CoveredBlocks; + std::vector<uint32_t> CoveredBlocks; while (true) { uint32_t BB = 0; SS >> BB; @@ -60,6 +60,7 @@ bool BlockCoverage::AppendCoverage(std::istream &IN) { CoveredBlocks.push_back(BB); } if (CoveredBlocks.empty()) return false; + // Ensures no CoverageVector is longer than UINT32_MAX. uint32_t NumBlocks = CoveredBlocks.back(); CoveredBlocks.pop_back(); for (auto BB : CoveredBlocks) @@ -67,7 +68,7 @@ bool BlockCoverage::AppendCoverage(std::istream &IN) { auto It = Functions.find(FunctionId); auto &Counters = It == Functions.end() - ? Functions.insert({FunctionId, Vector<uint32_t>(NumBlocks)}) + ? Functions.insert({FunctionId, std::vector<uint32_t>(NumBlocks)}) .first->second : It->second; @@ -85,9 +86,9 @@ bool BlockCoverage::AppendCoverage(std::istream &IN) { // * any uncovered function gets weight 0. // * a function with lots of uncovered blocks gets bigger weight. // * a function with a less frequently executed code gets bigger weight. -Vector<double> BlockCoverage::FunctionWeights(size_t NumFunctions) const { - Vector<double> Res(NumFunctions); - for (auto It : Functions) { +std::vector<double> BlockCoverage::FunctionWeights(size_t NumFunctions) const { + std::vector<double> Res(NumFunctions); + for (const auto &It : Functions) { auto FunctionID = It.first; auto Counters = It.second; assert(FunctionID < NumFunctions); @@ -102,11 +103,9 @@ Vector<double> BlockCoverage::FunctionWeights(size_t NumFunctions) const { return Res; } -int DataFlowTrace::ReadCoverage(const std::string &DirPath) { - Vector<SizedFile> Files; - int Res = GetSizedFilesFromDir(DirPath, &Files); - if (Res != 0) - return Res; +void DataFlowTrace::ReadCoverage(const std::string &DirPath) { + std::vector<SizedFile> Files; + GetSizedFilesFromDir(DirPath, &Files); for (auto &SF : Files) { auto Name = Basename(SF.File); if (Name == kFunctionsTxt) continue; @@ -114,19 +113,18 @@ int DataFlowTrace::ReadCoverage(const std::string &DirPath) { std::ifstream IF(SF.File); Coverage.AppendCoverage(IF); } - return 0; } -static void DFTStringAppendToVector(Vector<uint8_t> *DFT, +static void DFTStringAppendToVector(std::vector<uint8_t> *DFT, const std::string &DFTString) { assert(DFT->size() == DFTString.size()); for (size_t I = 0, Len = DFT->size(); I < Len; I++) (*DFT)[I] = DFTString[I] == '1'; } -// converts a string of '0' and '1' into a Vector<uint8_t> -static Vector<uint8_t> DFTStringToVector(const std::string &DFTString) { - Vector<uint8_t> DFT(DFTString.size()); +// converts a string of '0' and '1' into a std::vector<uint8_t> +static std::vector<uint8_t> DFTStringToVector(const std::string &DFTString) { + std::vector<uint8_t> DFT(DFTString.size()); DFTStringAppendToVector(&DFT, DFTString); return DFT; } @@ -160,17 +158,15 @@ static bool ParseDFTLine(const std::string &Line, size_t *FunctionNum, return true; } -int DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, - Vector<SizedFile> &CorporaFiles, Random &Rand) { - if (DirPath.empty()) return 0; +bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, + std::vector<SizedFile> &CorporaFiles, Random &Rand) { + if (DirPath.empty()) return false; Printf("INFO: DataFlowTrace: reading from '%s'\n", DirPath.c_str()); - Vector<SizedFile> Files; - int Res = GetSizedFilesFromDir(DirPath, &Files); - if (Res != 0) - return Res; + std::vector<SizedFile> Files; + GetSizedFilesFromDir(DirPath, &Files); std::string L; size_t FocusFuncIdx = SIZE_MAX; - Vector<std::string> FunctionNames; + std::vector<std::string> FunctionNames; // Collect the hashes of the corpus files. for (auto &SF : CorporaFiles) @@ -186,18 +182,16 @@ int DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, FocusFuncIdx = NumFunctions - 1; } if (!NumFunctions) - return 0; + return false; if (*FocusFunction == "auto") { // AUTOFOCUS works like this: // * reads the coverage data from the DFT files. // * assigns weights to functions based on coverage. // * chooses a random function according to the weights. - Res = ReadCoverage(DirPath); - if (Res != 0) - return Res; + ReadCoverage(DirPath); auto Weights = Coverage.FunctionWeights(NumFunctions); - Vector<double> Intervals(NumFunctions + 1); + std::vector<double> Intervals(NumFunctions + 1); std::iota(Intervals.begin(), Intervals.end(), 0); auto Distribution = std::piecewise_constant_distribution<double>( Intervals.begin(), Intervals.end(), Weights.begin()); @@ -207,7 +201,8 @@ int DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, Printf("INFO: AUTOFOCUS: %zd %s\n", FocusFuncIdx, FunctionNames[FocusFuncIdx].c_str()); for (size_t i = 0; i < NumFunctions; i++) { - if (!Weights[i]) continue; + if (Weights[i] == 0.0) + continue; Printf(" [%zd] W %g\tBB-tot %u\tBB-cov %u\tEntryFreq %u:\t%s\n", i, Weights[i], Coverage.GetNumberOfBlocks(i), Coverage.GetNumberOfCoveredBlocks(i), Coverage.GetCounter(i, 0), @@ -216,7 +211,7 @@ int DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, } if (!NumFunctions || FocusFuncIdx == SIZE_MAX || Files.size() <= 1) - return 0; + return false; // Read traces. size_t NumTraceFiles = 0; @@ -235,10 +230,8 @@ int DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, FunctionNum == FocusFuncIdx) { NumTracesWithFocusFunction++; - if (FunctionNum >= NumFunctions) { - ParseError("N is greater than the number of functions", L); - return 0; - } + if (FunctionNum >= NumFunctions) + return ParseError("N is greater than the number of functions", L); Traces[Name] = DFTStringToVector(DFTString); // Print just a few small traces. if (NumTracesWithFocusFunction <= 3 && DFTString.size() <= 16) @@ -250,11 +243,11 @@ int DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, Printf("INFO: DataFlowTrace: %zd trace files, %zd functions, " "%zd traces with focus function\n", NumTraceFiles, NumFunctions, NumTracesWithFocusFunction); - return 0; + return NumTraceFiles > 0; } int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, - const Vector<SizedFile> &CorporaFiles) { + const std::vector<SizedFile> &CorporaFiles) { Printf("INFO: collecting data flow: bin: %s dir: %s files: %zd\n", DFTBinary.c_str(), DirPath.c_str(), CorporaFiles.size()); if (CorporaFiles.empty()) { @@ -272,8 +265,6 @@ int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, // we then request tags in [0,Size/2) and [Size/2, Size), and so on. // Function number => DFT. auto OutPath = DirPlusFile(DirPath, Hash(FileToVector(F.File))); - std::unordered_map<size_t, Vector<uint8_t>> DFTMap; - std::unordered_set<std::string> Cov; Command Cmd; Cmd.addArgument(DFTBinary); Cmd.addArgument(F.File); diff --git a/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.h b/tools/fuzzing/libfuzzer/FuzzerDataFlowTrace.h @@ -39,10 +39,11 @@ namespace fuzzer { int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, - const Vector<SizedFile> &CorporaFiles); + const std::vector<SizedFile> &CorporaFiles); class BlockCoverage { - public: +public: + // These functions guarantee no CoverageVector is longer than UINT32_MAX. bool AppendCoverage(std::istream &IN); bool AppendCoverage(const std::string &S); @@ -50,7 +51,8 @@ class BlockCoverage { uint32_t GetCounter(size_t FunctionId, size_t BasicBlockId) { auto It = Functions.find(FunctionId); - if (It == Functions.end()) return 0; + if (It == Functions.end()) + return 0; const auto &Counters = It->second; if (BasicBlockId < Counters.size()) return Counters[BasicBlockId]; @@ -61,7 +63,7 @@ class BlockCoverage { auto It = Functions.find(FunctionId); if (It == Functions.end()) return 0; const auto &Counters = It->second; - return Counters.size(); + return static_cast<uint32_t>(Counters.size()); } uint32_t GetNumberOfCoveredBlocks(size_t FunctionId) { @@ -75,12 +77,11 @@ class BlockCoverage { return Result; } - Vector<double> FunctionWeights(size_t NumFunctions) const; + std::vector<double> FunctionWeights(size_t NumFunctions) const; void clear() { Functions.clear(); } - private: - - typedef Vector<uint32_t> CoverageVector; +private: + typedef std::vector<uint32_t> CoverageVector; uint32_t NumberOfCoveredBlocks(const CoverageVector &Counters) const { uint32_t Res = 0; @@ -91,7 +92,8 @@ class BlockCoverage { } uint32_t NumberOfUncoveredBlocks(const CoverageVector &Counters) const { - return Counters.size() - NumberOfCoveredBlocks(Counters); + return static_cast<uint32_t>(Counters.size()) - + NumberOfCoveredBlocks(Counters); } uint32_t SmallestNonZeroCounter(const CoverageVector &Counters) const { @@ -113,11 +115,11 @@ class BlockCoverage { class DataFlowTrace { public: - int ReadCoverage(const std::string &DirPath); - int Init(const std::string &DirPath, std::string *FocusFunction, - Vector<SizedFile> &CorporaFiles, Random &Rand); + void ReadCoverage(const std::string &DirPath); + bool Init(const std::string &DirPath, std::string *FocusFunction, + std::vector<SizedFile> &CorporaFiles, Random &Rand); void Clear() { Traces.clear(); } - const Vector<uint8_t> *Get(const std::string &InputSha1) const { + const std::vector<uint8_t> *Get(const std::string &InputSha1) const { auto It = Traces.find(InputSha1); if (It != Traces.end()) return &It->second; @@ -126,9 +128,9 @@ class DataFlowTrace { private: // Input's sha1 => DFT for the FocusFunction. - std::unordered_map<std::string, Vector<uint8_t> > Traces; - BlockCoverage Coverage; - std::unordered_set<std::string> CorporaHashes; + std::unordered_map<std::string, std::vector<uint8_t>> Traces; + BlockCoverage Coverage; + std::unordered_set<std::string> CorporaHashes; }; } // namespace fuzzer diff --git a/tools/fuzzing/libfuzzer/FuzzerDefs.h b/tools/fuzzing/libfuzzer/FuzzerDefs.h @@ -38,28 +38,8 @@ struct ExternalFunctions; // Global interface to functions that may or may not be available. extern ExternalFunctions *EF; -// We are using a custom allocator to give a different symbol name to STL -// containers in order to avoid ODR violations. -template<typename T> - class fuzzer_allocator: public std::allocator<T> { - public: - fuzzer_allocator() = default; - - template<class U> - explicit fuzzer_allocator(const fuzzer_allocator<U>&) {} - - template<class Other> - struct rebind { typedef fuzzer_allocator<Other> other; }; - }; - -template<typename T> -using Vector = std::vector<T, fuzzer_allocator<T>>; - -template<typename T> -using Set = std::set<T, std::less<T>, fuzzer_allocator<T>>; - -typedef Vector<uint8_t> Unit; -typedef Vector<Unit> UnitVector; +typedef std::vector<uint8_t> Unit; +typedef std::vector<Unit> UnitVector; typedef int (*UserCallback)(const uint8_t *Data, size_t Size); int FuzzerDriver(int *argc, char ***argv, UserCallback Callback); diff --git a/tools/fuzzing/libfuzzer/FuzzerDictionary.h b/tools/fuzzing/libfuzzer/FuzzerDictionary.h @@ -23,12 +23,16 @@ template <size_t kMaxSizeT> class FixedWord { public: static const size_t kMaxSize = kMaxSizeT; FixedWord() {} - FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); } + FixedWord(const uint8_t *B, size_t S) { Set(B, S); } - void Set(const uint8_t *B, uint8_t S) { + void Set(const uint8_t *B, size_t S) { + static_assert(kMaxSizeT <= std::numeric_limits<uint8_t>::max(), + "FixedWord::kMaxSizeT cannot fit in a uint8_t."); assert(S <= kMaxSize); - memcpy(Data, B, S); - Size = S; + // memcpy cannot take null pointer arguments even if Size is 0. + if (S) + memcpy(Data, B, S); + Size = static_cast<uint8_t>(S); } bool operator==(const FixedWord<kMaxSize> &w) const { @@ -50,10 +54,13 @@ class DictionaryEntry { public: DictionaryEntry() {} DictionaryEntry(Word W) : W(W) {} - DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {} + DictionaryEntry(Word W, size_t PositionHint) + : W(W), PositionHint(PositionHint) {} const Word &GetW() const { return W; } - bool HasPositionHint() const { return PositionHint != std::numeric_limits<size_t>::max(); } + bool HasPositionHint() const { + return PositionHint != std::numeric_limits<size_t>::max(); + } size_t GetPositionHint() const { assert(HasPositionHint()); return PositionHint; @@ -106,12 +113,12 @@ private: }; // Parses one dictionary entry. -// If successful, write the enty to Unit and returns true, +// If successful, writes the entry to Unit and returns true, // otherwise returns false. bool ParseOneDictionaryEntry(const std::string &Str, Unit *U); // Parses the dictionary file, fills Units, returns true iff all lines // were parsed successfully. -bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units); +bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units); } // namespace fuzzer diff --git a/tools/fuzzing/libfuzzer/FuzzerDriver.cpp b/tools/fuzzing/libfuzzer/FuzzerDriver.cpp @@ -24,10 +24,11 @@ #include <chrono> #include <cstdlib> #include <cstring> +#include <fstream> +#include <functional> #include <mutex> #include <string> #include <thread> -#include <fstream> // This function should be present in the libFuzzer so that the client // binary can test for its existence. @@ -86,7 +87,7 @@ static const FlagDescription FlagDescriptions [] { static const size_t kNumFlags = sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]); -static Vector<std::string> *Inputs; +static std::vector<std::string> *Inputs; static std::string *ProgName; static void PrintHelp() { @@ -159,16 +160,16 @@ static bool ParseOneFlag(const char *Param) { const char *Str = FlagValue(Param, Name); if (Str) { if (FlagDescriptions[F].IntFlag) { - int Val = MyStol(Str); - *FlagDescriptions[F].IntFlag = Val; + auto Val = MyStol(Str); + *FlagDescriptions[F].IntFlag = static_cast<int>(Val); if (Flags.verbosity >= 2) - Printf("Flag: %s %d\n", Name, Val); + Printf("Flag: %s %d\n", Name, (int)Val); return true; } else if (FlagDescriptions[F].UIntFlag) { - unsigned int Val = std::stoul(Str); - *FlagDescriptions[F].UIntFlag = Val; + auto Val = std::stoul(Str); + *FlagDescriptions[F].UIntFlag = static_cast<unsigned int>(Val); if (Flags.verbosity >= 2) - Printf("Flag: %s %u\n", Name, Val); + Printf("Flag: %s %u\n", Name, (uint32_t)Val); return true; } else if (FlagDescriptions[F].StrFlag) { *FlagDescriptions[F].StrFlag = Str; @@ -187,7 +188,7 @@ static bool ParseOneFlag(const char *Param) { } // We don't use any library to minimize dependencies. -static void ParseFlags(const Vector<std::string> &Args, +static void ParseFlags(const std::vector<std::string> &Args, const ExternalFunctions *EF) { for (size_t F = 0; F < kNumFlags; F++) { if (FlagDescriptions[F].IntFlag) @@ -206,7 +207,7 @@ static void ParseFlags(const Vector<std::string> &Args, "Disabling -len_control by default.\n", EF->LLVMFuzzerCustomMutator); } - Inputs = new Vector<std::string>; + Inputs = new std::vector<std::string>; for (size_t A = 1; A < Args.size(); A++) { if (ParseOneFlag(Args[A].c_str())) { if (Flags.ignore_remaining_args) @@ -229,6 +230,7 @@ static void PulseThread() { static void WorkerThread(const Command &BaseCmd, std::atomic<unsigned> *Counter, unsigned NumJobs, std::atomic<bool> *HasErrors) { + ScopedDisableMsanInterceptorChecks S; while (true) { unsigned C = (*Counter)++; if (C >= NumJobs) break; @@ -250,7 +252,29 @@ static void WorkerThread(const Command &BaseCmd, std::atomic<unsigned> *Counter, } } -std::string CloneArgsWithoutX(const Vector<std::string> &Args, +static void ValidateDirectoryExists(const std::string &Path, + bool CreateDirectory) { + if (Path.empty()) { + Printf("ERROR: Provided directory path is an empty string\n"); + exit(1); + } + + if (IsDirectory(Path)) + return; + + if (CreateDirectory) { + if (!MkDirRecursive(Path)) { + Printf("ERROR: Failed to create directory \"%s\"\n", Path.c_str()); + exit(1); + } + return; + } + + Printf("ERROR: The required directory \"%s\" does not exist\n", Path.c_str()); + exit(1); +} + +std::string CloneArgsWithoutX(const std::vector<std::string> &Args, const char *X1, const char *X2) { std::string Cmd; for (auto &S : Args) { @@ -261,23 +285,32 @@ std::string CloneArgsWithoutX(const Vector<std::string> &Args, return Cmd; } -static int RunInMultipleProcesses(const Vector<std::string> &Args, +static int RunInMultipleProcesses(const std::vector<std::string> &Args, unsigned NumWorkers, unsigned NumJobs) { std::atomic<unsigned> Counter(0); std::atomic<bool> HasErrors(false); Command Cmd(Args); Cmd.removeFlag("jobs"); Cmd.removeFlag("workers"); - Vector<std::thread> V; + std::vector<std::thread> V; std::thread Pulse(PulseThread); Pulse.detach(); - for (unsigned i = 0; i < NumWorkers; i++) - V.push_back(std::thread(WorkerThread, std::ref(Cmd), &Counter, NumJobs, &HasErrors)); + V.resize(NumWorkers); + for (unsigned i = 0; i < NumWorkers; i++) { + V[i] = std::thread(WorkerThread, std::ref(Cmd), &Counter, NumJobs, + &HasErrors); + SetThreadName(V[i], "FuzzerWorker"); + } for (auto &T : V) T.join(); return HasErrors ? 1 : 0; } +void StartRssThread(Fuzzer *F, size_t RssLimitMb); + +// Fuchsia needs to do some book checking before starting the RssThread, +// so it has its own implementation. +#if !LIBFUZZER_FUCHSIA static void RssThread(Fuzzer *F, size_t RssLimitMb) { while (true) { SleepSeconds(1); @@ -287,19 +320,25 @@ static void RssThread(Fuzzer *F, size_t RssLimitMb) { } } -static void StartRssThread(Fuzzer *F, size_t RssLimitMb) { +void StartRssThread(Fuzzer *F, size_t RssLimitMb) { if (!RssLimitMb) return; std::thread T(RssThread, F, RssLimitMb); T.detach(); } +#endif int RunOneTest(Fuzzer *F, const char *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); + if (Flags.print_full_coverage) { + // Leak detection is not needed when collecting full coverage data. + F->TPCUpdateObservedPCs(); + } else { + F->TryDetectingAMemoryLeak(U.data(), U.size(), true); + } return 0; } @@ -321,12 +360,12 @@ static std::string GetDedupTokenFromCmdOutput(const std::string &S) { return S.substr(Beg, End - Beg); } -int CleanseCrashInput(const Vector<std::string> &Args, - const FuzzingOptions &Options) { +int CleanseCrashInput(const std::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; + exit(1); } std::string InputFilePath = Inputs->at(0); std::string OutputFilePath = Flags.exact_artifact_path; @@ -345,7 +384,7 @@ int CleanseCrashInput(const Vector<std::string> &Args, auto U = FileToVector(CurrentFilePath); size_t Size = U.size(); - const Vector<uint8_t> ReplacementBytes = {' ', 0xff}; + const std::vector<uint8_t> ReplacementBytes = {' ', 0xff}; for (int NumAttempts = 0; NumAttempts < 5; NumAttempts++) { bool Changed = false; for (size_t Idx = 0; Idx < Size; Idx++) { @@ -376,11 +415,11 @@ int CleanseCrashInput(const Vector<std::string> &Args, return 0; } -int MinimizeCrashInput(const Vector<std::string> &Args, +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"); - return 1; + exit(1); } std::string InputFilePath = Inputs->at(0); Command BaseCmd(Args); @@ -411,7 +450,7 @@ int MinimizeCrashInput(const Vector<std::string> &Args, bool Success = ExecuteCommand(Cmd, &CmdOutput); if (Success) { Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str()); - return 1; + exit(1); } Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize " "it further\n", @@ -435,7 +474,7 @@ int MinimizeCrashInput(const Vector<std::string> &Args, CurrentFilePath = Flags.exact_artifact_path; WriteToFile(U, CurrentFilePath); } - Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n", + Printf("CRASH_MIN: failed to minimize beyond %s (%zu bytes), exiting\n", CurrentFilePath.c_str(), U.size()); break; } @@ -466,64 +505,55 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size()); if (U.size() < 2) { Printf("INFO: The input is small enough, exiting\n"); - return 0; + exit(0); } F->SetMaxInputLen(U.size()); F->SetMaxMutationLen(U.size() - 1); F->MinimizeCrashLoop(U); Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n"); - return 0; + exit(0); } -int Merge(Fuzzer *F, FuzzingOptions &Options, const Vector<std::string> &Args, - const Vector<std::string> &Corpora, const char *CFPathOrNull) { +void Merge(Fuzzer *F, FuzzingOptions &Options, + const std::vector<std::string> &Args, + const std::vector<std::string> &Corpora, const char *CFPathOrNull) { if (Corpora.size() < 2) { Printf("INFO: Merge requires two or more corpus dirs\n"); - return 0; + exit(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::vector<SizedFile> OldCorpus, NewCorpus; + GetSizedFilesFromDir(Corpora[0], &OldCorpus); + for (size_t i = 1; i < Corpora.size(); i++) + GetSizedFilesFromDir(Corpora[i], &NewCorpus); std::sort(OldCorpus.begin(), OldCorpus.end()); std::sort(NewCorpus.begin(), NewCorpus.end()); std::string CFPath = CFPathOrNull ? CFPathOrNull : TempPath("Merge", ".txt"); - Vector<std::string> NewFiles; - Set<uint32_t> NewFeatures, NewCov; - Res = CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures, - {}, &NewCov, CFPath, true); - if (Res != 0) - return Res; - - if (F->isGracefulExitRequested()) - return 0; + std::vector<std::string> NewFiles; + std::set<uint32_t> NewFeatures, NewCov; + CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures, + {}, &NewCov, CFPath, true, Flags.set_cover_merge); 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); - return 0; + exit(0); } -int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict, - UnitVector& Corpus) { - Printf("Started dictionary minimization (up to %d tests)\n", +int AnalyzeDictionary(Fuzzer *F, const std::vector<Unit> &Dict, + UnitVector &Corpus) { + Printf("Started dictionary minimization (up to %zu tests)\n", Dict.size() * Corpus.size() * 2); // Scores and usage count for each dictionary unit. - Vector<int> Scores(Dict.size()); - Vector<int> Usages(Dict.size()); + std::vector<int> Scores(Dict.size()); + std::vector<int> Usages(Dict.size()); - Vector<size_t> InitialFeatures; - Vector<size_t> ModifiedFeatures; + 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()); @@ -533,7 +563,7 @@ int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict, }); for (size_t i = 0; i < Dict.size(); ++i) { - Vector<uint8_t> Data = C; + std::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. @@ -579,9 +609,10 @@ int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict, return 0; } -int ParseSeedInuts(const char *seed_inputs, Vector<std::string> &Files) { +std::vector<std::string> ParseSeedInputs(const char *seed_inputs) { // Parse -seed_inputs=file1,file2,... or -seed_inputs=@seed_inputs_file - if (!seed_inputs) return 0; + std::vector<std::string> Files; + if (!seed_inputs) return Files; std::string SeedInputs; if (Flags.seed_inputs[0] == '@') SeedInputs = FileToString(Flags.seed_inputs + 1); // File contains list. @@ -589,7 +620,7 @@ int ParseSeedInuts(const char *seed_inputs, Vector<std::string> &Files) { SeedInputs = Flags.seed_inputs; // seed_inputs contains the list. if (SeedInputs.empty()) { Printf("seed_inputs is empty or @file does not exist.\n"); - return 1; + exit(1); } // Parse SeedInputs. size_t comma_pos = 0; @@ -598,12 +629,13 @@ int ParseSeedInuts(const char *seed_inputs, Vector<std::string> &Files) { SeedInputs = SeedInputs.substr(0, comma_pos); } Files.push_back(SeedInputs); - return 0; + return Files; } -static Vector<SizedFile> ReadCorpora(const Vector<std::string> &CorpusDirs, - const Vector<std::string> &ExtraSeedFiles) { - Vector<SizedFile> SizedFiles; +static std::vector<SizedFile> +ReadCorpora(const std::vector<std::string> &CorpusDirs, + const std::vector<std::string> &ExtraSeedFiles) { + std::vector<SizedFile> SizedFiles; size_t LastNumFiles = 0; for (auto &Dir : CorpusDirs) { GetSizedFilesFromDir(Dir, &SizedFiles); @@ -621,18 +653,17 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { using namespace fuzzer; assert(argc && argv && "Argument pointers cannot be nullptr"); std::string Argv0((*argv)[0]); - if (!EF) - EF = new ExternalFunctions(); + EF = new ExternalFunctions(); if (EF->LLVMFuzzerInitialize) EF->LLVMFuzzerInitialize(argc, argv); if (EF->__msan_scoped_disable_interceptor_checks) EF->__msan_scoped_disable_interceptor_checks(); - const Vector<std::string> Args(*argv, *argv + *argc); + 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"); - return 1; + exit(1); } ParseFlags(Args, EF); if (Flags.help) { @@ -658,6 +689,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.Verbosity = Flags.verbosity; Options.MaxLen = Flags.max_len; Options.LenControl = Flags.len_control; + Options.KeepSeed = Flags.keep_seed; Options.UnitTimeoutSec = Flags.timeout; Options.ErrorExitCode = Flags.error_exitcode; Options.TimeoutExitCode = Flags.timeout_exitcode; @@ -666,6 +698,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.IgnoreCrashes = Flags.ignore_crashes; Options.MaxTotalTimeSec = Flags.max_total_time; Options.DoCrossOver = Flags.cross_over; + Options.CrossOverUniformDist = Flags.cross_over_uniform_dist; Options.MutateDepth = Flags.mutate_depth; Options.ReduceDepth = Flags.reduce_depth; Options.UseCounters = Flags.use_counters; @@ -687,14 +720,34 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.MallocLimitMb = Options.RssLimitMb; if (Flags.runs >= 0) Options.MaxNumberOfRuns = Flags.runs; - if (!Inputs->empty() && !Flags.minimize_crash_internal_step) - Options.OutputCorpus = (*Inputs)[0]; + if (!Inputs->empty() && !Flags.minimize_crash_internal_step) { + // Ensure output corpus assumed to be the first arbitrary argument input + // is not a path to an existing file. + std::string OutputCorpusDir = (*Inputs)[0]; + if (!IsFile(OutputCorpusDir)) { + Options.OutputCorpus = OutputCorpusDir; + ValidateDirectoryExists(Options.OutputCorpus, Flags.create_missing_dirs); + } + } Options.ReportSlowUnits = Flags.report_slow_units; - if (Flags.artifact_prefix) + if (Flags.artifact_prefix) { Options.ArtifactPrefix = Flags.artifact_prefix; - if (Flags.exact_artifact_path) + + // Since the prefix could be a full path to a file name prefix, assume + // that if the path ends with the platform's separator that a directory + // is desired + std::string ArtifactPathDir = Options.ArtifactPrefix; + if (!IsSeparator(ArtifactPathDir[ArtifactPathDir.length() - 1])) { + ArtifactPathDir = DirName(ArtifactPathDir); + } + ValidateDirectoryExists(ArtifactPathDir, Flags.create_missing_dirs); + } + if (Flags.exact_artifact_path) { Options.ExactArtifactPath = Flags.exact_artifact_path; - Vector<Unit> Dictionary; + ValidateDirectoryExists(DirName(Options.ExactArtifactPath), + Flags.create_missing_dirs); + } + std::vector<Unit> Dictionary; if (Flags.dict) if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary)) return 1; @@ -708,6 +761,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.PrintFinalStats = Flags.print_final_stats; Options.PrintCorpusStats = Flags.print_corpus_stats; Options.PrintCoverage = Flags.print_coverage; + Options.PrintFullCoverage = Flags.print_full_coverage; if (Flags.exit_on_src_pos) Options.ExitOnSrcPos = Flags.exit_on_src_pos; if (Flags.exit_on_item) @@ -716,8 +770,12 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.FocusFunction = Flags.focus_function; if (Flags.data_flow_trace) Options.DataFlowTrace = Flags.data_flow_trace; - if (Flags.features_dir) + if (Flags.features_dir) { Options.FeaturesDir = Flags.features_dir; + ValidateDirectoryExists(Options.FeaturesDir, Flags.create_missing_dirs); + } + if (Flags.mutation_graph_file) + Options.MutationGraphFile = Flags.mutation_graph_file; if (Flags.collect_data_flow) Options.CollectDataFlow = Flags.collect_data_flow; if (Flags.stop_file) @@ -727,31 +785,30 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { (size_t)Flags.entropic_feature_frequency_threshold; Options.EntropicNumberOfRarestFeatures = (size_t)Flags.entropic_number_of_rarest_features; - if (Options.Entropic) { - if (!Options.FocusFunction.empty()) { - Printf("ERROR: The parameters `--entropic` and `--focus_function` cannot " - "be used together.\n"); - return 1; - } - Printf("INFO: Running with entropic power schedule (0x%X, %d).\n", + Options.EntropicScalePerExecTime = Flags.entropic_scale_per_exec_time; + if (!Options.FocusFunction.empty()) + Options.Entropic = false; // FocusFunction overrides entropic scheduling. + if (Options.Entropic) + Printf("INFO: Running with entropic power schedule (0x%zX, %zu).\n", Options.EntropicFeatureFrequencyThreshold, Options.EntropicNumberOfRarestFeatures); - } struct EntropicOptions Entropic; Entropic.Enabled = Options.Entropic; Entropic.FeatureFrequencyThreshold = Options.EntropicFeatureFrequencyThreshold; Entropic.NumberOfRarestFeatures = Options.EntropicNumberOfRarestFeatures; + Entropic.ScalePerExecTime = Options.EntropicScalePerExecTime; unsigned Seed = Flags.seed; // Initialize Seed. if (Seed == 0) - Seed = - std::chrono::system_clock::now().time_since_epoch().count() + GetPid(); + Seed = static_cast<unsigned>( + std::chrono::system_clock::now().time_since_epoch().count() + GetPid()); if (Flags.verbosity) Printf("INFO: Seed: %u\n", Seed); - if (Flags.collect_data_flow && !Flags.fork && !Flags.merge) { + if (Flags.collect_data_flow && Flags.data_flow_trace && !Flags.fork && + !(Flags.merge || Flags.set_cover_merge)) { if (RunIndividualFiles) return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace, ReadCorpora({}, *Inputs)); @@ -776,15 +833,19 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { #endif // LIBFUZZER_EMSCRIPTEN Options.HandleAbrt = Flags.handle_abrt; + Options.HandleAlrm = !Flags.minimize_crash; Options.HandleBus = Flags.handle_bus; Options.HandleFpe = Flags.handle_fpe; Options.HandleIll = Flags.handle_ill; Options.HandleInt = Flags.handle_int; Options.HandleSegv = Flags.handle_segv; Options.HandleTerm = Flags.handle_term; + Options.HandleTrap = Flags.handle_trap; Options.HandleXfsz = Flags.handle_xfsz; Options.HandleUsr1 = Flags.handle_usr1; Options.HandleUsr2 = Flags.handle_usr2; + Options.HandleWinExcept = Flags.handle_winexcept; + SetSignalHandler(Options); std::atexit(Fuzzer::StaticExitCallback); @@ -810,28 +871,31 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { 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("Executed %s in %ld 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; + exit(0); } + Options.ForkCorpusGroups = Flags.fork_corpus_groups; if (Flags.fork) - return FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork); + 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 || Flags.set_cover_merge) + 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); + F->CrashResistantMergeInternalStep(Flags.merge_control_file, + !strncmp(Flags.merge_inner, "2", 1)); + exit(0); } if (Flags.analyze_dict) { @@ -849,31 +913,21 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { } if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) { Printf("Dictionary analysis failed\n"); - return 1; + exit(1); } Printf("Dictionary analysis succeeded\n"); - return 0; + exit(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; - } + auto CorporaFiles = ReadCorpora(*Inputs, ParseSeedInputs(Flags.seed_inputs)); + F->Loop(CorporaFiles); 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. + exit(0); // Don't let F destroy itself. } extern "C" ATTRIBUTE_INTERFACE int diff --git a/tools/fuzzing/libfuzzer/FuzzerExtFunctionsWeak.cpp b/tools/fuzzing/libfuzzer/FuzzerExtFunctionsWeak.cpp @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// #include "FuzzerPlatform.h" #if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FUCHSIA || \ - LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN + LIBFUZZER_FREEBSD || LIBFUZZER_EMSCRIPTEN #include "FuzzerExtFunctions.h" #include "FuzzerIO.h" diff --git a/tools/fuzzing/libfuzzer/FuzzerExtFunctionsWindows.cpp b/tools/fuzzing/libfuzzer/FuzzerExtFunctionsWindows.cpp @@ -14,6 +14,7 @@ #include "FuzzerExtFunctions.h" #include "FuzzerIO.h" +#include <stdlib.h> using namespace fuzzer; @@ -22,6 +23,11 @@ using namespace fuzzer; #define STRINGIFY(A) STRINGIFY_(A) #if LIBFUZZER_MSVC +#define GET_FUNCTION_ADDRESS(fn) &fn +#else +#define GET_FUNCTION_ADDRESS(fn) __builtin_function_start(fn) +#endif // LIBFUZER_MSVC + // Copied from compiler-rt/lib/sanitizer_common/sanitizer_win_defs.h #if defined(_M_IX86) || defined(__i386__) #define WIN_SYM_PREFIX "_" @@ -29,19 +35,11 @@ using namespace fuzzer; #define WIN_SYM_PREFIX #endif -// Declare external functions as having alternativenames, so that we can +// Declare external functions as having alternative names, so that we can // determine if they are not defined. -#define EXTERNAL_FUNC(Name, Default) \ - __pragma(comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \ +#define EXTERNAL_FUNC(Name, Default) \ + __pragma(comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \ Name) "=" WIN_SYM_PREFIX STRINGIFY(Default))) -#else -// Declare external functions as weak to allow them to default to a specified -// function if not defined explicitly. We must use weak symbols because clang's -// support for alternatename is not 100%, see -// https://bugs.llvm.org/show_bug.cgi?id=40218 for more details. -#define EXTERNAL_FUNC(Name, Default) \ - __attribute__((weak, alias(STRINGIFY(Default)))) -#endif // LIBFUZZER_MSVC extern "C" { #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ @@ -57,20 +55,23 @@ extern "C" { } template <typename T> -static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) { +static T *GetFnPtr(void *Fun, void *FunDef, const char *FnName, + bool WarnIfMissing) { if (Fun == FunDef) { if (WarnIfMissing) Printf("WARNING: Failed to find function \"%s\".\n", FnName); return nullptr; } - return Fun; + return (T *)Fun; } namespace fuzzer { ExternalFunctions::ExternalFunctions() { -#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ - this->NAME = GetFnPtr<decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN); +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = GetFnPtr<decltype(::NAME)>(GET_FUNCTION_ADDRESS(::NAME), \ + GET_FUNCTION_ADDRESS(::NAME##Def), \ + #NAME, WARN); #include "FuzzerExtFunctions.def" diff --git a/tools/fuzzing/libfuzzer/FuzzerExtraCounters.cpp b/tools/fuzzing/libfuzzer/FuzzerExtraCounters.cpp @@ -12,7 +12,7 @@ #include <cstdint> #if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \ - LIBFUZZER_OPENBSD || LIBFUZZER_FUCHSIA || LIBFUZZER_EMSCRIPTEN + LIBFUZZER_FUCHSIA || LIBFUZZER_EMSCRIPTEN __attribute__((weak)) extern uint8_t __start___libfuzzer_extra_counters; __attribute__((weak)) extern uint8_t __stop___libfuzzer_extra_counters; @@ -31,12 +31,4 @@ void ClearExtraCounters() { // hand-written memset, don't asan-ify. } // namespace fuzzer -#else -// TODO: implement for other platforms. -namespace fuzzer { -uint8_t *ExtraCountersBegin() { return nullptr; } -uint8_t *ExtraCountersEnd() { return nullptr; } -void ClearExtraCounters() {} -} // namespace fuzzer - #endif diff --git a/tools/fuzzing/libfuzzer/FuzzerExtraCountersDarwin.cpp b/tools/fuzzing/libfuzzer/FuzzerExtraCountersDarwin.cpp @@ -0,0 +1,22 @@ +//===- FuzzerExtraCountersDarwin.cpp - Extra coverage counters for Darwin -===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// Extra coverage counters defined by user code for Darwin. +//===----------------------------------------------------------------------===// + +#include "FuzzerPlatform.h" +#include <cstdint> + +#if LIBFUZZER_APPLE + +namespace fuzzer { +uint8_t *ExtraCountersBegin() { return nullptr; } +uint8_t *ExtraCountersEnd() { return nullptr; } +void ClearExtraCounters() {} +} // namespace fuzzer + +#endif diff --git a/tools/fuzzing/libfuzzer/FuzzerExtraCountersWindows.cpp b/tools/fuzzing/libfuzzer/FuzzerExtraCountersWindows.cpp @@ -0,0 +1,80 @@ +//===- FuzzerExtraCountersWindows.cpp - Extra coverage counters for Win32 -===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// Extra coverage counters defined by user code for Windows. +//===----------------------------------------------------------------------===// + +#include "FuzzerPlatform.h" +#include <cstdint> + +#if LIBFUZZER_WINDOWS +#include <windows.h> + +namespace fuzzer { + +// +// The __start___libfuzzer_extra_counters variable is align 16, size 16 to +// ensure the padding between it and the next variable in this section (either +// __libfuzzer_extra_counters or __stop___libfuzzer_extra_counters) will be +// located at (__start___libfuzzer_extra_counters + +// sizeof(__start___libfuzzer_extra_counters)). Otherwise, the calculation of +// (stop - (start + sizeof(start))) might be skewed. +// +// The section name, __libfuzzer_extra_countaaa ends with "aaa", so it sorts +// before __libfuzzer_extra_counters alphabetically. We want the start symbol to +// be placed in the section just before the user supplied counters (if present). +// +#pragma section(".data$__libfuzzer_extra_countaaa") +ATTRIBUTE_ALIGNED(16) +__declspec(allocate(".data$__libfuzzer_extra_countaaa")) uint8_t + __start___libfuzzer_extra_counters[16] = {0}; + +// +// Example of what the user-supplied counters should look like. First, the +// pragma to create the section name. It will fall alphabetically between +// ".data$__libfuzzer_extra_countaaa" and ".data$__libfuzzer_extra_countzzz". +// Next, the declspec to allocate the variable inside the specified section. +// Finally, some array, struct, whatever that is used to track the counter data. +// The size of this variable is computed at runtime by finding the difference of +// __stop___libfuzzer_extra_counters and __start___libfuzzer_extra_counters + +// sizeof(__start___libfuzzer_extra_counters). +// + +// +// #pragma section(".data$__libfuzzer_extra_counters") +// __declspec(allocate(".data$__libfuzzer_extra_counters")) +// uint8_t any_name_variable[64 * 1024]; +// + +// +// Here, the section name, __libfuzzer_extra_countzzz ends with "zzz", so it +// sorts after __libfuzzer_extra_counters alphabetically. We want the stop +// symbol to be placed in the section just after the user supplied counters (if +// present). Align to 1 so there isn't any padding placed between this and the +// previous variable. +// +#pragma section(".data$__libfuzzer_extra_countzzz") +ATTRIBUTE_ALIGNED(1) +__declspec(allocate(".data$__libfuzzer_extra_countzzz")) uint8_t + __stop___libfuzzer_extra_counters = 0; + +uint8_t *ExtraCountersBegin() { + return __start___libfuzzer_extra_counters + + sizeof(__start___libfuzzer_extra_counters); +} + +uint8_t *ExtraCountersEnd() { return &__stop___libfuzzer_extra_counters; } + +ATTRIBUTE_NO_SANITIZE_ALL +void ClearExtraCounters() { + uint8_t *Beg = ExtraCountersBegin(); + SecureZeroMemory(Beg, ExtraCountersEnd() - Beg); +} + +} // namespace fuzzer + +#endif diff --git a/tools/fuzzing/libfuzzer/FuzzerFlags.def b/tools/fuzzing/libfuzzer/FuzzerFlags.def @@ -14,8 +14,9 @@ FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.") FUZZER_FLAG_INT(runs, -1, "Number of individual test runs (-1 for infinite runs).") FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. " - "If 0, libFuzzer tries to guess a good value based on the corpus " - "and reports it. ") + "Contents of corpus files are going to be truncated to this value. " + "If 0, libFuzzer tries to guess a good value based on the corpus " + "and reports it.") FUZZER_FLAG_INT(len_control, 100, "Try generating small inputs first, " "then try larger inputs over time. Specifies the rate at which the length " "limit is increased (smaller == faster). If 0, immediately try inputs with " @@ -23,7 +24,21 @@ FUZZER_FLAG_INT(len_control, 100, "Try generating small inputs first, " FUZZER_FLAG_STRING(seed_inputs, "A comma-separated list of input files " "to use as an additional seed corpus. Alternatively, an \"@\" followed by " "the name of a file containing the comma-separated list.") +FUZZER_FLAG_INT(keep_seed, 0, "If 1, keep seed inputs in the corpus even if " + "they do not produce new coverage. When used with |reduce_inputs==1|, the " + "seed inputs will never be reduced. This option can be useful when seeds are" + "not properly formed for the fuzz target but still have useful snippets.") FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.") +FUZZER_FLAG_INT(cross_over_uniform_dist, 0, "Experimental. If 1, use a " + "uniform probability distribution when choosing inputs to cross over with. " + "Some of the inputs in the corpus may never get chosen for mutation " + "depending on the input mutation scheduling policy. With this flag, all " + "inputs, regardless of the input mutation scheduling policy, can be chosen " + "as an input to cross over with. This can be particularly useful with " + "|keep_seed==1|; all the initial seed inputs, even though they do not " + "increase coverage because they are not properly formed, will still be " + "chosen as an input to cross over with.") + FUZZER_FLAG_INT(mutate_depth, 5, "Apply this number of consecutive mutations to each input.") FUZZER_FLAG_INT(reduce_depth, 0, "Experimental/internal. " @@ -44,12 +59,21 @@ FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total " FUZZER_FLAG_INT(help, 0, "Print help.") FUZZER_FLAG_INT(fork, 0, "Experimental mode where fuzzing happens " "in a subprocess") +FUZZER_FLAG_INT(fork_corpus_groups, 0, "For fork mode, enable the corpus-group " + "strategy, The main corpus will be grouped according to size, " + "and each sub-process will randomly select seeds from different " + "groups as the sub-corpus.") FUZZER_FLAG_INT(ignore_timeouts, 1, "Ignore timeouts in fork mode") FUZZER_FLAG_INT(ignore_ooms, 1, "Ignore OOMs in fork mode") FUZZER_FLAG_INT(ignore_crashes, 0, "Ignore crashes in fork mode") 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(set_cover_merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be " + "merged into the 1-st corpus. Same as the 'merge' flag, but uses the " + "standard greedy algorithm for the set cover problem to " + "compute an approximation of the minimum set of testcases that " + "provide the same coverage as the initial corpora") FUZZER_FLAG_STRING(stop_file, "Stop fuzzing ASAP if this file exists") FUZZER_FLAG_STRING(merge_inner, "internal flag") FUZZER_FLAG_STRING(merge_control_file, @@ -74,6 +98,11 @@ FUZZER_FLAG_STRING(features_dir, "internal flag. Used to dump feature sets on di "Every time a new input is added to the corpus, a corresponding file in the features_dir" " is created containing the unique features of that input." " Features are stored in binary format.") +FUZZER_FLAG_STRING(mutation_graph_file, "Saves a graph (in DOT format) to" + " mutation_graph_file. The graph contains a vertex for each input that has" + " unique coverage; directed edges are provided between parents and children" + " where the child has unique coverage, and are recorded with the type of" + " mutation that caused the child.") FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters") FUZZER_FLAG_INT(use_memmem, 1, "Use hints from intercepting memmem, strstr, etc") @@ -113,6 +142,8 @@ FUZZER_FLAG_INT(print_corpus_stats, 0, "If 1, print statistics on corpus elements at exit.") FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text" " at exit.") +FUZZER_FLAG_INT(print_full_coverage, 0, "If 1, print full coverage information " + "(all branches) as text at exit.") FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated.") FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.") @@ -121,9 +152,12 @@ FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.") FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.") FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.") FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.") +FUZZER_FLAG_INT(handle_trap, 1, "If 1, try to intercept SIGTRAP.") 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(handle_winexcept, 1, "If 1, try to intercept uncaught Windows " + "Visual C++ Exceptions.") 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.") @@ -135,7 +169,7 @@ FUZZER_FLAG_INT(purge_allocator_interval, 1, "Purge allocator caches and " "purge_allocator_interval=-1 to disable this functionality.") FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. " "If >= 2 will also print stack traces.") -FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon" +FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon " "reaching this limit of RSS memory usage.") FUZZER_FLAG_INT(malloc_limit_mb, 0, "If non-zero, the fuzzer will exit " "if the target tries to allocate this number of Mb with one malloc call. " @@ -152,8 +186,9 @@ FUZZER_FLAG_INT(ignore_remaining_args, 0, "If 1, ignore all arguments passed " FUZZER_FLAG_STRING(focus_function, "Experimental. " "Fuzzing will focus on inputs that trigger calls to this function. " "If -focus_function=auto and -data_flow_trace is used, libFuzzer " - "will choose the focus functions automatically.") -FUZZER_FLAG_INT(entropic, 0, "Experimental. Enables entropic power schedule.") + "will choose the focus functions automatically. Disables -entropic when " + "specified.") +FUZZER_FLAG_INT(entropic, 1, "Enables entropic power schedule.") FUZZER_FLAG_INT(entropic_feature_frequency_threshold, 0xFF, "Experimental. If " "entropic is enabled, all features which are observed less often than " "the specified value are considered as rare.") @@ -161,9 +196,18 @@ FUZZER_FLAG_INT(entropic_number_of_rarest_features, 100, "Experimental. If " "entropic is enabled, we keep track of the frequencies only for the " "Top-X least abundant features (union features that are considered as " "rare).") +FUZZER_FLAG_INT(entropic_scale_per_exec_time, 0, "Experimental. If 1, " + "the Entropic power schedule gets scaled based on the input execution " + "time. Inputs with lower execution time get scheduled more (up to 30x). " + "Note that, if 1, fuzzer stops from being deterministic even if a " + "non-zero random seed is given.") FUZZER_FLAG_INT(analyze_dict, 0, "Experimental") FUZZER_DEPRECATED_FLAG(use_clang_coverage) FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace") FUZZER_FLAG_STRING(collect_data_flow, "Experimental: collect the data flow trace") + +FUZZER_FLAG_INT(create_missing_dirs, 0, "Automatically attempt to create " + "directories for arguments that would normally expect them to already " + "exist (i.e. artifact_prefix, exact_artifact_path, features_dir, corpus)") diff --git a/tools/fuzzing/libfuzzer/FuzzerFork.cpp b/tools/fuzzing/libfuzzer/FuzzerFork.cpp @@ -86,18 +86,21 @@ struct FuzzJob { }; struct GlobalEnv { - Vector<std::string> Args; - Vector<std::string> CorpusDirs; + std::vector<std::string> Args; + std::vector<std::string> CorpusDirs; std::string MainCorpusDir; std::string TempDir; std::string DFTDir; std::string DataFlowBinary; - Set<uint32_t> Features, Cov; - Set<std::string> FilesWithDFT; - Vector<std::string> Files; + std::set<uint32_t> Features, Cov; + std::set<std::string> FilesWithDFT; + std::vector<std::string> Files; + std::vector<std::size_t> FilesSizes; Random *Rand; std::chrono::system_clock::time_point ProcessStartTime; int Verbosity = 0; + int Group = 0; + int NumCorpuses = 8; size_t NumTimeouts = 0; size_t NumOOMs = 0; @@ -136,13 +139,29 @@ struct GlobalEnv { if (size_t CorpusSubsetSize = std::min(Files.size(), (size_t)sqrt(Files.size() + 2))) { auto Time1 = std::chrono::system_clock::now(); - for (size_t i = 0; i < CorpusSubsetSize; i++) { - auto &SF = Files[Rand->SkewTowardsLast(Files.size())]; - Seeds += (Seeds.empty() ? "" : ",") + SF; - CollectDFT(SF); + if (Group) { // whether to group the corpus. + size_t AverageCorpusSize = Files.size() / NumCorpuses + 1; + size_t StartIndex = ((JobId - 1) % NumCorpuses) * AverageCorpusSize; + for (size_t i = 0; i < CorpusSubsetSize; i++) { + size_t RandNum = (*Rand)(AverageCorpusSize); + size_t Index = RandNum + StartIndex; + Index = Index < Files.size() ? Index + : Rand->SkewTowardsLast(Files.size()); + auto &SF = Files[Index]; + Seeds += (Seeds.empty() ? "" : ",") + SF; + CollectDFT(SF); + } + } else { + for (size_t i = 0; i < CorpusSubsetSize; i++) { + auto &SF = Files[Rand->SkewTowardsLast(Files.size())]; + Seeds += (Seeds.empty() ? "" : ",") + SF; + CollectDFT(SF); + } } auto Time2 = std::chrono::system_clock::now(); - Job->DftTimeInSeconds = duration_cast<seconds>(Time2 - Time1).count(); + auto DftTimeInSeconds = duration_cast<seconds>(Time2 - Time1).count(); + assert(DftTimeInSeconds < std::numeric_limits<int>::max()); + Job->DftTimeInSeconds = static_cast<int>(DftTimeInSeconds); } if (!Seeds.empty()) { Job->SeedListPath = @@ -177,23 +196,21 @@ struct GlobalEnv { return Job; } - int RunOneMergeJob(FuzzJob *Job) { + void RunOneMergeJob(FuzzJob *Job) { auto Stats = ParseFinalStatsFromLog(Job->LogPath); NumRuns += Stats.number_of_executed_units; - Vector<SizedFile> TempFiles, MergeCandidates; + std::vector<SizedFile> TempFiles, MergeCandidates; // Read all newly created inputs and their feature sets. // Choose only those inputs that have new features. - int Res = GetSizedFilesFromDir(Job->CorpusDir, &TempFiles); - if (Res != 0) - return Res; + GetSizedFilesFromDir(Job->CorpusDir, &TempFiles); std::sort(TempFiles.begin(), TempFiles.end()); for (auto &F : TempFiles) { auto FeatureFile = F.File; FeatureFile.replace(0, Job->CorpusDir.size(), Job->FeaturesDir); auto FeatureBytes = FileToVector(FeatureFile, 0, false); assert((FeatureBytes.size() % sizeof(uint32_t)) == 0); - Vector<uint32_t> NewFeatures(FeatureBytes.size() / sizeof(uint32_t)); + std::vector<uint32_t> NewFeatures(FeatureBytes.size() / sizeof(uint32_t)); memcpy(NewFeatures.data(), FeatureBytes.data(), FeatureBytes.size()); for (auto Ft : NewFeatures) { if (!Features.count(Ft)) { @@ -203,25 +220,35 @@ struct GlobalEnv { } } // if (!FilesToAdd.empty() || Job->ExitCode != 0) - Printf("#%zd: cov: %zd ft: %zd corp: %zd exec/s %zd " + Printf("#%zd: cov: %zd ft: %zd corp: %zd exec/s: %zd " "oom/timeout/crash: %zd/%zd/%zd time: %zds job: %zd dft_time: %d\n", NumRuns, Cov.size(), Features.size(), Files.size(), Stats.average_exec_per_sec, NumOOMs, NumTimeouts, NumCrashes, secondsSinceProcessStartUp(), Job->JobId, Job->DftTimeInSeconds); - if (MergeCandidates.empty()) return 0; + if (MergeCandidates.empty()) return; - Vector<std::string> FilesToAdd; - Set<uint32_t> NewFeatures, NewCov; + std::vector<std::string> FilesToAdd; + std::set<uint32_t> NewFeatures, NewCov; + bool IsSetCoverMerge = + !Job->Cmd.getFlagValue("set_cover_merge").compare("1"); CrashResistantMerge(Args, {}, MergeCandidates, &FilesToAdd, Features, - &NewFeatures, Cov, &NewCov, Job->CFPath, false); - if (Fuzzer::isGracefulExitRequested()) - return 0; + &NewFeatures, Cov, &NewCov, Job->CFPath, false, + IsSetCoverMerge); for (auto &Path : FilesToAdd) { auto U = FileToVector(Path); auto NewPath = DirPlusFile(MainCorpusDir, Hash(U)); WriteToFile(U, NewPath); - Files.push_back(NewPath); + if (Group) { // Insert the queue according to the size of the seed. + size_t UnitSize = U.size(); + auto Idx = + std::upper_bound(FilesSizes.begin(), FilesSizes.end(), UnitSize) - + FilesSizes.begin(); + FilesSizes.insert(FilesSizes.begin() + Idx, UnitSize); + Files.insert(Files.begin() + Idx, NewPath); + } else { + Files.push_back(NewPath); + } } Features.insert(NewFeatures.begin(), NewFeatures.end()); Cov.insert(NewCov.begin(), NewCov.end()); @@ -230,10 +257,8 @@ struct GlobalEnv { if (TPC.PcIsFuncEntry(TE)) PrintPC(" NEW_FUNC: %p %F %L\n", "", TPC.GetNextInstructionPc(TE->PC)); - return 0; } - void CollectDFT(const std::string &InputPath) { if (DataFlowBinary.empty()) return; if (!FilesWithDFT.insert(InputPath).second) return; @@ -284,9 +309,9 @@ void WorkerThread(JobQueue *FuzzQ, JobQueue *MergeQ) { } // This is just a skeleton of an experimental -fork=1 feature. -int FuzzWithFork(Random &Rand, const FuzzingOptions &Options, - const Vector<std::string> &Args, - const Vector<std::string> &CorpusDirs, int NumJobs) { +void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + const std::vector<std::string> &Args, + const std::vector<std::string> &CorpusDirs, int NumJobs) { Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs); GlobalEnv Env; @@ -296,14 +321,11 @@ int FuzzWithFork(Random &Rand, const FuzzingOptions &Options, Env.Verbosity = Options.Verbosity; Env.ProcessStartTime = std::chrono::system_clock::now(); Env.DataFlowBinary = Options.CollectDataFlow; + Env.Group = Options.ForkCorpusGroups; - Vector<SizedFile> SeedFiles; - int Res; - for (auto &Dir : CorpusDirs) { - Res = GetSizedFilesFromDir(Dir, &SeedFiles); - if (Res != 0) - return Res; - } + std::vector<SizedFile> SeedFiles; + for (auto &Dir : CorpusDirs) + GetSizedFilesFromDir(Dir, &SeedFiles); std::sort(SeedFiles.begin(), SeedFiles.end()); Env.TempDir = TempPath("FuzzWithFork", ".dir"); Env.DFTDir = DirPlusFile(Env.TempDir, "DFT"); @@ -317,16 +339,25 @@ int FuzzWithFork(Random &Rand, const FuzzingOptions &Options, else Env.MainCorpusDir = CorpusDirs[0]; - auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); - Res = CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features, - {}, &Env.Cov, - CFPath, false); - if (Res != 0) - return Res; - if (Fuzzer::isGracefulExitRequested()) - return 0; + if (Options.KeepSeed) { + for (auto &File : SeedFiles) + Env.Files.push_back(File.File); + } else { + auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); + std::set<uint32_t> NewFeatures, NewCov; + CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, Env.Features, + &NewFeatures, Env.Cov, &NewCov, CFPath, + /*Verbose=*/false, /*IsSetCoverMerge=*/false); + Env.Features.insert(NewFeatures.begin(), NewFeatures.end()); + Env.Cov.insert(NewCov.begin(), NewCov.end()); + RemoveFile(CFPath); + } + + if (Env.Group) { + for (auto &path : Env.Files) + Env.FilesSizes.push_back(FileSize(path)); + } - RemoveFile(CFPath); Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs, Env.Files.size(), Env.TempDir.c_str()); @@ -341,8 +372,10 @@ int FuzzWithFork(Random &Rand, const FuzzingOptions &Options, WriteToFile(Unit({1}), Env.StopFile()); }; + size_t MergeCycle = 20; + size_t JobExecuted = 0; size_t JobId = 1; - Vector<std::thread> Threads; + std::vector<std::thread> Threads; for (int t = 0; t < NumJobs; t++) { Threads.push_back(std::thread(WorkerThread, &FuzzQ, &MergeQ)); FuzzQ.Push(Env.CreateNewJob(JobId++)); @@ -358,16 +391,50 @@ int FuzzWithFork(Random &Rand, const FuzzingOptions &Options, StopJobs(); break; } - if (Fuzzer::MaybeExitGracefully()) - return 0; - - Res = Env.RunOneMergeJob(Job.get()); - if (Res != 0) - return Res; - if (Fuzzer::isGracefulExitRequested()) - return 0; + Fuzzer::MaybeExitGracefully(); + + Env.RunOneMergeJob(Job.get()); + + // merge the corpus . + JobExecuted++; + if (Env.Group && JobExecuted >= MergeCycle) { + std::vector<SizedFile> CurrentSeedFiles; + for (auto &Dir : CorpusDirs) + GetSizedFilesFromDir(Dir, &CurrentSeedFiles); + std::sort(CurrentSeedFiles.begin(), CurrentSeedFiles.end()); + + auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); + std::set<uint32_t> TmpNewFeatures, TmpNewCov; + std::set<uint32_t> TmpFeatures, TmpCov; + Env.Files.clear(); + Env.FilesSizes.clear(); + CrashResistantMerge(Env.Args, {}, CurrentSeedFiles, &Env.Files, + TmpFeatures, &TmpNewFeatures, TmpCov, &TmpNewCov, + CFPath, /*Verbose=*/false, /*IsSetCoverMerge=*/false); + for (auto &path : Env.Files) + Env.FilesSizes.push_back(FileSize(path)); + RemoveFile(CFPath); + JobExecuted = 0; + MergeCycle += 5; + } - // Continue if our crash is one of the ignorred ones. + // Since the number of corpus seeds will gradually increase, in order to + // control the number in each group to be about three times the number of + // seeds selected each time, the number of groups is dynamically adjusted. + if (Env.Files.size() < 2000) + Env.NumCorpuses = 12; + else if (Env.Files.size() < 6000) + Env.NumCorpuses = 20; + else if (Env.Files.size() < 12000) + Env.NumCorpuses = 32; + else if (Env.Files.size() < 16000) + Env.NumCorpuses = 40; + else if (Env.Files.size() < 24000) + Env.NumCorpuses = 60; + else + Env.NumCorpuses = 80; + + // Continue if our crash is one of the ignored ones. if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode) Env.NumTimeouts++; else if (Options.IgnoreOOMs && ExitCode == Options.OOMExitCode) @@ -421,7 +488,7 @@ int FuzzWithFork(Random &Rand, const FuzzingOptions &Options, // Use the exit code from the last child process. Printf("INFO: exiting: %d time: %zds\n", ExitCode, Env.secondsSinceProcessStartUp()); - return ExitCode; + exit(ExitCode); } } // namespace fuzzer diff --git a/tools/fuzzing/libfuzzer/FuzzerFork.h b/tools/fuzzing/libfuzzer/FuzzerFork.h @@ -16,9 +16,9 @@ #include <string> namespace fuzzer { -int FuzzWithFork(Random &Rand, const FuzzingOptions &Options, - const Vector<std::string> &Args, - const Vector<std::string> &CorpusDirs, int NumJobs); +void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + const std::vector<std::string> &Args, + const std::vector<std::string> &CorpusDirs, int NumJobs); } // namespace fuzzer #endif // LLVM_FUZZER_FORK_H diff --git a/tools/fuzzing/libfuzzer/FuzzerIO.cpp b/tools/fuzzing/libfuzzer/FuzzerIO.cpp @@ -23,6 +23,14 @@ namespace fuzzer { static FILE *OutputFile = stderr; +FILE *GetOutputFile() { + return OutputFile; +} + +void SetOutputFile(FILE *NewOutputFile) { + OutputFile = NewOutputFile; +} + long GetEpoch(const std::string &Path) { struct stat St; if (stat(Path.c_str(), &St)) @@ -57,7 +65,7 @@ std::string FileToString(const std::string &Path) { } void CopyFileToErr(const std::string &Path) { - Printf("%s", FileToString(Path).c_str()); + Puts(FileToString(Path).c_str()); } void WriteToFile(const Unit &U, const std::string &Path) { @@ -73,17 +81,29 @@ void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path) { // Use raw C interface because this function may be called from a sig handler. FILE *Out = fopen(Path.c_str(), "wb"); if (!Out) return; - (void)fwrite(Data, sizeof(Data[0]), Size, Out); + fwrite(Data, sizeof(Data[0]), Size, Out); fclose(Out); } -void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, - long *Epoch, size_t MaxSize, bool ExitOnError) { +void AppendToFile(const std::string &Data, const std::string &Path) { + AppendToFile(reinterpret_cast<const uint8_t *>(Data.data()), Data.size(), + Path); +} + +void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path) { + FILE *Out = fopen(Path.c_str(), "a"); + if (!Out) + return; + fwrite(Data, sizeof(Data[0]), Size, Out); + fclose(Out); +} + +void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V, long *Epoch, + size_t MaxSize, bool ExitOnError, + std::vector<std::string> *VPaths) { long E = Epoch ? *Epoch : 0; - Vector<std::string> Files; - int Res = ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true); - if (ExitOnError && Res != 0) - exit(Res); + std::vector<std::string> Files; + ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true); size_t NumLoaded = 0; for (size_t i = 0; i < Files.size(); i++) { auto &X = Files[i]; @@ -92,21 +112,20 @@ void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024) Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path); auto S = FileToVector(X, MaxSize, ExitOnError); - if (!S.empty()) + if (!S.empty()) { V->push_back(S); + if (VPaths) + VPaths->push_back(X); + } } } - -int GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V) { - Vector<std::string> Files; - int Res = ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/true); - if (Res != 0) - return Res; +void GetSizedFilesFromDir(const std::string &Dir, std::vector<SizedFile> *V) { + std::vector<std::string> Files; + ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/true); for (auto &File : Files) if (size_t Size = FileSize(File)) V->push_back({File, Size}); - return 0; } std::string DirPlusFile(const std::string &DirPath, @@ -132,6 +151,11 @@ void CloseStdout() { DiscardOutput(1); } +void Puts(const char *Str) { + fputs(Str, OutputFile); + fflush(OutputFile); +} + void Printf(const char *Fmt, ...) { va_list ap; va_start(ap, Fmt); @@ -149,6 +173,38 @@ void VPrintf(bool Verbose, const char *Fmt, ...) { fflush(OutputFile); } +static bool MkDirRecursiveInner(const std::string &Leaf) { + // Prevent chance of potential infinite recursion + if (Leaf == ".") + return true; + + const std::string &Dir = DirName(Leaf); + + if (IsDirectory(Dir)) { + MkDir(Leaf); + return IsDirectory(Leaf); + } + + bool ret = MkDirRecursiveInner(Dir); + if (!ret) { + // Give up early if a previous MkDir failed + return ret; + } + + MkDir(Leaf); + return IsDirectory(Leaf); +} + +bool MkDirRecursive(const std::string &Dir) { + if (Dir.empty()) + return false; + + if (IsDirectory(Dir)) + return true; + + return MkDirRecursiveInner(Dir); +} + void RmDirRecursive(const std::string &Dir) { IterateDirRecursive( Dir, [](const std::string &Path) {}, diff --git a/tools/fuzzing/libfuzzer/FuzzerIO.h b/tools/fuzzing/libfuzzer/FuzzerIO.h @@ -29,8 +29,12 @@ void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path); void WriteToFile(const std::string &Data, const std::string &Path); void WriteToFile(const Unit &U, const std::string &Path); -void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, - long *Epoch, size_t MaxSize, bool ExitOnError); +void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path); +void AppendToFile(const std::string &Data, const std::string &Path); + +void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V, long *Epoch, + size_t MaxSize, bool ExitOnError, + std::vector<std::string> *VPaths = 0); // Returns "Dir/FileName" or equivalent for the current OS. std::string DirPlusFile(const std::string &DirPath, @@ -50,6 +54,11 @@ void DupAndCloseStderr(); void CloseStdout(); +// For testing. +FILE *GetOutputFile(); +void SetOutputFile(FILE *NewOutputFile); + +void Puts(const char *Str); void Printf(const char *Fmt, ...); void VPrintf(bool Verbose, const char *Fmt, ...); @@ -58,11 +67,13 @@ void RawPrint(const char *Str); // Platform specific functions: bool IsFile(const std::string &Path); +bool IsDirectory(const std::string &Path); size_t FileSize(const std::string &Path); -int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, - Vector<std::string> *V, bool TopDir); +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + std::vector<std::string> *V, bool TopDir); +bool MkDirRecursive(const std::string &Dir); void RmDirRecursive(const std::string &Dir); // Iterate files and dirs inside Dir, recursively. @@ -79,9 +90,10 @@ struct SizedFile { bool operator<(const SizedFile &B) const { return Size < B.Size; } }; -int GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V); +void GetSizedFilesFromDir(const std::string &Dir, std::vector<SizedFile> *V); char GetSeparator(); +bool IsSeparator(char C); // Similar to the basename utility: returns the file name w/o the dir prefix. std::string Basename(const std::string &Path); diff --git a/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp b/tools/fuzzing/libfuzzer/FuzzerIOPosix.cpp @@ -12,6 +12,7 @@ #include "FuzzerExtFunctions.h" #include "FuzzerIO.h" +#include <cerrno> #include <cstdarg> #include <cstdio> #include <dirent.h> @@ -31,7 +32,7 @@ bool IsFile(const std::string &Path) { return S_ISREG(St.st_mode); } -static bool IsDirectory(const std::string &Path) { +bool IsDirectory(const std::string &Path) { struct stat St; if (stat(Path.c_str(), &St)) return false; @@ -52,16 +53,16 @@ std::string Basename(const std::string &Path) { return Path.substr(Pos + 1); } -int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, - Vector<std::string> *V, bool TopDir) { +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + std::vector<std::string> *V, bool TopDir) { auto E = GetEpoch(Dir); if (Epoch) - if (E && *Epoch >= E) return 0; + if (E && *Epoch >= E) return; DIR *D = opendir(Dir.c_str()); if (!D) { Printf("%s: %s; exiting\n", strerror(errno), Dir.c_str()); - return 1; + exit(1); } while (auto E = readdir(D)) { std::string Path = DirPlusFile(Dir, E->d_name); @@ -70,19 +71,14 @@ int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, V->push_back(Path); else if ((E->d_type == DT_DIR || (E->d_type == DT_UNKNOWN && IsDirectory(Path))) && - *E->d_name != '.') { - int Res = ListFilesInDirRecursive(Path, Epoch, V, false); - if (Res != 0) - return Res; - } + *E->d_name != '.') + ListFilesInDirRecursive(Path, Epoch, V, false); } closedir(D); if (Epoch && TopDir) *Epoch = E; - return 0; } - void IterateDirRecursive(const std::string &Dir, void (*DirPreCallback)(const std::string &Dir), void (*DirPostCallback)(const std::string &Dir), @@ -108,6 +104,10 @@ char GetSeparator() { return '/'; } +bool IsSeparator(char C) { + return C == '/'; +} + FILE* OpenFile(int Fd, const char* Mode) { return fdopen(Fd, Mode); } diff --git a/tools/fuzzing/libfuzzer/FuzzerIOWindows.cpp b/tools/fuzzing/libfuzzer/FuzzerIOWindows.cpp @@ -76,6 +76,18 @@ static bool IsDir(DWORD FileAttrs) { return FileAttrs & FILE_ATTRIBUTE_DIRECTORY; } +bool IsDirectory(const std::string &Path) { + DWORD Att = GetFileAttributesA(Path.c_str()); + + if (Att == INVALID_FILE_ATTRIBUTES) { + Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), GetLastError()); + return false; + } + + return IsDir(Att); +} + std::string Basename(const std::string &Path) { size_t Pos = Path.find_last_of("/\\"); if (Pos == std::string::npos) return Path; @@ -98,12 +110,11 @@ size_t FileSize(const std::string &Path) { return size.QuadPart; } -int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, - Vector<std::string> *V, bool TopDir) { - int Res; +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + std::vector<std::string> *V, bool TopDir) { auto E = GetEpoch(Dir); if (Epoch) - if (E && *Epoch >= E) return 0; + if (E && *Epoch >= E) return; std::string Path(Dir); assert(!Path.empty()); @@ -117,9 +128,9 @@ int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, if (FindHandle == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_FILE_NOT_FOUND) - return 0; + return; Printf("No such file or directory: %s; exiting\n", Dir.c_str()); - return 1; + exit(1); } do { @@ -132,9 +143,7 @@ int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, FindInfo.cFileName[1] == '.')) continue; - int Res = ListFilesInDirRecursive(FileName, Epoch, V, false); - if (Res != 0) - return Res; + ListFilesInDirRecursive(FileName, Epoch, V, false); } else if (IsFile(FileName, FindInfo.dwFileAttributes)) V->push_back(FileName); @@ -148,10 +157,8 @@ int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, if (Epoch && TopDir) *Epoch = E; - return 0; } - void IterateDirRecursive(const std::string &Dir, void (*DirPreCallback)(const std::string &Dir), void (*DirPostCallback)(const std::string &Dir), @@ -231,7 +238,7 @@ intptr_t GetHandleFromFd(int fd) { return _get_osfhandle(fd); } -static bool IsSeparator(char C) { +bool IsSeparator(char C) { return C == '\\' || C == '/'; } @@ -289,9 +296,8 @@ static size_t ParseServerAndShare(const std::string &FileName, return Pos - Offset; } -// Parse the given Ref string from the position Offset, to exactly match the given -// string Patt. -// Returns number of characters considered if successful. +// Parse the given Ref string from the position Offset, to exactly match the +// given string Patt. Returns number of characters considered if successful. static size_t ParseCustomString(const std::string &Ref, size_t Offset, const char *Patt) { size_t Len = strlen(Patt); diff --git a/tools/fuzzing/libfuzzer/FuzzerInterceptors.cpp b/tools/fuzzing/libfuzzer/FuzzerInterceptors.cpp @@ -25,9 +25,9 @@ } #include <cassert> +#include <cstddef> // for size_t #include <cstdint> #include <dlfcn.h> // for dlsym() -#include <sanitizer/common_interface_defs.h> static void *getFuncAddr(const char *name, uintptr_t wrapper_addr) { void *addr = dlsym(RTLD_NEXT, name); @@ -119,6 +119,25 @@ static char *internal_strstr(const char *haystack, const char *needle) { extern "C" { +// Weak hooks forward-declared to avoid dependency on +// <sanitizer/common_interface_defs.h>. +void __sanitizer_weak_hook_memcmp(void *called_pc, const void *s1, + const void *s2, size_t n, int result); +void __sanitizer_weak_hook_strncmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result); +void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result); +void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1, + const char *s2, int result); +void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, + const char *s2, int result); +void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1, + const char *s2, char *result); +void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, + const char *s2, char *result); +void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1, + const void *s2, size_t len2, void *result); + DEFINE_REAL(int, bcmp, const void *, const void *, size_t) DEFINE_REAL(int, memcmp, const void *, const void *, size_t) DEFINE_REAL(int, strncmp, const char *, const char *, size_t) diff --git a/tools/fuzzing/libfuzzer/FuzzerInternal.h b/tools/fuzzing/libfuzzer/FuzzerInternal.h @@ -29,14 +29,13 @@ namespace fuzzer { using namespace std::chrono; -class Fuzzer { +class Fuzzer final { public: - Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, - FuzzingOptions Options); - ~Fuzzer(); - int Loop(Vector<SizedFile> &CorporaFiles); - int ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles); + const FuzzingOptions &Options); + ~Fuzzer() = delete; + void Loop(std::vector<SizedFile> &CorporaFiles); + void ReadAndExecuteSeedCorpora(std::vector<SizedFile> &CorporaFiles); void MinimizeCrashLoop(const Unit &U); void RereadOutputCorpus(size_t MaxSize); @@ -65,16 +64,19 @@ public: static void StaticFileSizeExceedCallback(); static void StaticGracefulExitCallback(); - static void GracefullyExit(); - static bool isGracefulExitRequested(); - - int ExecuteCallback(const uint8_t *Data, size_t Size); + // Executes the target callback on {Data, Size} once. + // Returns false if the input was rejected by the target (target returned -1), + // and true otherwise. + bool ExecuteCallback(const uint8_t *Data, size_t Size); bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false, - InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr); + InputInfo *II = nullptr, bool ForceAddToCorpus = false, + bool *FoundUniqFeatures = nullptr); + void TPCUpdateObservedPCs(); // Merge Corpora[1:] into Corpora[0]. - void Merge(const Vector<std::string> &Corpora); - int CrashResistantMergeInternalStep(const std::string &ControlFilePath); + void Merge(const std::vector<std::string> &Corpora); + void CrashResistantMergeInternalStep(const std::string &ControlFilePath, + bool IsSetCoverMerge); MutationDispatcher &GetMD() { return MD; } void PrintFinalStats(); void SetMaxInputLen(size_t MaxInputLen); @@ -87,7 +89,8 @@ public: bool DuringInitialCorpusExecution); void HandleMalloc(size_t Size); - static bool MaybeExitGracefully(); + static void MaybeExitGracefully(); + static int InterruptExitCode(); std::string WriteToOutputCorpus(const Unit &U); private: @@ -96,7 +99,7 @@ private: void ExitCallback(); void CrashOnOverwrittenData(); void InterruptCallback(); - bool MutateAndTestOne(); + void MutateAndTestOne(); void PurgeAllocator(); void ReportNewCoverage(InputInfo *II, const Unit &U); void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size); @@ -142,7 +145,7 @@ private: size_t MaxMutationLen = 0; size_t TmpMaxMutationLen = 0; - Vector<uint32_t> UniqFeatureSetTmp; + std::vector<uint32_t> UniqFeatureSetTmp; // Need to know our own thread. static thread_local bool IsMyThread; diff --git a/tools/fuzzing/libfuzzer/FuzzerLoop.cpp b/tools/fuzzing/libfuzzer/FuzzerLoop.cpp @@ -125,8 +125,8 @@ void FreeHook(const volatile void *ptr) { void Fuzzer::HandleMalloc(size_t Size) { if (!Options.MallocLimitMb || (Size >> 20) < (size_t)Options.MallocLimitMb) return; - Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(), - Size); + Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", + (int)GetPid(), Size); Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n"); PrintStackTrace(); DumpCurrentUnit("oom-"); @@ -136,7 +136,7 @@ void Fuzzer::HandleMalloc(size_t Size) { } Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, - FuzzingOptions Options) + const FuzzingOptions &Options) : CB(CB), Corpus(Corpus), MD(MD), Options(Options) { if (EF->__sanitizer_set_death_callback) EF->__sanitizer_set_death_callback(StaticDeathCallback); @@ -160,8 +160,6 @@ Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, memset(BaseSha1, 0, sizeof(BaseSha1)); } -Fuzzer::~Fuzzer() {} - void Fuzzer::AllocateCurrentUnitData() { if (CurrentUnitData || MaxInputLen == 0) return; @@ -254,20 +252,17 @@ void Fuzzer::ExitCallback() { _Exit(Options.ErrorExitCode); } -bool Fuzzer::MaybeExitGracefully() { - if (!F->GracefulExitRequested) return false; +void Fuzzer::MaybeExitGracefully() { + if (!F->GracefulExitRequested) return; Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid()); RmDirRecursive(TempPath("FuzzWithFork", ".dir")); F->PrintFinalStats(); - return true; -} - -void Fuzzer::GracefullyExit() { - F->GracefulExitRequested = true; + _Exit(0); } -bool Fuzzer::isGracefulExitRequested() { - return F->GracefulExitRequested; +int Fuzzer::InterruptExitCode() { + assert(F); + return F->Options.InterruptExitCode; } void Fuzzer::InterruptCallback() { @@ -304,7 +299,7 @@ void Fuzzer::AlarmCallback() { Printf(" and the timeout value is %d (use -timeout=N to change)\n", Options.UnitTimeoutSec); DumpCurrentUnit("timeout-"); - Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(), + Printf("==%lu== ERROR: libFuzzer: timeout after %zu seconds\n", GetPid(), Seconds); PrintStackTrace(); Printf("SUMMARY: libFuzzer: timeout\n"); @@ -317,9 +312,8 @@ void Fuzzer::RssLimitCallback() { if (EF->__sanitizer_acquire_crash_state && !EF->__sanitizer_acquire_crash_state()) return; - Printf( - "==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n", - GetPid(), GetPeakRSSMb(), Options.RssLimitMb); + Printf("==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %dMb)\n", + GetPid(), GetPeakRSSMb(), Options.RssLimitMb); Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n"); PrintMemoryProfile(); DumpCurrentUnit("oom-"); @@ -362,8 +356,10 @@ void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units, } void Fuzzer::PrintFinalStats() { + if (Options.PrintFullCoverage) + TPC.PrintCoverage(/*PrintAllCounters=*/true); if (Options.PrintCoverage) - TPC.PrintCoverage(); + TPC.PrintCoverage(/*PrintAllCounters=*/false); if (Options.PrintCorpusStats) Corpus.PrintStats(); if (!Options.PrintFinalStats) @@ -372,7 +368,7 @@ void Fuzzer::PrintFinalStats() { Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns); Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec); Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded); - Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds); + Printf("stat::slowest_unit_time_sec: %ld\n", TimeOfLongestUnitInSeconds); Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb()); } @@ -394,7 +390,7 @@ void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) { void Fuzzer::CheckExitOnSrcPosOrItem() { if (!Options.ExitOnSrcPos.empty()) { - static auto *PCsSet = new Set<uintptr_t>; + static auto *PCsSet = new std::set<uintptr_t>; auto HandlePC = [&](const TracePC::PCTableEntry *TE) { if (!PCsSet->insert(TE->PC).second) return; @@ -419,20 +415,26 @@ void Fuzzer::CheckExitOnSrcPosOrItem() { void Fuzzer::RereadOutputCorpus(size_t MaxSize) { if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return; - Vector<Unit> AdditionalCorpus; - ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus, - &EpochOfLastReadOfOutputCorpus, MaxSize, - /*ExitOnError*/ false); + std::vector<Unit> AdditionalCorpus; + std::vector<std::string> AdditionalCorpusPaths; + ReadDirToVectorOfUnits( + Options.OutputCorpus.c_str(), &AdditionalCorpus, + &EpochOfLastReadOfOutputCorpus, MaxSize, + /*ExitOnError*/ false, + (Options.Verbosity >= 2 ? &AdditionalCorpusPaths : nullptr)); if (Options.Verbosity >= 2) Printf("Reload: read %zd new units.\n", AdditionalCorpus.size()); bool Reloaded = false; - for (auto &U : AdditionalCorpus) { + for (size_t i = 0; i != AdditionalCorpus.size(); ++i) { + auto &U = AdditionalCorpus[i]; if (U.size() > MaxSize) U.resize(MaxSize); if (!Corpus.HasUnit(U)) { if (RunOne(U.data(), U.size())) { CheckExitOnSrcPosOrItem(); Reloaded = true; + if (Options.Verbosity >= 2) + Printf("Reloaded %s\n", AdditionalCorpusPaths[i].c_str()); } } } @@ -446,17 +448,18 @@ void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) { if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && secondsSinceProcessStartUp() >= 2) PrintStats("pulse "); - if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 && - TimeOfUnit >= Options.ReportSlowUnits) { + auto Threshold = + static_cast<long>(static_cast<double>(TimeOfLongestUnitInSeconds) * 1.1); + if (TimeOfUnit > Threshold && TimeOfUnit >= Options.ReportSlowUnits) { TimeOfLongestUnitInSeconds = TimeOfUnit; - Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds); + Printf("Slowest unit: %ld s:\n", TimeOfLongestUnitInSeconds); WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-"); } } static void WriteFeatureSetToFile(const std::string &FeaturesDir, const std::string &FileName, - const Vector<uint32_t> &FeatureSet) { + const std::vector<uint32_t> &FeatureSet) { if (FeaturesDir.empty() || FeatureSet.empty()) return; WriteToFile(reinterpret_cast<const uint8_t *>(FeatureSet.data()), FeatureSet.size() * sizeof(FeatureSet[0]), @@ -471,24 +474,57 @@ static void RenameFeatureSetFile(const std::string &FeaturesDir, DirPlusFile(FeaturesDir, NewFile)); } +static void WriteEdgeToMutationGraphFile(const std::string &MutationGraphFile, + const InputInfo *II, + const InputInfo *BaseII, + const std::string &MS) { + if (MutationGraphFile.empty()) + return; + + std::string Sha1 = Sha1ToString(II->Sha1); + + std::string OutputString; + + // Add a new vertex. + OutputString.append("\""); + OutputString.append(Sha1); + OutputString.append("\"\n"); + + // Add a new edge if there is base input. + if (BaseII) { + std::string BaseSha1 = Sha1ToString(BaseII->Sha1); + OutputString.append("\""); + OutputString.append(BaseSha1); + OutputString.append("\" -> \""); + OutputString.append(Sha1); + OutputString.append("\" [label=\""); + OutputString.append(MS); + OutputString.append("\"];\n"); + } + + AppendToFile(OutputString, MutationGraphFile); +} + bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, - InputInfo *II, bool *FoundUniqFeatures) { + InputInfo *II, bool ForceAddToCorpus, + bool *FoundUniqFeatures) { if (!Size) return false; + // Largest input length should be INT_MAX. + assert(Size < std::numeric_limits<uint32_t>::max()); - if (ExecuteCallback(Data, Size)) { - return false; - } + if(!ExecuteCallback(Data, Size)) return false; + auto TimeOfUnit = duration_cast<microseconds>(UnitStopTime - UnitStartTime); UniqFeatureSetTmp.clear(); size_t FoundUniqFeaturesOfII = 0; size_t NumUpdatesBefore = Corpus.NumFeatureUpdates(); - TPC.CollectFeatures([&](size_t Feature) { - if (Corpus.AddFeature(Feature, Size, Options.Shrink)) + TPC.CollectFeatures([&](uint32_t Feature) { + if (Corpus.AddFeature(Feature, static_cast<uint32_t>(Size), Options.Shrink)) UniqFeatureSetTmp.push_back(Feature); if (Options.Entropic) Corpus.UpdateFeatureFrequency(II, Feature); - if (Options.ReduceInputs && II) + if (Options.ReduceInputs && II && !II->NeverReduce) if (std::binary_search(II->UniqFeatureSet.begin(), II->UniqFeatureSet.end(), Feature)) FoundUniqFeaturesOfII++; @@ -497,13 +533,16 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, *FoundUniqFeatures = FoundUniqFeaturesOfII; PrintPulseAndReportSlowInput(Data, Size); size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore; - if (NumNewFeatures) { + if (NumNewFeatures || ForceAddToCorpus) { TPC.UpdateObservedPCs(); - auto NewII = Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, - MayDeleteFile, TPC.ObservedFocusFunction(), - UniqFeatureSetTmp, DFT, II); + auto NewII = + Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile, + TPC.ObservedFocusFunction(), ForceAddToCorpus, + TimeOfUnit, UniqFeatureSetTmp, DFT, II); WriteFeatureSetToFile(Options.FeaturesDir, Sha1ToString(NewII->Sha1), NewII->UniqFeatureSet); + WriteEdgeToMutationGraphFile(Options.MutationGraphFile, NewII, II, + MD.MutationSequence()); return true; } if (II && FoundUniqFeaturesOfII && @@ -511,7 +550,7 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, FoundUniqFeaturesOfII == II->UniqFeatureSet.size() && II->U.size() > Size) { auto OldFeaturesFile = Sha1ToString(II->Sha1); - Corpus.Replace(II, {Data, Data + Size}); + Corpus.Replace(II, {Data, Data + Size}, TimeOfUnit); RenameFeatureSetFile(Options.FeaturesDir, OldFeaturesFile, Sha1ToString(II->Sha1)); return true; @@ -519,6 +558,8 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, return false; } +void Fuzzer::TPCUpdateObservedPCs() { TPC.UpdateObservedPCs(); } + size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { assert(InFuzzingThread()); *Data = CurrentUnitData; @@ -527,7 +568,7 @@ size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { void Fuzzer::CrashOnOverwrittenData() { Printf("==%d== ERROR: libFuzzer: fuzz target overwrites its const input\n", - GetPid()); + (int)GetPid()); PrintStackTrace(); Printf("SUMMARY: libFuzzer: overwrites-const-input\n"); DumpCurrentUnit("crash-"); @@ -538,6 +579,9 @@ void Fuzzer::CrashOnOverwrittenData() { // Compare two arrays, but not all bytes if the arrays are large. static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) { const size_t Limit = 64; + // memcmp cannot take null pointer arguments even if Size is 0. + if (!Size) + return true; if (Size <= 64) return !memcmp(A, B, Size); // Compare first and last Limit/2 bytes. @@ -545,14 +589,19 @@ static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) { !memcmp(A + Size - Limit / 2, B + Size - Limit / 2, Limit / 2); } -int Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { +// This method is not inlined because it would cause a test to fail where it +// is part of the stack unwinding. See D97975 for details. +ATTRIBUTE_NOINLINE bool Fuzzer::ExecuteCallback(const uint8_t *Data, + size_t Size) { TPC.RecordInitialStack(); TotalNumberOfRuns++; assert(InFuzzingThread()); // We copy the contents of Unit into a separate heap buffer // so that we reliably find buffer overflows in it. uint8_t *DataCopy = new uint8_t[Size]; - memcpy(DataCopy, Data, Size); + // memcpy cannot take null pointer arguments even if Size is 0. + if (Size) + memcpy(DataCopy, Data, Size); if (EF->__msan_unpoison) EF->__msan_unpoison(DataCopy, Size); if (EF->__msan_unpoison_param) @@ -560,23 +609,24 @@ int Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { if (CurrentUnitData && CurrentUnitData != Data) memcpy(CurrentUnitData, Data, Size); CurrentUnitSize = Size; - int Res = 0; + int CBRes = 0; { ScopedEnableMsanInterceptorChecks S; AllocTracer.Start(Options.TraceMalloc); UnitStartTime = system_clock::now(); TPC.ResetMaps(); RunningUserCallback = true; - Res = CB(DataCopy, Size); + CBRes = CB(DataCopy, Size); RunningUserCallback = false; UnitStopTime = system_clock::now(); + assert(CBRes == 0 || CBRes == -1); HasMoreMallocsThanFrees = AllocTracer.Stop(); } if (!LooseMemeq(DataCopy, Data, Size)) CrashOnOverwrittenData(); CurrentUnitSize = 0; delete[] DataCopy; - return Res; + return CBRes == 0; } std::string Fuzzer::WriteToOutputCorpus(const Unit &U) { @@ -610,13 +660,13 @@ void Fuzzer::PrintStatusForNewUnit(const Unit &U, const char *Text) { PrintStats(Text, ""); if (Options.Verbosity) { Printf(" L: %zd/%zd ", U.size(), Corpus.MaxInputSize()); - MD.PrintMutationSequence(); + MD.PrintMutationSequence(Options.Verbosity >= 2); Printf("\n"); } } void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) { - II->NumSuccessfullMutations++; + II->NumSuccessfulMutations++; MD.RecordSuccessfulMutationSequence(); PrintStatusForNewUnit(U, II->Reduced ? "REDUCE" : "NEW "); WriteToOutputCorpus(U); @@ -670,12 +720,15 @@ void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, } } -bool Fuzzer::MutateAndTestOne() { +void Fuzzer::MutateAndTestOne() { MD.StartMutationSequence(); auto &II = Corpus.ChooseUnitToMutate(MD.GetRand()); - if (Options.DoCrossOver) - MD.SetCrossOverWith(&Corpus.ChooseUnitToMutate(MD.GetRand()).U); + if (Options.DoCrossOver) { + auto &CrossOverII = Corpus.ChooseUnitToCrossOverWith( + MD.GetRand(), Options.CrossOverUniformDist); + MD.SetCrossOverWith(&CrossOverII.U); + } const auto &U = II.U; memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1)); assert(CurrentUnitData); @@ -692,7 +745,7 @@ bool Fuzzer::MutateAndTestOne() { for (int i = 0; i < Options.MutateDepth; i++) { if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) break; - if (MaybeExitGracefully()) return true; + MaybeExitGracefully(); size_t NewSize = 0; if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() && Size <= CurrentMaxMutationLen) @@ -702,10 +755,6 @@ bool Fuzzer::MutateAndTestOne() { // If MutateWithMask either failed or wasn't called, call default Mutate. if (!NewSize) NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen); - - if (!NewSize) - continue; - assert(NewSize > 0 && "Mutator returned empty unit"); assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit"); Size = NewSize; @@ -714,7 +763,7 @@ bool Fuzzer::MutateAndTestOne() { bool FoundUniqFeatures = false; bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II, - &FoundUniqFeatures); + /*ForceAddToCorpus*/ false, &FoundUniqFeatures); TryDetectingAMemoryLeak(CurrentUnitData, Size, /*DuringInitialCorpusExecution*/ false); if (NewCov) { @@ -726,7 +775,6 @@ bool Fuzzer::MutateAndTestOne() { } II.NeedsEnergyUpdate = true; - return false; } void Fuzzer::PurgeAllocator() { @@ -744,7 +792,7 @@ void Fuzzer::PurgeAllocator() { LastAllocatorPurgeAttemptTime = system_clock::now(); } -int Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) { +void Fuzzer::ReadAndExecuteSeedCorpora(std::vector<SizedFile> &CorporaFiles) { const size_t kMaxSaneLen = 1 << 20; const size_t kMinDefaultLen = 4096; size_t MaxSize = 0; @@ -756,7 +804,7 @@ int Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) { TotalSize += File.Size; } if (Options.MaxLen == 0) - SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxSize), kMaxSaneLen)); + SetMaxInputLen(std::clamp(MaxSize, kMinDefaultLen, kMaxSaneLen)); assert(MaxInputLen > 0); // Test the callback with empty input and never try it again. @@ -783,7 +831,9 @@ int Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) { for (auto &SF : CorporaFiles) { auto U = FileToVector(SF.File, MaxInputLen, /*ExitOnError=*/false); assert(U.size() <= MaxInputLen); - RunOne(U.data(), U.size()); + RunOne(U.data(), U.size(), /*MayDeleteFile*/ false, /*II*/ nullptr, + /*ForceAddToCorpus*/ Options.KeepSeed, + /*FoundUniqFeatures*/ nullptr); CheckExitOnSrcPosOrItem(); TryDetectingAMemoryLeak(U.data(), U.size(), /*DuringInitialCorpusExecution*/ true); @@ -801,25 +851,25 @@ int Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) { } if (Corpus.empty() && Options.MaxNumberOfRuns) { - Printf("ERROR: no interesting inputs were found. " - "Is the code instrumented for coverage? Exiting.\n"); - return 1; + Printf("WARNING: no interesting inputs were found so far. " + "Is the code instrumented for coverage?\n" + "This may also happen if the target rejected all inputs we tried so " + "far\n"); + // The remaining logic requires that the corpus is not empty, + // so we add one fake input to the in-memory corpus. + Corpus.AddToCorpus({'\n'}, /*NumFeatures=*/1, /*MayDeleteFile=*/true, + /*HasFocusFunction=*/false, /*NeverReduce=*/false, + /*TimeOfUnit=*/duration_cast<microseconds>(0s), {0}, DFT, + /*BaseII*/ nullptr); } - return 0; } -int Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) { +void Fuzzer::Loop(std::vector<SizedFile> &CorporaFiles) { auto FocusFunctionOrAuto = Options.FocusFunction; - int Res = DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles, + DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles, MD.GetRand()); - if (Res != 0) - return Res; - Res = TPC.SetFocusFunction(FocusFunctionOrAuto); - if (Res != 0) - return Res; - Res = ReadAndExecuteSeedCorpora(CorporaFiles); - if (Res != 0) - return Res; + TPC.SetFocusFunction(FocusFunctionOrAuto); + ReadAndExecuteSeedCorpora(CorporaFiles); DFT.Clear(); // No need for DFT any more. TPC.SetPrintNewPCs(Options.PrintNewCovPcs); TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs); @@ -857,15 +907,13 @@ int Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) { } // Perform several mutations and runs. - if (MutateAndTestOne()) - return 0; + MutateAndTestOne(); PurgeAllocator(); } PrintStats("DONE ", "\n"); MD.PrintRecommendedDictionary(); - return 0; } void Fuzzer::MinimizeCrashLoop(const Unit &U) { @@ -876,9 +924,7 @@ void Fuzzer::MinimizeCrashLoop(const Unit &U) { memcpy(CurrentUnitData, U.data(), U.size()); for (int i = 0; i < Options.MutateDepth; i++) { size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen); - assert(NewSize <= MaxMutationLen); - if (!NewSize) - continue; + assert(NewSize > 0 && NewSize <= MaxMutationLen); ExecuteCallback(CurrentUnitData, NewSize); PrintPulseAndReportSlowInput(CurrentUnitData, NewSize); TryDetectingAMemoryLeak(CurrentUnitData, NewSize, diff --git a/tools/fuzzing/libfuzzer/FuzzerMerge.cpp b/tools/fuzzing/libfuzzer/FuzzerMerge.cpp @@ -28,12 +28,11 @@ bool Merger::Parse(const std::string &Str, bool ParseCoverage) { return Parse(SS, ParseCoverage); } -int Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) { +void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) { if (!Parse(IS, ParseCoverage)) { Printf("MERGE: failed to parse the control file (unexpected error)\n"); - return 1; + exit(1); } - return 0; } // The control file example: @@ -78,14 +77,15 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) { size_t ExpectedStartMarker = 0; const size_t kInvalidStartMarker = -1; size_t LastSeenStartMarker = kInvalidStartMarker; - Vector<uint32_t> TmpFeatures; - Set<uint32_t> PCs; + bool HaveFtMarker = true; + std::vector<uint32_t> TmpFeatures; + std::set<uint32_t> PCs; while (std::getline(IS, Line, '\n')) { std::istringstream ISS1(Line); std::string Marker; - size_t N; - ISS1 >> Marker; - ISS1 >> N; + uint32_t N; + if (!(ISS1 >> Marker) || !(ISS1 >> N)) + return false; if (Marker == "STARTED") { // STARTED FILE_ID FILE_SIZE if (ExpectedStartMarker != N) @@ -94,12 +94,13 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) { LastSeenStartMarker = ExpectedStartMarker; assert(ExpectedStartMarker < Files.size()); ExpectedStartMarker++; + HaveFtMarker = false; } else if (Marker == "FT") { // FT FILE_ID COV1 COV2 COV3 ... size_t CurrentFileIdx = N; if (CurrentFileIdx != LastSeenStartMarker) return false; - LastSeenStartMarker = kInvalidStartMarker; + HaveFtMarker = true; if (ParseCoverage) { TmpFeatures.clear(); // use a vector from outer scope to avoid resizes. while (ISS1 >> N) @@ -109,6 +110,8 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) { } } else if (Marker == "COV") { size_t CurrentFileIdx = N; + if (CurrentFileIdx != LastSeenStartMarker) + return false; if (ParseCoverage) while (ISS1 >> N) if (PCs.insert(N).second) @@ -117,7 +120,7 @@ bool Merger::Parse(std::istream &IS, bool ParseCoverage) { return false; } } - if (LastSeenStartMarker != kInvalidStartMarker) + if (!HaveFtMarker && LastSeenStartMarker != kInvalidStartMarker) LastFailure = Files[LastSeenStartMarker].Name; FirstNotProcessedFile = ExpectedStartMarker; @@ -133,13 +136,16 @@ size_t Merger::ApproximateMemoryConsumption() const { // Decides which files need to be merged (add those to NewFiles). // Returns the number of new features added. -size_t Merger::Merge(const Set<uint32_t> &InitialFeatures, - Set<uint32_t> *NewFeatures, - const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov, - Vector<std::string> *NewFiles) { +size_t Merger::Merge(const std::set<uint32_t> &InitialFeatures, + std::set<uint32_t> *NewFeatures, + const std::set<uint32_t> &InitialCov, + std::set<uint32_t> *NewCov, + std::vector<std::string> *NewFiles) { NewFiles->clear(); + NewFeatures->clear(); + NewCov->clear(); assert(NumFilesInFirstCorpus <= Files.size()); - Set<uint32_t> AllFeatures = InitialFeatures; + std::set<uint32_t> AllFeatures = InitialFeatures; // What features are in the initial corpus? for (size_t i = 0; i < NumFilesInFirstCorpus; i++) { @@ -149,7 +155,7 @@ size_t Merger::Merge(const Set<uint32_t> &InitialFeatures, // Remove all features that we already know from all other inputs. for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) { auto &Cur = Files[i].Features; - Vector<uint32_t> Tmp; + std::vector<uint32_t> Tmp; std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(), AllFeatures.end(), std::inserter(Tmp, Tmp.begin())); Cur.swap(Tmp); @@ -187,21 +193,20 @@ size_t Merger::Merge(const Set<uint32_t> &InitialFeatures, return NewFeatures->size(); } -Set<uint32_t> Merger::AllFeatures() const { - Set<uint32_t> S; +std::set<uint32_t> Merger::AllFeatures() const { + std::set<uint32_t> S; for (auto &File : Files) S.insert(File.Features.begin(), File.Features.end()); return S; } // Inner process. May crash if the target crashes. -int Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { +void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath, + bool IsSetCoverMerge) { Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str()); Merger M; std::ifstream IF(CFPath); - int Res = M.ParseOrExit(IF, false); - if (Res != 0) - return Res; + M.ParseOrExit(IF, false); IF.close(); if (!M.LastFailure.empty()) Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n", @@ -213,14 +218,13 @@ int Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { M.Files.size() - M.FirstNotProcessedFile); std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app); - Set<size_t> AllFeatures; + std::set<size_t> AllFeatures; auto PrintStatsWrapper = [this, &AllFeatures](const char* Where) { this->PrintStats(Where, "\n", 0, AllFeatures.size()); }; - Set<const TracePC::PCTableEntry *> AllPCs; + std::set<const TracePC::PCTableEntry *> AllPCs; for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) { - if (Fuzzer::MaybeExitGracefully()) - return 0; + Fuzzer::MaybeExitGracefully(); auto U = FileToVector(M.Files[i].Name); if (U.size() > MaxInputLen) { U.resize(MaxInputLen); @@ -232,19 +236,18 @@ int Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { OF.flush(); // Flush is important since Command::Execute may crash. // Run. TPC.ResetMaps(); - if (ExecuteCallback(U.data(), U.size())) { - continue; - } + ExecuteCallback(U.data(), U.size()); // Collect coverage. We are iterating over the files in this order: // * First, files in the initial corpus ordered by size, smallest first. // * Then, all other files, smallest first. - // So it makes no sense to record all features for all files, instead we - // only record features that were not seen before. - Set<size_t> UniqFeatures; - TPC.CollectFeatures([&](size_t Feature) { - if (AllFeatures.insert(Feature).second) - UniqFeatures.insert(Feature); - }); + std::set<size_t> Features; + if (IsSetCoverMerge) + TPC.CollectFeatures([&](size_t Feature) { Features.insert(Feature); }); + else + TPC.CollectFeatures([&](size_t Feature) { + if (AllFeatures.insert(Feature).second) + Features.insert(Feature); + }); TPC.UpdateObservedPCs(); // Show stats. if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1))) @@ -253,7 +256,7 @@ int Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { PrintStatsWrapper("LOADED"); // Write the post-run marker and the coverage. OF << "FT " << i; - for (size_t F : UniqFeatures) + for (size_t F : Features) OF << " " << F; OF << "\n"; OF << "COV " << i; @@ -265,19 +268,139 @@ int Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { OF.flush(); } PrintStatsWrapper("DONE "); - return 0; } -static int WriteNewControlFile(const std::string &CFPath, - const Vector<SizedFile> &OldCorpus, - const Vector<SizedFile> &NewCorpus, - const Vector<MergeFileInfo> &KnownFiles, - size_t &NumFiles) { +// Merges all corpora into the first corpus. A file is added into +// the first corpus only if it adds new features. Unlike `Merger::Merge`, +// this implementation calculates an approximation of the minimum set +// of corpora files, that cover all known features (set cover problem). +// Generally, this means that files with more features are preferred for +// merge into the first corpus. When two files have the same number of +// features, the smaller one is preferred. +size_t Merger::SetCoverMerge(const std::set<uint32_t> &InitialFeatures, + std::set<uint32_t> *NewFeatures, + const std::set<uint32_t> &InitialCov, + std::set<uint32_t> *NewCov, + std::vector<std::string> *NewFiles) { + assert(NumFilesInFirstCorpus <= Files.size()); + NewFiles->clear(); + NewFeatures->clear(); + NewCov->clear(); + std::set<uint32_t> AllFeatures; + // 1 << 21 - 1 is the maximum feature index. + // See 'kFeatureSetSize' in 'FuzzerCorpus.h'. + const uint32_t kFeatureSetSize = 1 << 21; + std::vector<bool> Covered(kFeatureSetSize, false); + size_t NumCovered = 0; + + std::set<uint32_t> ExistingFeatures = InitialFeatures; + for (size_t i = 0; i < NumFilesInFirstCorpus; ++i) + ExistingFeatures.insert(Files[i].Features.begin(), Files[i].Features.end()); + + // Mark the existing features as covered. + for (const auto &F : ExistingFeatures) { + if (!Covered[F % kFeatureSetSize]) { + ++NumCovered; + Covered[F % kFeatureSetSize] = true; + } + // Calculate an underestimation of the set of covered features + // since the `Covered` bitvector is smaller than the feature range. + AllFeatures.insert(F % kFeatureSetSize); + } + + std::set<size_t> RemainingFiles; + for (size_t i = NumFilesInFirstCorpus; i < Files.size(); ++i) { + // Construct an incremental sequence which represent the + // indices to all files (excluding those in the initial corpus). + // RemainingFiles = range(NumFilesInFirstCorpus..Files.size()). + RemainingFiles.insert(i); + // Insert this file's unique features to all features. + for (const auto &F : Files[i].Features) + AllFeatures.insert(F % kFeatureSetSize); + } + + // Integrate files into Covered until set is complete. + while (NumCovered != AllFeatures.size()) { + // Index to file with largest number of unique features. + size_t MaxFeaturesIndex = NumFilesInFirstCorpus; + // Indices to remove from RemainingFiles. + std::set<size_t> RemoveIndices; + // Running max unique feature count. + // Updated upon finding a file with more features. + size_t MaxNumFeatures = 0; + + // Iterate over all files not yet integrated into Covered, + // to find the file which has the largest number of + // features that are not already in Covered. + for (const auto &i : RemainingFiles) { + const auto &File = Files[i]; + size_t CurrentUnique = 0; + // Count number of features in this file + // which are not yet in Covered. + for (const auto &F : File.Features) + if (!Covered[F % kFeatureSetSize]) + ++CurrentUnique; + + if (CurrentUnique == 0) { + // All features in this file are already in Covered: skip next time. + RemoveIndices.insert(i); + } else if (CurrentUnique > MaxNumFeatures || + (CurrentUnique == MaxNumFeatures && + File.Size < Files[MaxFeaturesIndex].Size)) { + // Update the max features file based on unique features + // Break ties by selecting smaller files. + MaxNumFeatures = CurrentUnique; + MaxFeaturesIndex = i; + } + } + // Must be a valid index/ + assert(MaxFeaturesIndex < Files.size()); + // Remove any feature-less files found. + for (const auto &i : RemoveIndices) + RemainingFiles.erase(i); + if (MaxNumFeatures == 0) { + // Did not find a file that adds unique features. + // This means that we should have no remaining files. + assert(RemainingFiles.size() == 0); + assert(NumCovered == AllFeatures.size()); + break; + } + + // MaxFeaturesIndex must be an element of Remaining. + assert(RemainingFiles.find(MaxFeaturesIndex) != RemainingFiles.end()); + // Remove the file with the most features from Remaining. + RemainingFiles.erase(MaxFeaturesIndex); + const auto &MaxFeatureFile = Files[MaxFeaturesIndex]; + // Add the features of the max feature file to Covered. + for (const auto &F : MaxFeatureFile.Features) { + if (!Covered[F % kFeatureSetSize]) { + ++NumCovered; + Covered[F % kFeatureSetSize] = true; + NewFeatures->insert(F); + } + } + // Add the index to this file to the result. + NewFiles->push_back(MaxFeatureFile.Name); + // Update NewCov with the additional coverage + // that MaxFeatureFile provides. + for (const auto &C : MaxFeatureFile.Cov) + if (InitialCov.find(C) == InitialCov.end()) + NewCov->insert(C); + } + + return NewFeatures->size(); +} + +static size_t +WriteNewControlFile(const std::string &CFPath, + const std::vector<SizedFile> &OldCorpus, + const std::vector<SizedFile> &NewCorpus, + const std::vector<MergeFileInfo> &KnownFiles) { std::unordered_set<std::string> FilesToSkip; for (auto &SF: KnownFiles) FilesToSkip.insert(SF.Name); - Vector<std::string> FilesToUse; + std::vector<std::string> FilesToUse; auto MaybeUseFile = [=, &FilesToUse](std::string Name) { if (FilesToSkip.find(Name) == FilesToSkip.end()) FilesToUse.push_back(Name); @@ -298,28 +421,26 @@ static int WriteNewControlFile(const std::string &CFPath, if (!ControlFile) { Printf("MERGE-OUTER: failed to write to the control file: %s\n", CFPath.c_str()); - return 1; + exit(1); } - NumFiles = FilesToUse.size(); - return 0; + return FilesToUse.size(); } // Outer process. Does not call the target code and thus should not fail. -int CrashResistantMerge(const Vector<std::string> &Args, - const Vector<SizedFile> &OldCorpus, - const Vector<SizedFile> &NewCorpus, - Vector<std::string> *NewFiles, - const Set<uint32_t> &InitialFeatures, - Set<uint32_t> *NewFeatures, - const Set<uint32_t> &InitialCov, - Set<uint32_t> *NewCov, - const std::string &CFPath, - bool V /*Verbose*/) { - if (NewCorpus.empty() && OldCorpus.empty()) return 0; // Nothing to merge. +void CrashResistantMerge(const std::vector<std::string> &Args, + const std::vector<SizedFile> &OldCorpus, + const std::vector<SizedFile> &NewCorpus, + std::vector<std::string> *NewFiles, + const std::set<uint32_t> &InitialFeatures, + std::set<uint32_t> *NewFeatures, + const std::set<uint32_t> &InitialCov, + std::set<uint32_t> *NewCov, const std::string &CFPath, + bool V, /*Verbose*/ + bool IsSetCoverMerge) { + if (NewCorpus.empty() && OldCorpus.empty()) return; // Nothing to merge. size_t NumAttempts = 0; - int Res; - Vector<MergeFileInfo> KnownFiles; + std::vector<MergeFileInfo> KnownFiles; if (FileSize(CFPath)) { VPrintf(V, "MERGE-OUTER: non-empty control file provided: '%s'\n", CFPath.c_str()); @@ -339,8 +460,7 @@ int CrashResistantMerge(const Vector<std::string> &Args, VPrintf( V, "MERGE-OUTER: nothing to do, merge has been completed before\n"); - Fuzzer::GracefullyExit(); - return 0; + exit(0); } // Number of input files likely changed, start merge from scratch, but @@ -365,31 +485,31 @@ int CrashResistantMerge(const Vector<std::string> &Args, "%zd files, %zd in the initial corpus, %zd processed earlier\n", OldCorpus.size() + NewCorpus.size(), OldCorpus.size(), KnownFiles.size()); - Res = WriteNewControlFile(CFPath, OldCorpus, NewCorpus, KnownFiles, NumAttempts); - if (Res != 0) - return Res; + NumAttempts = WriteNewControlFile(CFPath, OldCorpus, NewCorpus, KnownFiles); } // Execute the inner process until it passes. // Every inner process should execute at least one input. Command BaseCmd(Args); BaseCmd.removeFlag("merge"); + BaseCmd.removeFlag("set_cover_merge"); BaseCmd.removeFlag("fork"); BaseCmd.removeFlag("collect_data_flow"); for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) { - if (Fuzzer::MaybeExitGracefully()) - return 0; + Fuzzer::MaybeExitGracefully(); VPrintf(V, "MERGE-OUTER: attempt %zd\n", Attempt); Command Cmd(BaseCmd); Cmd.addFlag("merge_control_file", CFPath); - Cmd.addFlag("merge_inner", "1"); + // If we are going to use the set cover implementation for + // minimization add the merge_inner=2 internal flag. + Cmd.addFlag("merge_inner", IsSetCoverMerge ? "2" : "1"); if (!V) { Cmd.setOutputFile(getDevNull()); Cmd.combineOutAndErr(); } auto ExitCode = ExecuteCommand(Cmd); if (!ExitCode) { - VPrintf(V, "MERGE-OUTER: succesfull in %zd attempt(s)\n", Attempt); + VPrintf(V, "MERGE-OUTER: successful in %zd attempt(s)\n", Attempt); break; } } @@ -400,20 +520,20 @@ int CrashResistantMerge(const Vector<std::string> &Args, VPrintf(V, "MERGE-OUTER: the control file has %zd bytes\n", (size_t)IF.tellg()); IF.seekg(0, IF.beg); - Res = M.ParseOrExit(IF, true); - if (Res != 0) - return Res; + M.ParseOrExit(IF, true); IF.close(); 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); + if (IsSetCoverMerge) + M.SetCoverMerge(InitialFeatures, NewFeatures, InitialCov, NewCov, NewFiles); + else + M.Merge(InitialFeatures, NewFeatures, InitialCov, NewCov, NewFiles); VPrintf(V, "MERGE-OUTER: %zd new files with %zd new features added; " "%zd new coverage edges\n", NewFiles->size(), NewFeatures->size(), NewCov->size()); - return 0; } } // namespace fuzzer diff --git a/tools/fuzzing/libfuzzer/FuzzerMerge.h b/tools/fuzzing/libfuzzer/FuzzerMerge.h @@ -41,6 +41,7 @@ #define LLVM_FUZZER_MERGE_H #include "FuzzerDefs.h" +#include "FuzzerIO.h" #include <istream> #include <ostream> @@ -52,35 +53,40 @@ namespace fuzzer { struct MergeFileInfo { std::string Name; size_t Size = 0; - Vector<uint32_t> Features, Cov; + std::vector<uint32_t> Features, Cov; }; struct Merger { - Vector<MergeFileInfo> Files; + std::vector<MergeFileInfo> Files; size_t NumFilesInFirstCorpus = 0; size_t FirstNotProcessedFile = 0; std::string LastFailure; bool Parse(std::istream &IS, bool ParseCoverage); bool Parse(const std::string &Str, bool ParseCoverage); - int ParseOrExit(std::istream &IS, bool ParseCoverage); - size_t Merge(const Set<uint32_t> &InitialFeatures, Set<uint32_t> *NewFeatures, - const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov, - Vector<std::string> *NewFiles); + void ParseOrExit(std::istream &IS, bool ParseCoverage); + size_t Merge(const std::set<uint32_t> &InitialFeatures, + std::set<uint32_t> *NewFeatures, + const std::set<uint32_t> &InitialCov, std::set<uint32_t> *NewCov, + std::vector<std::string> *NewFiles); + size_t SetCoverMerge(const std::set<uint32_t> &InitialFeatures, + std::set<uint32_t> *NewFeatures, + const std::set<uint32_t> &InitialCov, + std::set<uint32_t> *NewCov, + std::vector<std::string> *NewFiles); size_t ApproximateMemoryConsumption() const; - Set<uint32_t> AllFeatures() const; + std::set<uint32_t> AllFeatures() const; }; -int CrashResistantMerge(const Vector<std::string> &Args, - const Vector<SizedFile> &OldCorpus, - const Vector<SizedFile> &NewCorpus, - Vector<std::string> *NewFiles, - const Set<uint32_t> &InitialFeatures, - Set<uint32_t> *NewFeatures, - const Set<uint32_t> &InitialCov, - Set<uint32_t> *NewCov, - const std::string &CFPath, - bool Verbose); +void CrashResistantMerge(const std::vector<std::string> &Args, + const std::vector<SizedFile> &OldCorpus, + const std::vector<SizedFile> &NewCorpus, + std::vector<std::string> *NewFiles, + const std::set<uint32_t> &InitialFeatures, + std::set<uint32_t> *NewFeatures, + const std::set<uint32_t> &InitialCov, + std::set<uint32_t> *NewCov, const std::string &CFPath, + bool Verbose, bool IsSetCoverMerge); } // namespace fuzzer diff --git a/tools/fuzzing/libfuzzer/FuzzerMutate.cpp b/tools/fuzzing/libfuzzer/FuzzerMutate.cpp @@ -18,6 +18,7 @@ namespace fuzzer { const size_t Dictionary::kMaxDictSize; +static const size_t kMaxMutationsToPrint = 10; static void PrintASCII(const Word &W, const char *PrintAfter) { PrintASCII(W.data(), W.size(), PrintAfter); @@ -60,14 +61,20 @@ MutationDispatcher::MutationDispatcher(Random &Rand, } static char RandCh(Random &Rand) { - if (Rand.RandBool()) return Rand(256); + if (Rand.RandBool()) + return static_cast<char>(Rand(256)); const char Special[] = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00"; return Special[Rand(sizeof(Special) - 1)]; } size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize) { - return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand()); + if (EF->__msan_unpoison) + EF->__msan_unpoison(Data, Size); + if (EF->__msan_unpoison_param) + EF->__msan_unpoison_param(4); + return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, + Rand.Rand<unsigned int>()); } size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size, @@ -80,11 +87,21 @@ size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size, return 0; CustomCrossOverInPlaceHere.resize(MaxSize); auto &U = CustomCrossOverInPlaceHere; + + if (EF->__msan_unpoison) { + EF->__msan_unpoison(Data, Size); + EF->__msan_unpoison(Other.data(), Other.size()); + EF->__msan_unpoison(U.data(), U.size()); + } + if (EF->__msan_unpoison_param) + EF->__msan_unpoison_param(7); size_t NewSize = EF->LLVMFuzzerCustomCrossOver( - Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand()); + Data, Size, Other.data(), Other.size(), U.data(), U.size(), + Rand.Rand<unsigned int>()); + if (!NewSize) return 0; - assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit"); + assert(NewSize <= MaxSize && "CustomCrossOver returned oversized unit"); memcpy(Data, U.data(), NewSize); return NewSize; } @@ -134,7 +151,8 @@ size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data, // Insert new values at Data[Idx]. memmove(Data + Idx + N, Data + Idx, Size - Idx); // Give preference to 0x00 and 0xff. - uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255); + uint8_t Byte = static_cast<uint8_t>( + Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255)); for (size_t i = 0; i < N; i++) Data[Idx + i] = Byte; return Size + N; @@ -177,7 +195,8 @@ size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size, Size += W.size(); } else { // Overwrite some bytes with W. if (W.size() > Size) return 0; - size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size()); + size_t Idx = + UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1 - W.size()); memcpy(Data + Idx, W.data(), W.size()); } return Size; @@ -226,8 +245,8 @@ DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( T Arg1, T Arg2, const uint8_t *Data, size_t Size) { if (Rand.RandBool()) Arg1 = Bswap(Arg1); if (Rand.RandBool()) Arg2 = Bswap(Arg2); - T Arg1Mutation = Arg1 + Rand(-1, 1); - T Arg2Mutation = Arg2 + Rand(-1, 1); + T Arg1Mutation = static_cast<T>(Arg1 + Rand(-1, 1)); + T Arg2Mutation = static_cast<T>(Arg2 + Rand(-1, 1)); return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation, sizeof(Arg1), Data, Size); } @@ -244,23 +263,23 @@ size_t MutationDispatcher::Mutate_AddWordFromTORC( DictionaryEntry DE; switch (Rand(4)) { case 0: { - auto X = TPC.TORC8.Get(Rand.Rand()); + auto X = TPC.TORC8.Get(Rand.Rand<size_t>()); DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); } break; case 1: { - auto X = TPC.TORC4.Get(Rand.Rand()); + auto X = TPC.TORC4.Get(Rand.Rand<size_t>()); if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool()) DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data, Size); else DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); } break; case 2: { - auto X = TPC.TORCW.Get(Rand.Rand()); + auto X = TPC.TORCW.Get(Rand.Rand<size_t>()); DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); } break; case 3: if (Options.UseMemmem) { - auto X = TPC.MMT.Get(Rand.Rand()); - DE = DictionaryEntry(X); + auto X = TPC.MMT.Get(Rand.Rand<size_t>()); + DE = DictionaryEntry(X); } break; default: assert(0); @@ -386,17 +405,17 @@ size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) { assert(Off + sizeof(T) <= Size); T Val; if (Off < 64 && !Rand(4)) { - Val = Size; + Val = static_cast<T>(Size); if (Rand.RandBool()) Val = Bswap(Val); } else { memcpy(&Val, Data + Off, sizeof(Val)); - T Add = Rand(21); + T Add = static_cast<T>(Rand(21)); Add -= 10; if (Rand.RandBool()) - Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes. + Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endianness. else - Val = Val + Add; // Add assuming current endiannes. + Val = Val + Add; // Add assuming current endianness. if (Add == 0 || Rand.RandBool()) // Maybe negate. Val = -Val; } @@ -425,26 +444,26 @@ size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size, if (!CrossOverWith) return 0; const Unit &O = *CrossOverWith; if (O.empty()) return 0; - MutateInPlaceHere.resize(MaxSize); - auto &U = MutateInPlaceHere; size_t NewSize = 0; switch(Rand(3)) { case 0: - NewSize = CrossOver(Data, Size, O.data(), O.size(), U.data(), U.size()); + MutateInPlaceHere.resize(MaxSize); + NewSize = CrossOver(Data, Size, O.data(), O.size(), + MutateInPlaceHere.data(), MaxSize); + memcpy(Data, MutateInPlaceHere.data(), NewSize); break; case 1: - NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize); + NewSize = InsertPartOf(O.data(), O.size(), Data, Size, MaxSize); if (!NewSize) - NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size()); + NewSize = CopyPartOf(O.data(), O.size(), Data, Size); break; case 2: - NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size()); + NewSize = CopyPartOf(O.data(), O.size(), Data, Size); break; default: assert(0); } assert(NewSize > 0 && "CrossOver returned empty unit"); - assert(NewSize <= MaxSize && "CrossOver returned overisized unit"); - memcpy(Data, U.data(), NewSize); + assert(NewSize <= MaxSize && "CrossOver returned oversized unit"); return NewSize; } @@ -461,12 +480,12 @@ void MutationDispatcher::RecordSuccessfulMutationSequence() { assert(DE->GetW().size()); // Linear search is fine here as this happens seldom. if (!PersistentAutoDictionary.ContainsWord(DE->GetW())) - PersistentAutoDictionary.push_back({DE->GetW(), 1}); + PersistentAutoDictionary.push_back(*DE); } } void MutationDispatcher::PrintRecommendedDictionary() { - Vector<DictionaryEntry> V; + std::vector<DictionaryEntry> V; for (auto &DE : PersistentAutoDictionary) if (!ManualDictionary.ContainsWord(DE.GetW())) V.push_back(DE); @@ -481,19 +500,34 @@ void MutationDispatcher::PrintRecommendedDictionary() { Printf("###### End of recommended dictionary. ######\n"); } -void MutationDispatcher::PrintMutationSequence() { +void MutationDispatcher::PrintMutationSequence(bool Verbose) { Printf("MS: %zd ", CurrentMutatorSequence.size()); - for (auto M : CurrentMutatorSequence) - Printf("%s-", M.Name); + size_t EntriesToPrint = + Verbose ? CurrentMutatorSequence.size() + : std::min(kMaxMutationsToPrint, CurrentMutatorSequence.size()); + for (size_t i = 0; i < EntriesToPrint; i++) + Printf("%s-", CurrentMutatorSequence[i].Name); if (!CurrentDictionaryEntrySequence.empty()) { Printf(" DE: "); - for (auto DE : CurrentDictionaryEntrySequence) { + EntriesToPrint = Verbose ? CurrentDictionaryEntrySequence.size() + : std::min(kMaxMutationsToPrint, + CurrentDictionaryEntrySequence.size()); + for (size_t i = 0; i < EntriesToPrint; i++) { Printf("\""); - PrintASCII(DE->GetW(), "\"-"); + PrintASCII(CurrentDictionaryEntrySequence[i]->GetW(), "\"-"); } } } +std::string MutationDispatcher::MutationSequence() { + std::string MS; + for (const auto &M : CurrentMutatorSequence) { + MS += M.Name; + MS += "-"; + } + return MS; +} + size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) { return MutateImpl(Data, Size, MaxSize, Mutators); } @@ -506,7 +540,7 @@ size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size, // Mutates Data in place, returns new size. size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize, - Vector<Mutator> &Mutators) { + std::vector<Mutator> &Mutators) { assert(MaxSize > 0); // Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize), // in which case they will return 0. @@ -528,7 +562,7 @@ size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size, // Mask represents the set of Data bytes that are worth mutating. size_t MutationDispatcher::MutateWithMask(uint8_t *Data, size_t Size, size_t MaxSize, - const Vector<uint8_t> &Mask) { + const std::vector<uint8_t> &Mask) { size_t MaskedSize = std::min(Size, Mask.size()); // * Copy the worthy bytes into a temporary array T // * Mutate T diff --git a/tools/fuzzing/libfuzzer/FuzzerMutate.h b/tools/fuzzing/libfuzzer/FuzzerMutate.h @@ -24,8 +24,11 @@ public: ~MutationDispatcher() {} /// Indicate that we are about to start a new sequence of mutations. void StartMutationSequence(); - /// Print the current sequence of mutations. - void PrintMutationSequence(); + /// Print the current sequence of mutations. Only prints the full sequence + /// when Verbose is true. + void PrintMutationSequence(bool Verbose = true); + /// Return the current sequence of mutations. + std::string MutationSequence(); /// Indicate that the current sequence of mutations was successful. void RecordSuccessfulMutationSequence(); /// Mutates data by invoking user-provided mutator. @@ -40,9 +43,9 @@ public: size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize); /// Mutates data by inserting several repeated bytes. size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize); - /// Mutates data by chanding one byte. + /// Mutates data by changing one byte. size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize); - /// Mutates data by chanding one bit. + /// Mutates data by changing one bit. size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize); /// Mutates data by copying/inserting a part of data into a different place. size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize); @@ -74,7 +77,7 @@ public: /// that have '1' in Mask. /// Mask.size() should be >= Size. size_t MutateWithMask(uint8_t *Data, size_t Size, size_t MaxSize, - const Vector<uint8_t> &Mask); + const std::vector<uint8_t> &Mask); /// Applies one of the default mutations. Provided as a service /// to mutation authors. @@ -101,7 +104,7 @@ public: size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size, size_t MaxSize); size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize, - Vector<Mutator> &Mutators); + std::vector<Mutator> &Mutators); size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To, size_t ToSize, size_t MaxToSize); @@ -126,29 +129,26 @@ public: // Dictionary provided by the user via -dict=DICT_FILE. Dictionary ManualDictionary; - // Temporary dictionary modified by the fuzzer itself, - // recreated periodically. - Dictionary TempAutoDictionary; // Persistent dictionary modified by the fuzzer, consists of // entries that led to successful discoveries in the past mutations. Dictionary PersistentAutoDictionary; - Vector<DictionaryEntry *> CurrentDictionaryEntrySequence; + std::vector<DictionaryEntry *> CurrentDictionaryEntrySequence; static const size_t kCmpDictionaryEntriesDequeSize = 16; DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize]; size_t CmpDictionaryEntriesDequeIdx = 0; const Unit *CrossOverWith = nullptr; - Vector<uint8_t> MutateInPlaceHere; - Vector<uint8_t> MutateWithMaskTemp; + std::vector<uint8_t> MutateInPlaceHere; + std::vector<uint8_t> MutateWithMaskTemp; // CustomCrossOver needs its own buffer as a custom implementation may call // LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere. - Vector<uint8_t> CustomCrossOverInPlaceHere; + std::vector<uint8_t> CustomCrossOverInPlaceHere; - Vector<Mutator> Mutators; - Vector<Mutator> DefaultMutators; - Vector<Mutator> CurrentMutatorSequence; + std::vector<Mutator> Mutators; + std::vector<Mutator> DefaultMutators; + std::vector<Mutator> CurrentMutatorSequence; }; } // namespace fuzzer diff --git a/tools/fuzzing/libfuzzer/FuzzerOptions.h b/tools/fuzzing/libfuzzer/FuzzerOptions.h @@ -18,6 +18,7 @@ struct FuzzingOptions { int Verbosity = 1; size_t MaxLen = 0; size_t LenControl = 1000; + bool KeepSeed = false; int UnitTimeoutSec = 300; int TimeoutExitCode = 70; int OOMExitCode = 71; @@ -30,6 +31,7 @@ struct FuzzingOptions { int RssLimitMb = 0; int MallocLimitMb = 0; bool DoCrossOver = true; + bool CrossOverUniformDist = false; int MutateDepth = 5; bool ReduceDepth = false; bool UseCounters = false; @@ -44,9 +46,11 @@ struct FuzzingOptions { size_t MaxNumberOfRuns = -1L; int ReportSlowUnits = 10; bool OnlyASCII = false; - bool Entropic = false; + bool Entropic = true; + bool ForkCorpusGroups = false; size_t EntropicFeatureFrequencyThreshold = 0xFF; size_t EntropicNumberOfRarestFeatures = 100; + bool EntropicScalePerExecTime = false; std::string OutputCorpus; std::string ArtifactPrefix = "./"; std::string ExactArtifactPath; @@ -56,6 +60,7 @@ struct FuzzingOptions { std::string DataFlowTrace; std::string CollectDataFlow; std::string FeaturesDir; + std::string MutationGraphFile; std::string StopFile; bool SaveArtifacts = true; bool PrintNEW = true; // Print a status line when new units are found; @@ -64,20 +69,24 @@ struct FuzzingOptions { bool PrintFinalStats = false; bool PrintCorpusStats = false; bool PrintCoverage = false; + bool PrintFullCoverage = false; bool DumpCoverage = false; bool DetectLeaks = true; int PurgeAllocatorIntervalSec = 1; int TraceMalloc = 0; bool HandleAbrt = false; + bool HandleAlrm = false; bool HandleBus = false; bool HandleFpe = false; bool HandleIll = false; bool HandleInt = false; bool HandleSegv = false; bool HandleTerm = false; + bool HandleTrap = false; bool HandleXfsz = false; bool HandleUsr1 = false; bool HandleUsr2 = false; + bool HandleWinExcept = false; }; } // namespace fuzzer diff --git a/tools/fuzzing/libfuzzer/FuzzerPlatform.h b/tools/fuzzing/libfuzzer/FuzzerPlatform.h @@ -18,7 +18,6 @@ #define LIBFUZZER_LINUX 1 #define LIBFUZZER_NETBSD 0 #define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 0 #define LIBFUZZER_WINDOWS 0 #define LIBFUZZER_EMSCRIPTEN 0 #elif __APPLE__ @@ -27,7 +26,6 @@ #define LIBFUZZER_LINUX 0 #define LIBFUZZER_NETBSD 0 #define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 0 #define LIBFUZZER_WINDOWS 0 #define LIBFUZZER_EMSCRIPTEN 0 #elif __NetBSD__ @@ -36,7 +34,6 @@ #define LIBFUZZER_LINUX 0 #define LIBFUZZER_NETBSD 1 #define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 0 #define LIBFUZZER_WINDOWS 0 #define LIBFUZZER_EMSCRIPTEN 0 #elif __FreeBSD__ @@ -45,16 +42,6 @@ #define LIBFUZZER_LINUX 0 #define LIBFUZZER_NETBSD 0 #define LIBFUZZER_FREEBSD 1 -#define LIBFUZZER_OPENBSD 0 -#define LIBFUZZER_WINDOWS 0 -#define LIBFUZZER_EMSCRIPTEN 0 -#elif __OpenBSD__ -#define LIBFUZZER_APPLE 0 -#define LIBFUZZER_FUCHSIA 0 -#define LIBFUZZER_LINUX 0 -#define LIBFUZZER_NETBSD 0 -#define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 1 #define LIBFUZZER_WINDOWS 0 #define LIBFUZZER_EMSCRIPTEN 0 #elif _WIN32 @@ -63,7 +50,6 @@ #define LIBFUZZER_LINUX 0 #define LIBFUZZER_NETBSD 0 #define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 0 #define LIBFUZZER_WINDOWS 1 #define LIBFUZZER_EMSCRIPTEN 0 #elif __Fuchsia__ @@ -72,7 +58,6 @@ #define LIBFUZZER_LINUX 0 #define LIBFUZZER_NETBSD 0 #define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 0 #define LIBFUZZER_WINDOWS 0 #define LIBFUZZER_EMSCRIPTEN 0 #elif __EMSCRIPTEN__ @@ -81,7 +66,6 @@ #define LIBFUZZER_LINUX 0 #define LIBFUZZER_NETBSD 0 #define LIBFUZZER_FREEBSD 0 -#define LIBFUZZER_OPENBSD 0 #define LIBFUZZER_WINDOWS 0 #define LIBFUZZER_EMSCRIPTEN 1 #else @@ -101,7 +85,7 @@ #define LIBFUZZER_POSIX \ (LIBFUZZER_APPLE || LIBFUZZER_LINUX || LIBFUZZER_NETBSD || \ - LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN) + LIBFUZZER_FREEBSD || LIBFUZZER_EMSCRIPTEN) #ifdef __x86_64 #if __has_attribute(target) diff --git a/tools/fuzzing/libfuzzer/FuzzerRandom.h b/tools/fuzzing/libfuzzer/FuzzerRandom.h @@ -11,6 +11,7 @@ #ifndef LLVM_FUZZER_RANDOM_H #define LLVM_FUZZER_RANDOM_H +#include <cmath> #include <random> namespace fuzzer { @@ -18,18 +19,27 @@ class Random : public std::minstd_rand { public: Random(unsigned int seed) : std::minstd_rand(seed) {} result_type operator()() { return this->std::minstd_rand::operator()(); } - size_t Rand() { return this->operator()(); } - size_t RandBool() { return Rand() % 2; } + template <typename T> + typename std::enable_if<std::is_integral<T>::value, T>::type Rand() { + return static_cast<T>(this->operator()()); + } + size_t RandBool() { return this->operator()() % 2; } size_t SkewTowardsLast(size_t n) { size_t T = this->operator()(n * n); - size_t Res = sqrt(T); + size_t Res = static_cast<size_t>(sqrt(T)); return Res; } - size_t operator()(size_t n) { return n ? Rand() % n : 0; } - intptr_t operator()(intptr_t From, intptr_t To) { + template <typename T> + typename std::enable_if<std::is_integral<T>::value, T>::type operator()(T n) { + return n ? Rand<T>() % n : 0; + } + template <typename T> + typename std::enable_if<std::is_integral<T>::value, T>::type + operator()(T From, T To) { assert(From < To); - intptr_t RangeSize = To - From + 1; - return operator()(RangeSize) + From; + auto RangeSize = static_cast<unsigned long long>(To) - + static_cast<unsigned long long>(From) + 1; + return static_cast<T>(this->operator()(RangeSize) + From); } }; diff --git a/tools/fuzzing/libfuzzer/FuzzerSHA1.cpp b/tools/fuzzing/libfuzzer/FuzzerSHA1.cpp @@ -134,12 +134,13 @@ void sha1_hashBlock(sha1nfo *s) { s->state[4] += e; } -void sha1_addUncounted(sha1nfo *s, uint8_t data) { - uint8_t * const b = (uint8_t*) s->buffer; +// Adds the least significant byte of |data|. +void sha1_addUncounted(sha1nfo *s, uint32_t data) { + uint8_t *const b = (uint8_t *)s->buffer; #ifdef SHA_BIG_ENDIAN - b[s->bufferOffset] = data; + b[s->bufferOffset] = static_cast<uint8_t>(data); #else - b[s->bufferOffset ^ 3] = data; + b[s->bufferOffset ^ 3] = static_cast<uint8_t>(data); #endif s->bufferOffset++; if (s->bufferOffset == BLOCK_LENGTH) { diff --git a/tools/fuzzing/libfuzzer/FuzzerTracePC.cpp b/tools/fuzzing/libfuzzer/FuzzerTracePC.cpp @@ -69,6 +69,9 @@ void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) { } void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) { + if (Start == Stop) { + return; + } const PCTableEntry *B = reinterpret_cast<const PCTableEntry *>(Start); const PCTableEntry *E = reinterpret_cast<const PCTableEntry *>(Stop); if (NumPCTables && ModulePCTable[NumPCTables - 1].Start == B) return; @@ -106,6 +109,15 @@ void TracePC::PrintModuleInfo() { } if (size_t NumExtraCounters = ExtraCountersEnd() - ExtraCountersBegin()) Printf("INFO: %zd Extra Counters\n", NumExtraCounters); + + size_t MaxFeatures = CollectFeatures([](uint32_t) {}); + if (MaxFeatures > std::numeric_limits<uint32_t>::max()) + Printf("WARNING: The coverage PC tables may produce up to %zu features.\n" + "This exceeds the maximum 32-bit value. Some features may be\n" + "ignored, and fuzzing may become less precise. If possible,\n" + "consider refactoring the fuzzer into several smaller fuzzers\n" + "linked against only a portion of the current target.\n", + MaxFeatures); } ATTRIBUTE_NO_SANITIZE_ALL @@ -124,13 +136,14 @@ inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) { // so we return (pc-2) in that case in order to be safe. // For A32 mode we return (pc-4) because all instructions are 32 bit long. return (PC - 3) & (~1); -#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__aarch64__) - // PCs are always 4 byte aligned. - return PC - 4; #elif defined(__sparc__) || defined(__mips__) return PC - 8; -#else +#elif defined(__riscv__) + return PC - 2; +#elif defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64) return PC - 1; +#else + return PC - 4; #endif } @@ -139,8 +152,8 @@ inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) { ALWAYS_INLINE uintptr_t TracePC::GetNextInstructionPc(uintptr_t PC) { #if defined(__mips__) return PC + 8; -#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \ - defined(__aarch64__) +#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \ + defined(__aarch64__) || defined(__loongarch__) return PC + 4; #else return PC + 1; @@ -148,7 +161,7 @@ ALWAYS_INLINE uintptr_t TracePC::GetNextInstructionPc(uintptr_t PC) { } void TracePC::UpdateObservedPCs() { - Vector<uintptr_t> CoveredFuncs; + std::vector<uintptr_t> CoveredFuncs; auto ObservePC = [&](const PCTableEntry *TE) { if (ObservedPCs.insert(TE).second && DoPrintNewPCs) { PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", @@ -238,13 +251,13 @@ void TracePC::IterateCoveredFunctions(CallBack CB) { } } -int TracePC::SetFocusFunction(const std::string &FuncName) { +void TracePC::SetFocusFunction(const std::string &FuncName) { // This function should be called once. assert(!FocusFunctionCounterPtr); // "auto" is not a valid function name. If this function is called with "auto" // that means the auto focus functionality failed. if (FuncName.empty() || FuncName == "auto") - return 0; + return; for (size_t M = 0; M < NumModules; M++) { auto &PCTE = ModulePCTable[M]; size_t N = PCTE.Stop - PCTE.Start; @@ -256,20 +269,20 @@ int TracePC::SetFocusFunction(const std::string &FuncName) { if (FuncName != Name) continue; Printf("INFO: Focus function is set to '%s'\n", Name.c_str()); FocusFunctionCounterPtr = Modules[M].Start() + I; - return 0; + return; } } Printf("ERROR: Failed to set focus function. Make sure the function name is " "valid (%s) and symbolization is enabled.\n", FuncName.c_str()); - return 1; + exit(1); } bool TracePC::ObservedFocusFunction() { return FocusFunctionCounterPtr && *FocusFunctionCounterPtr; } -void TracePC::PrintCoverage() { +void TracePC::PrintCoverage(bool PrintAllCounters) { if (!EF->__sanitizer_symbolize_pc || !EF->__sanitizer_get_module_and_offset_for_pc) { Printf("INFO: __sanitizer_symbolize_pc or " @@ -277,7 +290,7 @@ void TracePC::PrintCoverage() { " not printing coverage\n"); return; } - Printf("COVERAGE:\n"); + Printf(PrintAllCounters ? "FULL COVERAGE:\n" : "COVERAGE:\n"); auto CoveredFunctionCallback = [&](const PCTableEntry *First, const PCTableEntry *Last, uintptr_t Counter) { @@ -291,18 +304,34 @@ void TracePC::PrintCoverage() { FunctionStr = FunctionStr.substr(3); std::string LineStr = DescribePC("%l", VisualizePC); size_t NumEdges = Last - First; - Vector<uintptr_t> UncoveredPCs; + std::vector<uintptr_t> UncoveredPCs; + std::vector<uintptr_t> CoveredPCs; for (auto TE = First; TE < Last; TE++) if (!ObservedPCs.count(TE)) UncoveredPCs.push_back(TE->PC); - Printf("%sCOVERED_FUNC: hits: %zd", Counter ? "" : "UN", Counter); - Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges); - Printf(" %s %s:%s\n", FunctionStr.c_str(), FileStr.c_str(), - LineStr.c_str()); - if (Counter) + else + CoveredPCs.push_back(TE->PC); + + if (PrintAllCounters) { + Printf("U"); for (auto PC : UncoveredPCs) - Printf(" UNCOVERED_PC: %s\n", - DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str()); + Printf(DescribePC(" %l", GetNextInstructionPc(PC)).c_str()); + Printf("\n"); + + Printf("C"); + for (auto PC : CoveredPCs) + Printf(DescribePC(" %l", GetNextInstructionPc(PC)).c_str()); + Printf("\n"); + } else { + Printf("%sCOVERED_FUNC: hits: %zd", Counter ? "" : "UN", Counter); + Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges); + Printf(" %s %s:%s\n", FunctionStr.c_str(), FileStr.c_str(), + LineStr.c_str()); + if (Counter) + for (auto PC : UncoveredPCs) + Printf(" UNCOVERED_PC: %s\n", + DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str()); + } }; IterateCoveredFunctions(CoveredFunctionCallback); @@ -340,7 +369,7 @@ void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, uint8_t HammingDistance = 0; for (; I < Len; I++) { if (B1[I] != B2[I] || (StopAtZero && B1[I] == 0)) { - HammingDistance = Popcountll(B1[I] ^ B2[I]); + HammingDistance = static_cast<uint8_t>(Popcountll(B1[I] ^ B2[I])); break; } } @@ -366,6 +395,7 @@ void TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) { ValueProfileMap.AddValue(PC * 128 + 64 + AbsoluteDistance); } +ATTRIBUTE_NO_SANITIZE_MEMORY static size_t InternalStrnlen(const char *S, size_t MaxLen) { size_t Len = 0; for (; Len < MaxLen && S[Len]; Len++) {} @@ -373,7 +403,8 @@ static size_t InternalStrnlen(const char *S, size_t MaxLen) { } // Finds min of (strlen(S1), strlen(S2)). -// Needed bacause one of these strings may actually be non-zero terminated. +// Needed because one of these strings may actually be non-zero terminated. +ATTRIBUTE_NO_SANITIZE_MEMORY static size_t InternalStrnlen2(const char *S1, const char *S2) { size_t Len = 0; for (; S1[Len] && S2[Len]; Len++) {} diff --git a/tools/fuzzing/libfuzzer/FuzzerTracePC.h b/tools/fuzzing/libfuzzer/FuzzerTracePC.h @@ -54,7 +54,7 @@ struct MemMemTable { void Add(const uint8_t *Data, size_t Size) { if (Size <= 2) return; Size = std::min(Size, Word::GetMaxSize()); - size_t Idx = SimpleFastHash(Data, Size) % kSize; + auto Idx = SimpleFastHash(Data, Size) % kSize; MemMemWords[Idx].Set(Data, Size); } const Word &Get(size_t Idx) { @@ -79,7 +79,7 @@ class TracePC { void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; } void SetPrintNewFuncs(size_t P) { NumPrintNewFuncs = P; } void UpdateObservedPCs(); - template <class Callback> void CollectFeatures(Callback CB) const; + template <class Callback> size_t CollectFeatures(Callback CB) const; void ResetMaps() { ValueProfileMap.Reset(); @@ -94,7 +94,7 @@ class TracePC { void PrintModuleInfo(); - void PrintCoverage(); + void PrintCoverage(bool PrintAllCounters); template<class CallBack> void IterateCoveredFunctions(CallBack CB); @@ -116,7 +116,7 @@ class TracePC { CB(PC); } - int SetFocusFunction(const std::string &FuncName); + void SetFocusFunction(const std::string &FuncName); bool ObservedFocusFunction(); struct PCTableEntry { @@ -169,7 +169,7 @@ private: size_t NumPCTables; size_t NumPCsInPCTables; - Set<const PCTableEntry*> ObservedPCs; + std::set<const PCTableEntry *> ObservedPCs; std::unordered_map<uintptr_t, uintptr_t> ObservedFuncs; // PC => Counter. uint8_t *FocusFunctionCounterPtr = nullptr; @@ -193,11 +193,13 @@ size_t ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End, Handle8bitCounter(FirstFeature, P - Begin, V); // Iterate by Step bytes at a time. - for (; P < End; P += Step) - if (LargeType Bundle = *reinterpret_cast<const LargeType *>(P)) + for (; P + Step <= End; P += Step) + if (LargeType Bundle = *reinterpret_cast<const LargeType *>(P)) { + Bundle = HostToLE(Bundle); for (size_t I = 0; I < Step; I++, Bundle >>= 8) if (uint8_t V = Bundle & 0xff) Handle8bitCounter(FirstFeature, P - Begin + I, V); + } // Iterate by 1 byte until the end. for (; P < End; P++) @@ -232,16 +234,16 @@ unsigned CounterToFeature(T Counter) { return Bit; } -template <class Callback> // void Callback(size_t Feature) -ATTRIBUTE_NO_SANITIZE_ADDRESS -ATTRIBUTE_NOINLINE -void TracePC::CollectFeatures(Callback HandleFeature) const { +template <class Callback> // void Callback(uint32_t Feature) +ATTRIBUTE_NO_SANITIZE_ADDRESS ATTRIBUTE_NOINLINE size_t +TracePC::CollectFeatures(Callback HandleFeature) const { auto Handle8bitCounter = [&](size_t FirstFeature, size_t Idx, uint8_t Counter) { if (UseCounters) - HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Counter)); + HandleFeature(static_cast<uint32_t>(FirstFeature + Idx * 8 + + CounterToFeature(Counter))); else - HandleFeature(FirstFeature + Idx); + HandleFeature(static_cast<uint32_t>(FirstFeature + Idx)); }; size_t FirstFeature = 0; @@ -261,16 +263,18 @@ void TracePC::CollectFeatures(Callback HandleFeature) const { if (UseValueProfileMask) { ValueProfileMap.ForEach([&](size_t Idx) { - HandleFeature(FirstFeature + Idx); + HandleFeature(static_cast<uint32_t>(FirstFeature + Idx)); }); FirstFeature += ValueProfileMap.SizeInBits(); } // Step function, grows similar to 8 * Log_2(A). - auto StackDepthStepFunction = [](uint32_t A) -> uint32_t { - if (!A) return A; - uint32_t Log2 = Log(A); - if (Log2 < 3) return A; + auto StackDepthStepFunction = [](size_t A) -> size_t { + if (!A) + return A; + auto Log2 = Log(A); + if (Log2 < 3) + return A; Log2 -= 3; return (Log2 + 1) * 8 + ((A >> Log2) & 7); }; @@ -278,8 +282,13 @@ void TracePC::CollectFeatures(Callback HandleFeature) const { assert(StackDepthStepFunction(1024 * 4) == 80); assert(StackDepthStepFunction(1024 * 1024) == 144); - if (auto MaxStackOffset = GetMaxStackOffset()) - HandleFeature(FirstFeature + StackDepthStepFunction(MaxStackOffset / 8)); + if (auto MaxStackOffset = GetMaxStackOffset()) { + HandleFeature(static_cast<uint32_t>( + FirstFeature + StackDepthStepFunction(MaxStackOffset / 8))); + FirstFeature += StackDepthStepFunction(std::numeric_limits<size_t>::max()); + } + + return FirstFeature; } extern TracePC TPC; diff --git a/tools/fuzzing/libfuzzer/FuzzerUtil.cpp b/tools/fuzzing/libfuzzer/FuzzerUtil.cpp @@ -43,7 +43,7 @@ void PrintASCIIByte(uint8_t Byte) { else if (Byte >= 32 && Byte < 127) Printf("%c", Byte); else - Printf("\\x%02x", Byte); + Printf("\\%03o", Byte); } void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) { @@ -111,7 +111,7 @@ bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) { char Hex[] = "0xAA"; Hex[2] = Str[Pos + 2]; Hex[3] = Str[Pos + 3]; - U->push_back(strtol(Hex, nullptr, 16)); + U->push_back(static_cast<uint8_t>(strtol(Hex, nullptr, 16))); Pos += 3; continue; } @@ -124,7 +124,7 @@ bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) { return true; } -bool ParseDictionaryFile(const std::string &Text, Vector<Unit> *Units) { +bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units) { if (Text.empty()) { Printf("ParseDictionaryFile: file does not exist or is empty\n"); return false; @@ -226,10 +226,11 @@ unsigned NumberOfCpuCores() { return N; } -size_t SimpleFastHash(const uint8_t *Data, size_t Size) { - size_t Res = 0; +uint64_t SimpleFastHash(const void *Data, size_t Size, uint64_t Initial) { + uint64_t Res = Initial; + const uint8_t *Bytes = static_cast<const uint8_t *>(Data); for (size_t i = 0; i < Size; i++) - Res = Res * 11 + Data[i]; + Res = Res * 11 + Bytes[i]; return Res; } diff --git a/tools/fuzzing/libfuzzer/FuzzerUtil.h b/tools/fuzzing/libfuzzer/FuzzerUtil.h @@ -59,6 +59,8 @@ size_t GetPeakRSSMb(); int ExecuteCommand(const Command &Cmd); bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput); +void SetThreadName(std::thread &thread, const std::string &name); + // Fuchsia does not have popen/pclose. FILE *OpenProcessPipe(const char *Command, const char *Mode); int CloseProcessPipe(FILE *F); @@ -66,10 +68,10 @@ int CloseProcessPipe(FILE *F); const void *SearchMemory(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen); -std::string CloneArgsWithoutX(const Vector<std::string> &Args, +std::string CloneArgsWithoutX(const std::vector<std::string> &Args, const char *X1, const char *X2); -inline std::string CloneArgsWithoutX(const Vector<std::string> &Args, +inline std::string CloneArgsWithoutX(const std::vector<std::string> &Args, const char *X) { return CloneArgsWithoutX(Args, X, X); } @@ -88,11 +90,14 @@ std::string DisassembleCmd(const std::string &FileName); std::string SearchRegexCmd(const std::string &Regex); -size_t SimpleFastHash(const uint8_t *Data, size_t Size); +uint64_t SimpleFastHash(const void *Data, size_t Size, uint64_t Initial = 0); + +inline size_t Log(size_t X) { + return static_cast<size_t>((sizeof(unsigned long long) * 8) - Clzll(X) - 1); +} -inline uint32_t Log(uint32_t X) { return 32 - Clz(X) - 1; } +size_t PageSize(); -inline size_t PageSize() { return 4096; } inline uint8_t *RoundUpByPage(uint8_t *P) { uintptr_t X = reinterpret_cast<uintptr_t>(P); size_t Mask = PageSize() - 1; @@ -106,6 +111,12 @@ inline uint8_t *RoundDownByPage(uint8_t *P) { return reinterpret_cast<uint8_t *>(X); } +#if __BYTE_ORDER == __LITTLE_ENDIAN +template <typename T> T HostToLE(T X) { return X; } +#else +template <typename T> T HostToLE(T X) { return Bswap(X); } +#endif + } // namespace fuzzer #endif // LLVM_FUZZER_UTIL_H diff --git a/tools/fuzzing/libfuzzer/FuzzerUtilDarwin.cpp b/tools/fuzzing/libfuzzer/FuzzerUtilDarwin.cpp @@ -165,6 +165,11 @@ void DiscardOutput(int Fd) { fclose(Temp); } +void SetThreadName(std::thread &thread, const std::string &name) { + // TODO ? + // Darwin allows to set the name only on the current thread it seems +} + } // namespace fuzzer #endif // LIBFUZZER_APPLE diff --git a/tools/fuzzing/libfuzzer/FuzzerUtilFuchsia.cpp b/tools/fuzzing/libfuzzer/FuzzerUtilFuchsia.cpp @@ -52,6 +52,12 @@ void CrashTrampolineAsm() __asm__("CrashTrampolineAsm"); namespace { +// The signal handler thread uses Zircon exceptions to resume crashed threads +// into libFuzzer's POSIX signal handlers. The associated event is used to +// signal when the thread is running, and when it should stop. +std::thread SignalHandler; +zx_handle_t SignalHandlerEvent = ZX_HANDLE_INVALID; + // Helper function to handle Zircon syscall failures. void ExitOnErr(zx_status_t Status, const char *Syscall) { if (Status != ZX_OK) { @@ -62,40 +68,15 @@ void ExitOnErr(zx_status_t Status, const char *Syscall) { } void AlarmHandler(int Seconds) { + // Signal the alarm thread started. + ExitOnErr(_zx_object_signal(SignalHandlerEvent, 0, ZX_USER_SIGNAL_0), + "_zx_object_signal alarm"); while (true) { SleepSeconds(Seconds); Fuzzer::StaticAlarmCallback(); } } -void InterruptHandler() { - fd_set readfds; - // Ctrl-C sends ETX in Zircon. - do { - FD_ZERO(&readfds); - FD_SET(STDIN_FILENO, &readfds); - select(STDIN_FILENO + 1, &readfds, nullptr, nullptr, nullptr); - } while(!FD_ISSET(STDIN_FILENO, &readfds) || getchar() != 0x03); - Fuzzer::StaticInterruptCallback(); -} - -// CFAOffset is used to reference the stack pointer before entering the -// trampoline (Stack Pointer + CFAOffset = prev Stack Pointer). Before jumping -// to the trampoline we copy all the registers onto the stack. We need to make -// sure that the new stack has enough space to store all the registers. -// -// The trampoline holds CFI information regarding the registers stored in the -// stack, which is then used by the unwinder to restore them. -#if defined(__x86_64__) -// In x86_64 the crashing function might also be using the red zone (128 bytes -// on top of their rsp). -constexpr size_t CFAOffset = 128 + sizeof(zx_thread_state_general_regs_t); -#elif defined(__aarch64__) -// In aarch64 we need to always have the stack pointer aligned to 16 bytes, so we -// make sure that we are keeping that same alignment. -constexpr size_t CFAOffset = (sizeof(zx_thread_state_general_regs_t) + 15) & -(uintptr_t)16; -#endif - // For the crash handler, we need to call Fuzzer::StaticCrashSignalCallback // without POSIX signal handlers. To achieve this, we use an assembly function // to add the necessary CFI unwinding information and a C function to bridge @@ -109,6 +90,7 @@ constexpr size_t CFAOffset = (sizeof(zx_thread_state_general_regs_t) + 15) & -(u // Alternatively, Fuchsia may in future actually implement basic signal // handling for the machine trap signals. #if defined(__x86_64__) + #define FOREACH_REGISTER(OP_REG, OP_NUM) \ OP_REG(rax) \ OP_REG(rbx) \ @@ -129,6 +111,7 @@ constexpr size_t CFAOffset = (sizeof(zx_thread_state_general_regs_t) + 15) & -(u OP_REG(rip) #elif defined(__aarch64__) + #define FOREACH_REGISTER(OP_REG, OP_NUM) \ OP_NUM(0) \ OP_NUM(1) \ @@ -162,6 +145,41 @@ constexpr size_t CFAOffset = (sizeof(zx_thread_state_general_regs_t) + 15) & -(u OP_NUM(29) \ OP_REG(sp) +#elif defined(__riscv) + +#define FOREACH_REGISTER(OP_REG, OP_NUM) \ + OP_REG(ra) \ + OP_REG(sp) \ + OP_REG(gp) \ + OP_REG(tp) \ + OP_REG(t0) \ + OP_REG(t1) \ + OP_REG(t2) \ + OP_REG(s0) \ + OP_REG(s1) \ + OP_REG(a0) \ + OP_REG(a1) \ + OP_REG(a2) \ + OP_REG(a3) \ + OP_REG(a4) \ + OP_REG(a5) \ + OP_REG(a6) \ + OP_REG(a7) \ + OP_REG(s2) \ + OP_REG(s3) \ + OP_REG(s4) \ + OP_REG(s5) \ + OP_REG(s6) \ + OP_REG(s7) \ + OP_REG(s8) \ + OP_REG(s9) \ + OP_REG(s10) \ + OP_REG(s11) \ + OP_REG(t3) \ + OP_REG(t4) \ + OP_REG(t5) \ + OP_REG(t6) \ + #else #error "Unsupported architecture for fuzzing on Fuchsia" #endif @@ -174,10 +192,10 @@ constexpr size_t CFAOffset = (sizeof(zx_thread_state_general_regs_t) + 15) & -(u // Produces an assembler immediate operand for the named or numbered register. // This operand contains the offset of the register relative to the CFA. -#define ASM_OPERAND_REG(reg) \ - [reg] "i"(offsetof(zx_thread_state_general_regs_t, reg) - CFAOffset), -#define ASM_OPERAND_NUM(num) \ - [x##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num]) - CFAOffset), +#define ASM_OPERAND_REG(reg) \ + [reg] "i"(offsetof(zx_thread_state_general_regs_t, reg)), +#define ASM_OPERAND_NUM(num) \ + [x##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num])), // Trampoline to bridge from the assembly below to the static C++ crash // callback. @@ -189,62 +207,67 @@ static void StaticCrashHandler() { } } -// Creates the trampoline with the necessary CFI information to unwind through -// to the crashing call stack: -// * Defining the CFA so that it points to the stack pointer at the point -// of crash. -// * Storing all registers at the point of crash in the stack and refer to them -// via CFI information (relative to the CFA). -// * Setting the return column so the unwinder knows how to continue unwinding. -// * (x86_64) making sure rsp is aligned before calling StaticCrashHandler. -// * Calling StaticCrashHandler that will trigger the unwinder. +// This trampoline function has the necessary CFI information to unwind +// and get a backtrace: +// * The stack contains a copy of all the registers at the point of crash, +// the code has CFI directives specifying how to restore them. +// * A call to StaticCrashHandler, which will print the stacktrace and exit +// the fuzzer, generating a crash artifact. // // The __attribute__((used)) is necessary because the function // is never called; it's just a container around the assembly to allow it to // use operands for compile-time computed constants. __attribute__((used)) void MakeTrampoline() { - __asm__(".cfi_endproc\n" - ".pushsection .text.CrashTrampolineAsm\n" - ".type CrashTrampolineAsm,STT_FUNC\n" -"CrashTrampolineAsm:\n" - ".cfi_startproc simple\n" - ".cfi_signal_frame\n" + __asm__( + ".cfi_endproc\n" + ".pushsection .text.CrashTrampolineAsm\n" + ".type CrashTrampolineAsm,STT_FUNC\n" + "CrashTrampolineAsm:\n" + ".cfi_startproc simple\n" + ".cfi_signal_frame\n" #if defined(__x86_64__) - ".cfi_return_column rip\n" - ".cfi_def_cfa rsp, %c[CFAOffset]\n" - FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) - "mov %%rsp, %%rbp\n" - ".cfi_def_cfa_register rbp\n" - "andq $-16, %%rsp\n" - "call %c[StaticCrashHandler]\n" - "ud2\n" + ".cfi_return_column rip\n" + ".cfi_def_cfa rsp, 0\n" + FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) + "call %c[StaticCrashHandler]\n" + "ud2\n" #elif defined(__aarch64__) - ".cfi_return_column 33\n" - ".cfi_def_cfa sp, %c[CFAOffset]\n" - FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) - ".cfi_offset 33, %c[pc]\n" - ".cfi_offset 30, %c[lr]\n" - "bl %c[StaticCrashHandler]\n" - "brk 1\n" + ".cfi_return_column 33\n" + ".cfi_def_cfa sp, 0\n" + FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) + ".cfi_offset 33, %c[pc]\n" + ".cfi_offset 30, %c[lr]\n" + "bl %c[StaticCrashHandler]\n" + "brk 1\n" +#elif defined(__riscv) + ".cfi_return_column 64\n" + ".cfi_def_cfa sp, 0\n" + ".cfi_offset 64, %[pc]\n" + FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) + "call %c[StaticCrashHandler]\n" + "unimp\n" #else #error "Unsupported architecture for fuzzing on Fuchsia" #endif - ".cfi_endproc\n" - ".size CrashTrampolineAsm, . - CrashTrampolineAsm\n" - ".popsection\n" - ".cfi_startproc\n" - : // No outputs - : FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM) + ".cfi_endproc\n" + ".size CrashTrampolineAsm, . - CrashTrampolineAsm\n" + ".popsection\n" + ".cfi_startproc\n" + : // No outputs + : FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM) +#if defined(__aarch64__) || defined(__riscv) + ASM_OPERAND_REG(pc) +#endif #if defined(__aarch64__) - ASM_OPERAND_REG(pc) - ASM_OPERAND_REG(lr) + ASM_OPERAND_REG(lr) #endif - [StaticCrashHandler] "i" (StaticCrashHandler), - [CFAOffset] "i" (CFAOffset)); + [StaticCrashHandler] "i"(StaticCrashHandler)); } -void CrashHandler(zx_handle_t *Event) { +void CrashHandler() { + assert(SignalHandlerEvent != ZX_HANDLE_INVALID); + // This structure is used to ensure we close handles to objects we create in // this handler. struct ScopedHandle { @@ -262,16 +285,31 @@ void CrashHandler(zx_handle_t *Event) { Self, ZX_EXCEPTION_CHANNEL_DEBUGGER, &Channel.Handle), "_zx_task_create_exception_channel"); - ExitOnErr(_zx_object_signal(*Event, 0, ZX_USER_SIGNAL_0), + // Signal the crash thread started. + ExitOnErr(_zx_object_signal(SignalHandlerEvent, 0, ZX_USER_SIGNAL_0), "_zx_object_signal"); // This thread lives as long as the process in order to keep handling // crashes. In practice, the first crashed thread to reach the end of the // StaticCrashHandler will end the process. while (true) { - ExitOnErr(_zx_object_wait_one(Channel.Handle, ZX_CHANNEL_READABLE, - ZX_TIME_INFINITE, nullptr), - "_zx_object_wait_one"); + zx_wait_item_t WaitItems[] = { + { + .handle = SignalHandlerEvent, + .waitfor = ZX_USER_SIGNAL_1, + .pending = 0, + }, + { + .handle = Channel.Handle, + .waitfor = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, + .pending = 0, + }, + }; + auto Status = _zx_object_wait_many( + WaitItems, sizeof(WaitItems) / sizeof(WaitItems[0]), ZX_TIME_INFINITE); + if (Status != ZX_OK || (WaitItems[1].pending & ZX_CHANNEL_READABLE) == 0) { + break; + } zx_exception_info_t ExceptionInfo; ScopedHandle Exception; @@ -307,14 +345,19 @@ void CrashHandler(zx_handle_t *Event) { // onto the stack and jump into a trampoline with CFI instructions on how // to restore it. #if defined(__x86_64__) - uintptr_t StackPtr = GeneralRegisters.rsp - CFAOffset; + + uintptr_t StackPtr = + (GeneralRegisters.rsp - (128 + sizeof(GeneralRegisters))) & + -(uintptr_t)16; __unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters, sizeof(GeneralRegisters)); GeneralRegisters.rsp = StackPtr; GeneralRegisters.rip = reinterpret_cast<zx_vaddr_t>(CrashTrampolineAsm); -#elif defined(__aarch64__) - uintptr_t StackPtr = GeneralRegisters.sp - CFAOffset; +#elif defined(__aarch64__) || defined(__riscv) + + uintptr_t StackPtr = + (GeneralRegisters.sp - sizeof(GeneralRegisters)) & -(uintptr_t)16; __unsanitized_memcpy(reinterpret_cast<void *>(StackPtr), &GeneralRegisters, sizeof(GeneralRegisters)); GeneralRegisters.sp = StackPtr; @@ -338,10 +381,57 @@ void CrashHandler(zx_handle_t *Event) { } } +void StopSignalHandler() { + _zx_object_signal(SignalHandlerEvent, 0, ZX_USER_SIGNAL_1); + if (SignalHandler.joinable()) { + SignalHandler.join(); + } + _zx_handle_close(SignalHandlerEvent); +} + +void RssThread(Fuzzer *F, size_t RssLimitMb) { + // Signal the rss thread started. + // + // We must wait for this thread to start because we could accidentally suspend + // it while the crash handler is attempting to handle the + // ZX_EXCP_THREAD_STARTING exception. If the crash handler is suspended by the + // lsan machinery, then there's no way for this thread to indicate it's + // suspended because it's blocked on waiting for the exception to be handled. + ExitOnErr(_zx_object_signal(SignalHandlerEvent, 0, ZX_USER_SIGNAL_0), + "_zx_object_signal rss"); + while (true) { + SleepSeconds(1); + size_t Peak = GetPeakRSSMb(); + if (Peak > RssLimitMb) + F->RssLimitCallback(); + } +} + } // namespace +void StartRssThread(Fuzzer *F, size_t RssLimitMb) { + // Set up the crash handler and wait until it is ready before proceeding. + assert(SignalHandlerEvent == ZX_HANDLE_INVALID); + ExitOnErr(_zx_event_create(0, &SignalHandlerEvent), "_zx_event_create"); + + if (!RssLimitMb) + return; + std::thread T(RssThread, F, RssLimitMb); + T.detach(); + + // Wait for the rss thread to start. + ExitOnErr(_zx_object_wait_one(SignalHandlerEvent, ZX_USER_SIGNAL_0, + ZX_TIME_INFINITE, nullptr), + "_zx_object_wait_one rss"); + ExitOnErr(_zx_object_signal(SignalHandlerEvent, ZX_USER_SIGNAL_0, 0), + "_zx_object_signal rss clear"); +} + // Platform specific functions. void SetSignalHandler(const FuzzingOptions &Options) { + assert(SignalHandlerEvent != ZX_HANDLE_INVALID && + "This should've been setup by StartRssThread."); + // 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. @@ -354,33 +444,38 @@ void SetSignalHandler(const FuzzingOptions &Options) { Printf("%s", Buf); // Set up alarm handler if needed. - if (Options.UnitTimeoutSec > 0) { + if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) { std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1); T.detach(); - } - // Set up interrupt handler if needed. - if (Options.HandleInt || Options.HandleTerm) { - std::thread T(InterruptHandler); - T.detach(); + // Wait for the alarm thread to start. + // + // We must wait for this thread to start because we could accidentally + // suspend it while the crash handler is attempting to handle the + // ZX_EXCP_THREAD_STARTING exception. If the crash handler is suspended by + // the lsan machinery, then there's no way for this thread to indicate it's + // suspended because it's blocked on waiting for the exception to be + // handled. + ExitOnErr(_zx_object_wait_one(SignalHandlerEvent, ZX_USER_SIGNAL_0, + ZX_TIME_INFINITE, nullptr), + "_zx_object_wait_one alarm"); + ExitOnErr(_zx_object_signal(SignalHandlerEvent, ZX_USER_SIGNAL_0, 0), + "_zx_object_signal alarm clear"); } + // Options.HandleInt and Options.HandleTerm are not supported on Fuchsia + // Early exit if no crash handler needed. if (!Options.HandleSegv && !Options.HandleBus && !Options.HandleIll && - !Options.HandleFpe && !Options.HandleAbrt) + !Options.HandleFpe && !Options.HandleAbrt && !Options.HandleTrap) return; - // Set up the crash handler and wait until it is ready before proceeding. - zx_handle_t Event; - ExitOnErr(_zx_event_create(0, &Event), "_zx_event_create"); - - std::thread T(CrashHandler, &Event); - zx_status_t Status = - _zx_object_wait_one(Event, ZX_USER_SIGNAL_0, ZX_TIME_INFINITE, nullptr); - _zx_handle_close(Event); + SignalHandler = std::thread(CrashHandler); + zx_status_t Status = _zx_object_wait_one(SignalHandlerEvent, ZX_USER_SIGNAL_0, + ZX_TIME_INFINITE, nullptr); ExitOnErr(Status, "_zx_object_wait_one"); - T.detach(); + std::atexit(StopSignalHandler); } void SleepSeconds(int Seconds) { @@ -530,7 +625,7 @@ int ExecuteCommand(const Command &Cmd) { return rc; } - return Info.return_code; + return static_cast<int>(Info.return_code); } bool ExecuteCommand(const Command &BaseCmd, std::string *CmdOutput) { @@ -560,6 +655,19 @@ void DiscardOutput(int Fd) { dup2(nullfd, Fd); } +size_t PageSize() { + static size_t PageSizeCached = _zx_system_get_page_size(); + return PageSizeCached; +} + +void SetThreadName(std::thread &thread, const std::string &name) { + if (zx_status_t s = zx_object_set_property( + thread.native_handle(), ZX_PROP_NAME, name.data(), name.size()); + s != ZX_OK) + Printf("SetThreadName for name %s failed: %s", name.c_str(), + zx_status_get_string(s)); +} + } // namespace fuzzer #endif // LIBFUZZER_FUCHSIA diff --git a/tools/fuzzing/libfuzzer/FuzzerUtilLinux.cpp b/tools/fuzzing/libfuzzer/FuzzerUtilLinux.cpp @@ -9,9 +9,11 @@ //===----------------------------------------------------------------------===// #include "FuzzerPlatform.h" #if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \ - LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN + LIBFUZZER_EMSCRIPTEN #include "FuzzerCommand.h" +#include "FuzzerInternal.h" +#include <signal.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> @@ -25,6 +27,8 @@ int ExecuteCommand(const Command &Cmd) { int exit_code = system(CmdLine.c_str()); if (WIFEXITED(exit_code)) return WEXITSTATUS(exit_code); + if (WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGINT) + return Fuzzer::InterruptExitCode(); return exit_code; } @@ -36,6 +40,14 @@ void DiscardOutput(int Fd) { fclose(Temp); } +void SetThreadName(std::thread &thread, const std::string &name) { +#if LIBFUZZER_LINUX || LIBFUZZER_FREEBSD + (void)pthread_setname_np(thread.native_handle(), name.c_str()); +#elif LIBFUZZER_NETBSD + (void)pthread_setname_np(thread.native_handle(), "%s", const_cast<char *>(name.c_str())); +#endif +} + } // namespace fuzzer #endif diff --git a/tools/fuzzing/libfuzzer/FuzzerUtilPosix.cpp b/tools/fuzzing/libfuzzer/FuzzerUtilPosix.cpp @@ -77,10 +77,13 @@ static void SetSigaction(int signum, return; } - sigact = {}; - sigact.sa_flags = SA_SIGINFO; - sigact.sa_sigaction = callback; - if (sigaction(signum, &sigact, 0)) { + struct sigaction new_sigact = {}; + // Address sanitizer needs SA_ONSTACK (causing the signal handler to run on a + // dedicated stack) in order to be able to detect stack overflows; keep the + // flag if it's set. + new_sigact.sa_flags = SA_SIGINFO | (sigact.sa_flags & SA_ONSTACK); + new_sigact.sa_sigaction = callback; + if (sigaction(signum, &new_sigact, nullptr)) { Printf("libFuzzer: sigaction failed with %d\n", errno); exit(1); } @@ -113,7 +116,7 @@ void SetTimer(int Seconds) { void SetSignalHandler(const FuzzingOptions& Options) { // setitimer is not implemented in emscripten. - if (Options.UnitTimeoutSec > 0 && !LIBFUZZER_EMSCRIPTEN) + if (Options.HandleAlrm && Options.UnitTimeoutSec > 0 && !LIBFUZZER_EMSCRIPTEN) SetTimer(Options.UnitTimeoutSec / 2 + 1); if (Options.HandleInt) SetSigaction(SIGINT, InterruptHandler); @@ -129,6 +132,8 @@ void SetSignalHandler(const FuzzingOptions& Options) { SetSigaction(SIGILL, CrashHandler); if (Options.HandleFpe) SetSigaction(SIGFPE, CrashHandler); + if (Options.HandleTrap) + SetSigaction(SIGTRAP, CrashHandler); if (Options.HandleXfsz) SetSigaction(SIGXFSZ, FileSizeExceedHandler); if (Options.HandleUsr1) @@ -148,7 +153,7 @@ size_t GetPeakRSSMb() { if (getrusage(RUSAGE_SELF, &usage)) return 0; if (LIBFUZZER_LINUX || LIBFUZZER_FREEBSD || LIBFUZZER_NETBSD || - LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN) { + LIBFUZZER_EMSCRIPTEN) { // ru_maxrss is in KiB return usage.ru_maxrss >> 10; } else if (LIBFUZZER_APPLE) { @@ -180,6 +185,11 @@ std::string SearchRegexCmd(const std::string &Regex) { return "grep '" + Regex + "'"; } +size_t PageSize() { + static size_t PageSizeCached = sysconf(_SC_PAGESIZE); + return PageSizeCached; +} + } // namespace fuzzer #endif // LIBFUZZER_POSIX diff --git a/tools/fuzzing/libfuzzer/FuzzerUtilWindows.cpp b/tools/fuzzing/libfuzzer/FuzzerUtilWindows.cpp @@ -21,10 +21,15 @@ #include <signal.h> #include <stdio.h> #include <sys/types.h> +// clang-format off #include <windows.h> - -// This must be included after windows.h. +// These must be included after windows.h. +// architecture need to be set before including +// libloaderapi +#include <libloaderapi.h> +#include <stringapiset.h> #include <psapi.h> +// clang-format on namespace fuzzer { @@ -60,7 +65,15 @@ static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { if (HandlerOpt->HandleFpe) Fuzzer::StaticCrashSignalCallback(); break; - // TODO: handle (Options.HandleXfsz) + // This is an undocumented exception code corresponding to a Visual C++ + // Exception. + // + // See: https://devblogs.microsoft.com/oldnewthing/20100730-00/?p=13273 + case 0xE06D7363: + if (HandlerOpt->HandleWinExcept) + Fuzzer::StaticCrashSignalCallback(); + break; + // TODO: Handle (Options.HandleXfsz) } return EXCEPTION_CONTINUE_SEARCH; } @@ -115,7 +128,7 @@ static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); } void SetSignalHandler(const FuzzingOptions& Options) { HandlerOpt = &Options; - if (Options.UnitTimeoutSec > 0) + if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1); if (Options.HandleInt || Options.HandleTerm) @@ -127,7 +140,7 @@ void SetSignalHandler(const FuzzingOptions& Options) { } if (Options.HandleSegv || Options.HandleBus || Options.HandleIll || - Options.HandleFpe) + Options.HandleFpe || Options.HandleWinExcept) SetUnhandledExceptionFilter(ExceptionHandler); if (Options.HandleAbrt) @@ -196,7 +209,7 @@ const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, } std::string DisassembleCmd(const std::string &FileName) { - Vector<std::string> command_vector; + std::vector<std::string> command_vector; command_vector.push_back("dumpbin /summary > nul"); if (ExecuteCommand(Command(command_vector)) == 0) return "dumpbin /disasm " + FileName; @@ -216,6 +229,38 @@ void DiscardOutput(int Fd) { fclose(Temp); } +size_t PageSize() { + static size_t PageSizeCached = []() -> size_t { + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwPageSize; + }(); + return PageSizeCached; +} + +void SetThreadName(std::thread &thread, const std::string &name) { +#ifndef __MINGW32__ + // Not setting the thread name in MinGW environments. MinGW C++ standard + // libraries can either use native Windows threads or pthreads, so we + // don't know with certainty what kind of thread handle we're getting + // from thread.native_handle() here. + typedef HRESULT(WINAPI * proc)(HANDLE, PCWSTR); + HMODULE kbase = GetModuleHandleA("KernelBase.dll"); + proc ThreadNameProc = reinterpret_cast<proc>( + (void *)GetProcAddress(kbase, "SetThreadDescription")); + if (ThreadNameProc) { + std::wstring buf; + auto sz = MultiByteToWideChar(CP_UTF8, 0, name.data(), -1, nullptr, 0); + if (sz > 0) { + buf.resize(sz); + if (MultiByteToWideChar(CP_UTF8, 0, name.data(), -1, &buf[0], sz) > 0) { + (void)ThreadNameProc(thread.native_handle(), buf.c_str()); + } + } + } +#endif +} + } // namespace fuzzer #endif // LIBFUZZER_WINDOWS diff --git a/tools/fuzzing/libfuzzer/moz.build b/tools/fuzzing/libfuzzer/moz.build @@ -6,50 +6,56 @@ Library('fuzzer') -EXPORTS += [ - 'FuzzerDefs.h', - 'FuzzerExtFunctions.def', - 'FuzzerExtFunctions.h', -] +if CONFIG['LIBFUZZER']: + EXPORTS += [ + 'FuzzerDefs.h', + 'FuzzerExtFunctions.def', + 'FuzzerExtFunctions.h', + ] + + SOURCES += [ + 'FuzzerCrossOver.cpp', + 'FuzzerDataFlowTrace.cpp', + 'FuzzerDriver.cpp', + 'FuzzerExtFunctionsDlsym.cpp', + 'FuzzerExtFunctionsWeak.cpp', + 'FuzzerExtFunctionsWindows.cpp', + 'FuzzerExtraCounters.cpp', + 'FuzzerExtraCountersDarwin.cpp', + 'FuzzerExtraCountersWindows.cpp', + 'FuzzerFork.cpp', + 'FuzzerIO.cpp', + 'FuzzerIOPosix.cpp', + 'FuzzerIOWindows.cpp', + 'FuzzerLoop.cpp', + 'FuzzerMerge.cpp', + 'FuzzerMutate.cpp', + 'FuzzerSHA1.cpp', + 'FuzzerTracePC.cpp', + 'FuzzerUtil.cpp', + 'FuzzerUtilDarwin.cpp', + 'FuzzerUtilFuchsia.cpp', + 'FuzzerUtilLinux.cpp', + 'FuzzerUtilPosix.cpp', + 'FuzzerUtilWindows.cpp', + ] -SOURCES += [ - 'FuzzerCrossOver.cpp', - 'FuzzerDataFlowTrace.cpp', - 'FuzzerDriver.cpp', - 'FuzzerExtFunctionsDlsym.cpp', - 'FuzzerExtFunctionsWeak.cpp', - 'FuzzerExtFunctionsWindows.cpp', - 'FuzzerExtraCounters.cpp', - 'FuzzerFork.cpp', - 'FuzzerIO.cpp', - 'FuzzerIOPosix.cpp', - 'FuzzerIOWindows.cpp', - 'FuzzerLoop.cpp', - 'FuzzerMerge.cpp', - 'FuzzerMutate.cpp', - 'FuzzerSHA1.cpp', - 'FuzzerTracePC.cpp', - 'FuzzerUtil.cpp', - 'FuzzerUtilDarwin.cpp', - 'FuzzerUtilFuchsia.cpp', - 'FuzzerUtilLinux.cpp', - 'FuzzerUtilPosix.cpp', - 'FuzzerUtilWindows.cpp', -] + if CONFIG['CC_TYPE'] == 'clang': + CXXFLAGS += ['-Wno-unreachable-code-return'] -if CONFIG['CC_TYPE'] == 'clang': - CXXFLAGS += ['-Wno-unreachable-code-return'] + # According to the LLVM docs, LibFuzzer isn't supposed to be built with any + # sanitizer flags and in fact, building it with ASan coverage currently causes + # Clang 3.9+ to crash, so we filter out all sanitizer-related flags here. + for flags_var in ('OS_CFLAGS', 'OS_CXXFLAGS'): + COMPILE_FLAGS[flags_var] = [ + f for f in COMPILE_FLAGS.get(flags_var, []) + if not f.startswith(('-fsanitize', '-fno-sanitize-')) + ] -# According to the LLVM docs, LibFuzzer isn't supposed to be built with any -# sanitizer flags and in fact, building it with ASan coverage currently causes -# Clang 3.9+ to crash, so we filter out all sanitizer-related flags here. -for flags_var in ('OS_CFLAGS', 'OS_CXXFLAGS'): - COMPILE_FLAGS[flags_var] = [ - f for f in COMPILE_FLAGS.get(flags_var, []) + LINK_FLAGS['OS'] = [ + f for f in LINK_FLAGS.get('OS', []) if not f.startswith(('-fsanitize', '-fno-sanitize-')) ] -LINK_FLAGS['OS'] = [ - f for f in LINK_FLAGS.get('OS', []) - if not f.startswith(('-fsanitize', '-fno-sanitize-')) -] +# Always export FuzzedDataProvider so that it can be used by AFL builds +EXPORTS += ['FuzzedDataProvider.h'] +\ No newline at end of file diff --git a/tools/fuzzing/libfuzzer/moz.yaml b/tools/fuzzing/libfuzzer/moz.yaml @@ -12,9 +12,9 @@ origin: url: https://llvm.org/docs/LibFuzzer.html - release: 76d07503f0c69f6632e6d8d4736e2a4cb4055a92 (2020-07-30T12:42:56Z). + release: 5b7a5f7c5f3c1dcd8ea9debf56983b85e393bc86 (2025-11-03T16:03:54Z). - revision: 76d07503f0c69f6632e6d8d4736e2a4cb4055a92 + revision: 5b7a5f7c5f3c1dcd8ea9debf56983b85e393bc86 license: Apache-2.0 license-file: LICENSE.TXT @@ -30,6 +30,7 @@ vendoring: - "**" include: + - "compiler-rt/include/fuzzer/*.h" - "compiler-rt/lib/fuzzer/*.h" - "compiler-rt/lib/fuzzer/*.cpp" - "compiler-rt/lib/fuzzer/*.def" @@ -39,13 +40,15 @@ vendoring: - patches/11-callback-rv.patch - patches/12-custom-mutator-fail.patch - patches/13-unused-write.patch - - patches/14-explicit-allocator.patch - patches/15-return-to-exit.patch update-actions: - action: move-dir from: '{yaml_dir}/compiler-rt/lib/fuzzer' to: '{yaml_dir}' + - action: move-dir + from: '{yaml_dir}/compiler-rt/include/fuzzer' + to: '{yaml_dir}' - action: delete-path path: '{yaml_dir}/FuzzerMain*' - action: delete-path diff --git a/tools/fuzzing/libfuzzer/patches/10-ef-runtime.patch b/tools/fuzzing/libfuzzer/patches/10-ef-runtime.patch @@ -1,20 +1,14 @@ -# HG changeset patch -# User Christian Holler <choller@mozilla.com> -# Date 1596126054 -7200 -# Thu Jul 30 18:20:54 2020 +0200 -# Node ID 8a2a26b33d516c43c366b2f24d731d27d9843349 -# Parent 997c4109edd112695097fd8c55cbacd976cab24a -[libFuzzer] Allow external functions to be defined at runtime +commit eb2fa4068fcdf6e4aaeb4e98e26d444e3e739bde +Author: Christian Holler <choller@mozilla.com> +Date: Thu Jul 30 18:20:54 2020 +0200 + + [libFuzzer] Allow external functions to be defined at runtime diff --git a/FuzzerDriver.cpp b/FuzzerDriver.cpp +index 5928d1d96acd..ff43cb031dff 100644 --- a/FuzzerDriver.cpp +++ b/FuzzerDriver.cpp -@@ -608,17 +608,18 @@ static Vector<SizedFile> ReadCorpora(con - SizedFiles.push_back({File, Size}); - return SizedFiles; - } - - int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { +@@ -653,7 +653,8 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { using namespace fuzzer; assert(argc && argv && "Argument pointers cannot be nullptr"); std::string Argv0((*argv)[0]); @@ -24,8 +18,3 @@ diff --git a/FuzzerDriver.cpp b/FuzzerDriver.cpp if (EF->LLVMFuzzerInitialize) EF->LLVMFuzzerInitialize(argc, argv); if (EF->__msan_scoped_disable_interceptor_checks) - EF->__msan_scoped_disable_interceptor_checks(); - const Vector<std::string> Args(*argv, *argv + *argc); - assert(!Args.empty()); - ProgName = new std::string(Args[0]); - if (Argv0 != *ProgName) { diff --git a/tools/fuzzing/libfuzzer/patches/11-callback-rv.patch b/tools/fuzzing/libfuzzer/patches/11-callback-rv.patch @@ -1,131 +1,33 @@ -# HG changeset patch -# User Christian Holler <choller@mozilla.com> -# Date 1596126448 -7200 -# Thu Jul 30 18:27:28 2020 +0200 -# Node ID ea198a0331a6db043cb5978512226977514104db -# Parent 8a2a26b33d516c43c366b2f24d731d27d9843349 -[libFuzzer] Change libFuzzer callback contract to allow positive return values +commit 32a07682ac69aa3ef2a2e119335b9a7d4d9e2cd0 +Author: Christian Holler <choller@mozilla.com> +Date: Thu Jul 30 18:27:28 2020 +0200 + + [libFuzzer] Change libFuzzer callback contract to allow positive return values -diff --git a/FuzzerInternal.h b/FuzzerInternal.h ---- a/FuzzerInternal.h -+++ b/FuzzerInternal.h -@@ -60,17 +60,17 @@ public: - - static void StaticAlarmCallback(); - static void StaticCrashSignalCallback(); - static void StaticExitCallback(); - static void StaticInterruptCallback(); - static void StaticFileSizeExceedCallback(); - static void StaticGracefulExitCallback(); - -- void ExecuteCallback(const uint8_t *Data, size_t Size); -+ int ExecuteCallback(const uint8_t *Data, size_t Size); - bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false, - InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr); - - // Merge Corpora[1:] into Corpora[0]. - void Merge(const Vector<std::string> &Corpora); - void CrashResistantMergeInternalStep(const std::string &ControlFilePath); - MutationDispatcher &GetMD() { return MD; } - void PrintFinalStats(); diff --git a/FuzzerLoop.cpp b/FuzzerLoop.cpp +index a93cd16b8793..4339cf2e0dbb 100644 --- a/FuzzerLoop.cpp +++ b/FuzzerLoop.cpp -@@ -463,17 +463,19 @@ static void RenameFeatureSetFile(const s - DirPlusFile(FeaturesDir, NewFile)); - } - - bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, - InputInfo *II, bool *FoundUniqFeatures) { - if (!Size) - return false; - -- ExecuteCallback(Data, Size); -+ if (ExecuteCallback(Data, Size)) { -+ return false; -+ } - - UniqFeatureSetTmp.clear(); - size_t FoundUniqFeaturesOfII = 0; - size_t NumUpdatesBefore = Corpus.NumFeatureUpdates(); - TPC.CollectFeatures([&](size_t Feature) { - if (Corpus.AddFeature(Feature, Size, Options.Shrink)) - UniqFeatureSetTmp.push_back(Feature); - if (Options.Entropic) -@@ -530,48 +532,49 @@ static bool LooseMemeq(const uint8_t *A, - const size_t Limit = 64; - if (Size <= 64) - return !memcmp(A, B, Size); - // Compare first and last Limit/2 bytes. - return !memcmp(A, B, Limit / 2) && - !memcmp(A + Size - Limit / 2, B + Size - Limit / 2, Limit / 2); - } - --void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { -+int Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { - TPC.RecordInitialStack(); - TotalNumberOfRuns++; - assert(InFuzzingThread()); - // We copy the contents of Unit into a separate heap buffer - // so that we reliably find buffer overflows in it. - uint8_t *DataCopy = new uint8_t[Size]; - memcpy(DataCopy, Data, Size); - if (EF->__msan_unpoison) - EF->__msan_unpoison(DataCopy, Size); - if (EF->__msan_unpoison_param) - EF->__msan_unpoison_param(2); - if (CurrentUnitData && CurrentUnitData != Data) - memcpy(CurrentUnitData, Data, Size); - CurrentUnitSize = Size; -+ int Res = 0; - { - ScopedEnableMsanInterceptorChecks S; - AllocTracer.Start(Options.TraceMalloc); - UnitStartTime = system_clock::now(); - TPC.ResetMaps(); - RunningUserCallback = true; -- int Res = CB(DataCopy, Size); -+ Res = CB(DataCopy, Size); +@@ -619,7 +619,6 @@ ATTRIBUTE_NOINLINE bool Fuzzer::ExecuteCallback(const uint8_t *Data, + CBRes = CB(DataCopy, Size); RunningUserCallback = false; UnitStopTime = system_clock::now(); -- (void)Res; -- assert(Res == 0); +- assert(CBRes == 0 || CBRes == -1); HasMoreMallocsThanFrees = AllocTracer.Stop(); } if (!LooseMemeq(DataCopy, Data, Size)) - CrashOnOverwrittenData(); - CurrentUnitSize = 0; - delete[] DataCopy; -+ return Res; - } - - std::string Fuzzer::WriteToOutputCorpus(const Unit &U) { - if (Options.OnlyASCII) - assert(IsASCII(U)); - if (Options.OutputCorpus.empty()) - return ""; - std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U)); diff --git a/FuzzerMerge.cpp b/FuzzerMerge.cpp +index 8c8806e8aafd..69e71135a3e4 100644 --- a/FuzzerMerge.cpp +++ b/FuzzerMerge.cpp -@@ -223,17 +223,19 @@ void Fuzzer::CrashResistantMergeInternal - U.shrink_to_fit(); - } - - // Write the pre-run marker. - OF << "STARTED " << i << " " << U.size() << "\n"; +@@ -236,7 +236,9 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath, OF.flush(); // Flush is important since Command::Execute may crash. // Run. TPC.ResetMaps(); - ExecuteCallback(U.data(), U.size()); -+ if (ExecuteCallback(U.data(), U.size())) { ++ if (!ExecuteCallback(U.data(), U.size())) { + continue; + } // Collect coverage. We are iterating over the files in this order: // * First, files in the initial corpus ordered by size, smallest first. // * Then, all other files, smallest first. - // So it makes no sense to record all features for all files, instead we - // only record features that were not seen before. - Set<size_t> UniqFeatures; - TPC.CollectFeatures([&](size_t Feature) { - if (AllFeatures.insert(Feature).second) diff --git a/tools/fuzzing/libfuzzer/patches/12-custom-mutator-fail.patch b/tools/fuzzing/libfuzzer/patches/12-custom-mutator-fail.patch @@ -1,20 +1,14 @@ -# HG changeset patch -# User Christian Holler <choller@mozilla.com> -# Date 1596126768 -7200 -# Thu Jul 30 18:32:48 2020 +0200 -# Node ID 64e7d096fa77a62b71a306b2c5383b8f75ac4945 -# Parent ea198a0331a6db043cb5978512226977514104db -[libFuzzer] Allow custom mutators to fail +commit bde6e34b41ab68663a1c07a555432ecbd7358a55 +Author: Christian Holler <choller@mozilla.com> +Date: Thu Jul 30 18:32:48 2020 +0200 + + [libFuzzer] Allow custom mutators to fail diff --git a/FuzzerLoop.cpp b/FuzzerLoop.cpp +index 4339cf2e0dbb..263140c99f57 100644 --- a/FuzzerLoop.cpp +++ b/FuzzerLoop.cpp -@@ -690,16 +690,20 @@ void Fuzzer::MutateAndTestOne() { - if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() && - Size <= CurrentMaxMutationLen) - NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size, - II.DataFlowTraceForFocusFunction); - +@@ -754,6 +754,10 @@ void Fuzzer::MutateAndTestOne() { // If MutateWithMask either failed or wasn't called, call default Mutate. if (!NewSize) NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen); @@ -25,17 +19,7 @@ diff --git a/FuzzerLoop.cpp b/FuzzerLoop.cpp assert(NewSize > 0 && "Mutator returned empty unit"); assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit"); Size = NewSize; - II.NumExecutedMutations++; - Corpus.IncrementNumExecutedMutations(); - - bool FoundUniqFeatures = false; - bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II, -@@ -850,17 +854,19 @@ void Fuzzer::Loop(Vector<SizedFile> &Cor - void Fuzzer::MinimizeCrashLoop(const Unit &U) { - if (U.size() <= 1) - return; - while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) { - MD.StartMutationSequence(); +@@ -923,7 +927,9 @@ void Fuzzer::MinimizeCrashLoop(const Unit &U) { memcpy(CurrentUnitData, U.data(), U.size()); for (int i = 0; i < Options.MutateDepth; i++) { size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen); @@ -46,8 +30,3 @@ diff --git a/FuzzerLoop.cpp b/FuzzerLoop.cpp ExecuteCallback(CurrentUnitData, NewSize); PrintPulseAndReportSlowInput(CurrentUnitData, NewSize); TryDetectingAMemoryLeak(CurrentUnitData, NewSize, - /*DuringInitialCorpusExecution*/ false); - } - } - } - diff --git a/tools/fuzzing/libfuzzer/patches/13-unused-write.patch b/tools/fuzzing/libfuzzer/patches/13-unused-write.patch @@ -1,37 +1,14 @@ -# HG changeset patch -# User Christian Holler <choller@mozilla.com> -# Date 1596126946 -7200 -# Thu Jul 30 18:35:46 2020 +0200 -# Node ID 6c779ec81530b6784a714063af66085681ab7318 -# Parent 64e7d096fa77a62b71a306b2c5383b8f75ac4945 -[libFuzzer] Suppress warnings about unused return values +commit ce18e87898f32a5f683d8e5ea9841c735b24619a +Author: Christian Holler <choller@mozilla.com> +Date: Thu Jul 30 18:35:46 2020 +0200 + + [libFuzzer] Suppress warnings about unused return values diff --git a/FuzzerIO.cpp b/FuzzerIO.cpp +index 54cc4ee54be0..8d5b3ae3e1ae 100644 --- a/FuzzerIO.cpp +++ b/FuzzerIO.cpp -@@ -3,16 +3,17 @@ - // 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 - // - //===----------------------------------------------------------------------===// - // IO functions. - //===----------------------------------------------------------------------===// - - #include "FuzzerDefs.h" - #include "FuzzerExtFunctions.h" - #include "FuzzerIO.h" - #include "FuzzerUtil.h" - #include <algorithm> - #include <cstdarg> - #include <fstream> - #include <iterator> -@@ -68,17 +69,17 @@ void WriteToFile(const std::string &Data - WriteToFile(reinterpret_cast<const uint8_t *>(Data.c_str()), Data.size(), - Path); - } - - void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path) { +@@ -81,7 +81,7 @@ void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path) { // Use raw C interface because this function may be called from a sig handler. FILE *Out = fopen(Path.c_str(), "wb"); if (!Out) return; @@ -39,48 +16,3 @@ diff --git a/FuzzerIO.cpp b/FuzzerIO.cpp + (void)fwrite(Data, sizeof(Data[0]), Size, Out); fclose(Out); } - - void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, - long *Epoch, size_t MaxSize, bool ExitOnError) { - long E = Epoch ? *Epoch : 0; - Vector<std::string> Files; - ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true); -diff --git a/FuzzerIOPosix.cpp b/FuzzerIOPosix.cpp ---- a/FuzzerIOPosix.cpp -+++ b/FuzzerIOPosix.cpp -@@ -2,16 +2,17 @@ - // - // 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 - // - //===----------------------------------------------------------------------===// - // IO functions implementation using Posix API. - //===----------------------------------------------------------------------===// - #include "FuzzerPlatform.h" - #if LIBFUZZER_POSIX || LIBFUZZER_FUCHSIA - - #include "FuzzerExtFunctions.h" - #include "FuzzerIO.h" - #include <cstdarg> - #include <cstdio> - #include <dirent.h> -@@ -150,17 +151,17 @@ bool IsInterestingCoverageFile(const std - if (FileName.find("/usr/include/") != std::string::npos) - return false; - if (FileName == "<null>") - return false; - return true; - } - - void RawPrint(const char *Str) { -- write(2, Str, strlen(Str)); -+ (void)write(2, Str, strlen(Str)); - } - - void MkDir(const std::string &Path) { - mkdir(Path.c_str(), 0700); - } - - void RmDir(const std::string &Path) { - rmdir(Path.c_str()); diff --git a/tools/fuzzing/libfuzzer/patches/14-explicit-allocator.patch b/tools/fuzzing/libfuzzer/patches/14-explicit-allocator.patch @@ -1,30 +0,0 @@ -# HG changeset patch -# User Christian Holler <choller@mozilla.com> -# Date 1596126981 -7200 -# Thu Jul 30 18:36:21 2020 +0200 -# Node ID 069dfa3715b1d30905ff0ea1c0f66db88ce146f9 -# Parent 6c779ec81530b6784a714063af66085681ab7318 -[libFuzzer] Make fuzzer_allocator explicit - -diff --git a/FuzzerDefs.h b/FuzzerDefs.h ---- a/FuzzerDefs.h -+++ b/FuzzerDefs.h -@@ -41,17 +41,17 @@ extern ExternalFunctions *EF; - // We are using a custom allocator to give a different symbol name to STL - // containers in order to avoid ODR violations. - template<typename T> - class fuzzer_allocator: public std::allocator<T> { - public: - fuzzer_allocator() = default; - - template<class U> -- fuzzer_allocator(const fuzzer_allocator<U>&) {} -+ explicit fuzzer_allocator(const fuzzer_allocator<U>&) {} - - template<class Other> - struct rebind { typedef fuzzer_allocator<Other> other; }; - }; - - template<typename T> - using Vector = std::vector<T, fuzzer_allocator<T>>; - diff --git a/tools/fuzzing/libfuzzer/patches/15-return-to-exit.patch b/tools/fuzzing/libfuzzer/patches/15-return-to-exit.patch @@ -1,20 +1,20 @@ -commit f80733b3b1b5e05e7dfd7a071f60050fe20108c3 +commit ca00020372841cf2b3ae8774b1d0f8facfef3d1a Author: Jesse Schwartzentruber <truber@mozilla.com> Date: Mon Mar 1 15:47:38 2021 -0500 [libfuzzer] In most cases, return instead of exit(). diff --git a/FuzzerDataFlowTrace.cpp b/FuzzerDataFlowTrace.cpp -index 0e9cdf7e66b1..06ea287a3cfe 100644 +index c9210c78a063..61fdde855539 100644 --- a/FuzzerDataFlowTrace.cpp +++ b/FuzzerDataFlowTrace.cpp -@@ -102,9 +102,11 @@ Vector<double> BlockCoverage::FunctionWeights(size_t NumFunctions) const { +@@ -103,9 +103,11 @@ std::vector<double> BlockCoverage::FunctionWeights(size_t NumFunctions) const { return Res; } -void DataFlowTrace::ReadCoverage(const std::string &DirPath) { +int DataFlowTrace::ReadCoverage(const std::string &DirPath) { - Vector<SizedFile> Files; + std::vector<SizedFile> Files; - GetSizedFilesFromDir(DirPath, &Files); + int Res = GetSizedFilesFromDir(DirPath, &Files); + if (Res != 0) @@ -22,33 +22,33 @@ index 0e9cdf7e66b1..06ea287a3cfe 100644 for (auto &SF : Files) { auto Name = Basename(SF.File); if (Name == kFunctionsTxt) continue; -@@ -112,6 +114,7 @@ void DataFlowTrace::ReadCoverage(const std::string &DirPath) { +@@ -113,6 +115,7 @@ void DataFlowTrace::ReadCoverage(const std::string &DirPath) { std::ifstream IF(SF.File); Coverage.AppendCoverage(IF); } + return 0; } - static void DFTStringAppendToVector(Vector<uint8_t> *DFT, -@@ -157,12 +160,14 @@ static bool ParseDFTLine(const std::string &Line, size_t *FunctionNum, + static void DFTStringAppendToVector(std::vector<uint8_t> *DFT, +@@ -158,12 +161,14 @@ static bool ParseDFTLine(const std::string &Line, size_t *FunctionNum, return true; } -bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, +int DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, - Vector<SizedFile> &CorporaFiles, Random &Rand) { + std::vector<SizedFile> &CorporaFiles, Random &Rand) { - if (DirPath.empty()) return false; + if (DirPath.empty()) return 0; Printf("INFO: DataFlowTrace: reading from '%s'\n", DirPath.c_str()); - Vector<SizedFile> Files; + std::vector<SizedFile> Files; - GetSizedFilesFromDir(DirPath, &Files); + int Res = GetSizedFilesFromDir(DirPath, &Files); + if (Res != 0) + return Res; std::string L; size_t FocusFuncIdx = SIZE_MAX; - Vector<std::string> FunctionNames; -@@ -181,14 +186,16 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, + std::vector<std::string> FunctionNames; +@@ -182,14 +187,16 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, FocusFuncIdx = NumFunctions - 1; } if (!NumFunctions) @@ -65,9 +65,9 @@ index 0e9cdf7e66b1..06ea287a3cfe 100644 + if (Res != 0) + return Res; auto Weights = Coverage.FunctionWeights(NumFunctions); - Vector<double> Intervals(NumFunctions + 1); + std::vector<double> Intervals(NumFunctions + 1); std::iota(Intervals.begin(), Intervals.end(), 0); -@@ -209,7 +216,7 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, +@@ -211,7 +218,7 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, } if (!NumFunctions || FocusFuncIdx == SIZE_MAX || Files.size() <= 1) @@ -76,7 +76,7 @@ index 0e9cdf7e66b1..06ea287a3cfe 100644 // Read traces. size_t NumTraceFiles = 0; -@@ -228,8 +235,10 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, +@@ -230,8 +237,10 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, FunctionNum == FocusFuncIdx) { NumTracesWithFocusFunction++; @@ -89,7 +89,7 @@ index 0e9cdf7e66b1..06ea287a3cfe 100644 Traces[Name] = DFTStringToVector(DFTString); // Print just a few small traces. if (NumTracesWithFocusFunction <= 3 && DFTString.size() <= 16) -@@ -241,7 +250,7 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, +@@ -243,7 +252,7 @@ bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, Printf("INFO: DataFlowTrace: %zd trace files, %zd functions, " "%zd traces with focus function\n", NumTraceFiles, NumFunctions, NumTracesWithFocusFunction); @@ -99,10 +99,10 @@ index 0e9cdf7e66b1..06ea287a3cfe 100644 int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, diff --git a/FuzzerDataFlowTrace.h b/FuzzerDataFlowTrace.h -index d6e3de30a4ef..767bad24f1d0 100644 +index 054dce1bdcb6..4f0e23cfd340 100644 --- a/FuzzerDataFlowTrace.h +++ b/FuzzerDataFlowTrace.h -@@ -113,8 +113,8 @@ class BlockCoverage { +@@ -115,8 +115,8 @@ private: class DataFlowTrace { public: @@ -110,14 +110,14 @@ index d6e3de30a4ef..767bad24f1d0 100644 - bool Init(const std::string &DirPath, std::string *FocusFunction, + int ReadCoverage(const std::string &DirPath); + int Init(const std::string &DirPath, std::string *FocusFunction, - Vector<SizedFile> &CorporaFiles, Random &Rand); + std::vector<SizedFile> &CorporaFiles, Random &Rand); void Clear() { Traces.clear(); } - const Vector<uint8_t> *Get(const std::string &InputSha1) const { + const std::vector<uint8_t> *Get(const std::string &InputSha1) const { diff --git a/FuzzerDriver.cpp b/FuzzerDriver.cpp -index cd720200848b..bedad16efa7b 100644 +index ff43cb031dff..93d576757eab 100644 --- a/FuzzerDriver.cpp +++ b/FuzzerDriver.cpp -@@ -326,7 +326,7 @@ int CleanseCrashInput(const Vector<std::string> &Args, +@@ -365,7 +365,7 @@ int CleanseCrashInput(const std::vector<std::string> &Args, if (Inputs->size() != 1 || !Flags.exact_artifact_path) { Printf("ERROR: -cleanse_crash should be given one input file and" " -exact_artifact_path\n"); @@ -126,7 +126,7 @@ index cd720200848b..bedad16efa7b 100644 } std::string InputFilePath = Inputs->at(0); std::string OutputFilePath = Flags.exact_artifact_path; -@@ -380,7 +380,7 @@ int MinimizeCrashInput(const Vector<std::string> &Args, +@@ -419,7 +419,7 @@ 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"); @@ -135,7 +135,7 @@ index cd720200848b..bedad16efa7b 100644 } std::string InputFilePath = Inputs->at(0); Command BaseCmd(Args); -@@ -411,7 +411,7 @@ int MinimizeCrashInput(const Vector<std::string> &Args, +@@ -450,7 +450,7 @@ int MinimizeCrashInput(const std::vector<std::string> &Args, bool Success = ExecuteCommand(Cmd, &CmdOutput); if (Success) { Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str()); @@ -144,7 +144,7 @@ index cd720200848b..bedad16efa7b 100644 } Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize " "it further\n", -@@ -466,42 +466,51 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { +@@ -505,42 +505,53 @@ int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size()); if (U.size() < 2) { Printf("INFO: The input is small enough, exiting\n"); @@ -156,19 +156,20 @@ index cd720200848b..bedad16efa7b 100644 F->MinimizeCrashLoop(U); Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n"); - exit(0); - return 0; ++ return 0; } --void Merge(Fuzzer *F, FuzzingOptions &Options, const Vector<std::string> &Args, -+int Merge(Fuzzer *F, FuzzingOptions &Options, const Vector<std::string> &Args, - const Vector<std::string> &Corpora, const char *CFPathOrNull) { +-void Merge(Fuzzer *F, FuzzingOptions &Options, ++int Merge(Fuzzer *F, FuzzingOptions &Options, + const std::vector<std::string> &Args, + const std::vector<std::string> &Corpora, const char *CFPathOrNull) { if (Corpora.size() < 2) { Printf("INFO: Merge requires two or more corpus dirs\n"); - exit(0); + return 0; } - Vector<SizedFile> OldCorpus, NewCorpus; + std::vector<SizedFile> OldCorpus, NewCorpus; - GetSizedFilesFromDir(Corpora[0], &OldCorpus); - for (size_t i = 1; i < Corpora.size(); i++) - GetSizedFilesFromDir(Corpora[i], &NewCorpus); @@ -184,16 +185,17 @@ index cd720200848b..bedad16efa7b 100644 std::sort(NewCorpus.begin(), NewCorpus.end()); std::string CFPath = CFPathOrNull ? CFPathOrNull : TempPath("Merge", ".txt"); - Vector<std::string> NewFiles; - Set<uint32_t> NewFeatures, NewCov; + std::vector<std::string> NewFiles; + std::set<uint32_t> NewFeatures, NewCov; - CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures, + Res = CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures, - {}, &NewCov, CFPath, true); + {}, &NewCov, CFPath, true, Flags.set_cover_merge); + if (Res != 0) + return Res; + + 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. @@ -204,39 +206,17 @@ index cd720200848b..bedad16efa7b 100644 + return 0; } - int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict, -@@ -570,10 +579,9 @@ int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict, - return 0; - } - --Vector<std::string> ParseSeedInuts(const char *seed_inputs) { -+int ParseSeedInuts(const char *seed_inputs, Vector<std::string> &Files) { - // Parse -seed_inputs=file1,file2,... or -seed_inputs=@seed_inputs_file -- Vector<std::string> Files; -- if (!seed_inputs) return Files; -+ if (!seed_inputs) return 0; - std::string SeedInputs; - if (Flags.seed_inputs[0] == '@') - SeedInputs = FileToString(Flags.seed_inputs + 1); // File contains list. -@@ -581,7 +589,7 @@ Vector<std::string> ParseSeedInuts(const char *seed_inputs) { + int AnalyzeDictionary(Fuzzer *F, const std::vector<Unit> &Dict, +@@ -620,7 +631,7 @@ std::vector<std::string> ParseSeedInputs(const char *seed_inputs) { SeedInputs = Flags.seed_inputs; // seed_inputs contains the list. if (SeedInputs.empty()) { Printf("seed_inputs is empty or @file does not exist.\n"); - exit(1); -+ return 1; ++ return Files; } // Parse SeedInputs. size_t comma_pos = 0; -@@ -590,7 +598,7 @@ Vector<std::string> ParseSeedInuts(const char *seed_inputs) { - SeedInputs = SeedInputs.substr(0, comma_pos); - } - Files.push_back(SeedInputs); -- return Files; -+ return 0; - } - - static Vector<SizedFile> ReadCorpora(const Vector<std::string> &CorpusDirs, -@@ -624,7 +632,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { +@@ -664,7 +675,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { ProgName = new std::string(Args[0]); if (Argv0 != *ProgName) { Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n"); @@ -245,16 +225,7 @@ index cd720200848b..bedad16efa7b 100644 } ParseFlags(Args, EF); if (Flags.help) { -@@ -723,7 +731,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { - if (!Options.FocusFunction.empty()) { - Printf("ERROR: The parameters `--entropic` and `--focus_function` cannot " - "be used together.\n"); -- exit(1); -+ return 1; - } - Printf("INFO: Running with entropic power schedule (0x%X, %d).\n", - Options.EntropicFeatureFrequencyThreshold, -@@ -809,22 +817,21 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { +@@ -879,24 +890,23 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { "*** executed the target code on a fixed set of inputs.\n" "***\n"); F->PrintFinalStats(); @@ -262,11 +233,12 @@ index cd720200848b..bedad16efa7b 100644 + return 0; } + Options.ForkCorpusGroups = Flags.fork_corpus_groups; if (Flags.fork) - FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork); + return FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork); - if (Flags.merge) + if (Flags.merge || Flags.set_cover_merge) - Merge(F, Options, Args, *Inputs, Flags.merge_control_file); + return Merge(F, Options, Args, *Inputs, Flags.merge_control_file); @@ -275,13 +247,14 @@ index cd720200848b..bedad16efa7b 100644 if (Options.MaxLen == 0) F->SetMaxInputLen(kDefaultMaxMergeLen); assert(Flags.merge_control_file); -- F->CrashResistantMergeInternalStep(Flags.merge_control_file); +- F->CrashResistantMergeInternalStep(Flags.merge_control_file, ++ return F->CrashResistantMergeInternalStep(Flags.merge_control_file, + !strncmp(Flags.merge_inner, "2", 1)); - exit(0); -+ return F->CrashResistantMergeInternalStep(Flags.merge_control_file); } if (Flags.analyze_dict) { -@@ -842,21 +849,31 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { +@@ -914,21 +924,24 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { } if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) { Printf("Dictionary analysis failed\n"); @@ -293,20 +266,12 @@ index cd720200848b..bedad16efa7b 100644 + return 0; } -- auto CorporaFiles = ReadCorpora(*Inputs, ParseSeedInuts(Flags.seed_inputs)); + auto CorporaFiles = ReadCorpora(*Inputs, ParseSeedInputs(Flags.seed_inputs)); - F->Loop(CorporaFiles); -+ { -+ 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; -+ } ++ int 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(), @@ -319,10 +284,10 @@ index cd720200848b..bedad16efa7b 100644 extern "C" ATTRIBUTE_INTERFACE int diff --git a/FuzzerFork.cpp b/FuzzerFork.cpp -index d9e6b79443e0..ee2a99a250c1 100644 +index e544cd846e4d..33900a9b84c4 100644 --- a/FuzzerFork.cpp +++ b/FuzzerFork.cpp -@@ -177,14 +177,16 @@ struct GlobalEnv { +@@ -196,14 +196,16 @@ struct GlobalEnv { return Job; } @@ -331,7 +296,7 @@ index d9e6b79443e0..ee2a99a250c1 100644 auto Stats = ParseFinalStatsFromLog(Job->LogPath); NumRuns += Stats.number_of_executed_units; - Vector<SizedFile> TempFiles, MergeCandidates; + std::vector<SizedFile> TempFiles, MergeCandidates; // Read all newly created inputs and their feature sets. // Choose only those inputs that have new features. - GetSizedFilesFromDir(Job->CorpusDir, &TempFiles); @@ -341,44 +306,45 @@ index d9e6b79443e0..ee2a99a250c1 100644 std::sort(TempFiles.begin(), TempFiles.end()); for (auto &F : TempFiles) { auto FeatureFile = F.File; -@@ -207,12 +209,14 @@ struct GlobalEnv { +@@ -226,7 +228,7 @@ struct GlobalEnv { Stats.average_exec_per_sec, NumOOMs, NumTimeouts, NumCrashes, secondsSinceProcessStartUp(), Job->JobId, Job->DftTimeInSeconds); - if (MergeCandidates.empty()) return; + if (MergeCandidates.empty()) return 0; - Vector<std::string> FilesToAdd; - Set<uint32_t> NewFeatures, NewCov; + std::vector<std::string> FilesToAdd; + std::set<uint32_t> NewFeatures, NewCov; +@@ -235,6 +237,8 @@ struct GlobalEnv { CrashResistantMerge(Args, {}, MergeCandidates, &FilesToAdd, Features, - &NewFeatures, Cov, &NewCov, Job->CFPath, false); + &NewFeatures, Cov, &NewCov, Job->CFPath, false, + IsSetCoverMerge); + if (Fuzzer::isGracefulExitRequested()) + return 0; for (auto &Path : FilesToAdd) { auto U = FileToVector(Path); auto NewPath = DirPlusFile(MainCorpusDir, Hash(U)); -@@ -226,7 +230,7 @@ struct GlobalEnv { +@@ -257,6 +261,7 @@ struct GlobalEnv { if (TPC.PcIsFuncEntry(TE)) PrintPC(" NEW_FUNC: %p %F %L\n", "", TPC.GetNextInstructionPc(TE->PC)); -- + return 0; } - -@@ -280,7 +284,7 @@ void WorkerThread(JobQueue *FuzzQ, JobQueue *MergeQ) { + void CollectDFT(const std::string &InputPath) { +@@ -309,7 +314,7 @@ void WorkerThread(JobQueue *FuzzQ, JobQueue *MergeQ) { } // This is just a skeleton of an experimental -fork=1 feature. -void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, +int FuzzWithFork(Random &Rand, const FuzzingOptions &Options, - const Vector<std::string> &Args, - const Vector<std::string> &CorpusDirs, int NumJobs) { + const std::vector<std::string> &Args, + const std::vector<std::string> &CorpusDirs, int NumJobs) { Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs); -@@ -294,8 +298,12 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, - Env.DataFlowBinary = Options.CollectDataFlow; +@@ -324,8 +329,12 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + Env.Group = Options.ForkCorpusGroups; - Vector<SizedFile> SeedFiles; + std::vector<SizedFile> SeedFiles; - for (auto &Dir : CorpusDirs) - GetSizedFilesFromDir(Dir, &SeedFiles); + int Res; @@ -390,23 +356,28 @@ index d9e6b79443e0..ee2a99a250c1 100644 std::sort(SeedFiles.begin(), SeedFiles.end()); Env.TempDir = TempPath("FuzzWithFork", ".dir"); Env.DFTDir = DirPlusFile(Env.TempDir, "DFT"); -@@ -310,9 +318,14 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, - Env.MainCorpusDir = CorpusDirs[0]; - - auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); -- CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features, -+ Res = CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features, - {}, &Env.Cov, - CFPath, false); -+ if (Res != 0) -+ return Res; +@@ -345,14 +354,19 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + } else { + auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); + std::set<uint32_t> NewFeatures, NewCov; +- CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, Env.Features, ++ Res = CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, Env.Features, + &NewFeatures, Env.Cov, &NewCov, CFPath, + /*Verbose=*/false, /*IsSetCoverMerge=*/false); ++ if (Res != 0) ++ return Res; + Env.Features.insert(NewFeatures.begin(), NewFeatures.end()); + Env.Cov.insert(NewCov.begin(), NewCov.end()); + RemoveFile(CFPath); + } + + if (Fuzzer::isGracefulExitRequested()) + return 0; + - RemoveFile(CFPath); - Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs, - Env.Files.size(), Env.TempDir.c_str()); -@@ -345,9 +358,14 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + if (Env.Group) { + for (auto &path : Env.Files) + Env.FilesSizes.push_back(FileSize(path)); +@@ -391,9 +405,14 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, StopJobs(); break; } @@ -421,9 +392,9 @@ index d9e6b79443e0..ee2a99a250c1 100644 + if (Fuzzer::isGracefulExitRequested()) + return 0; - // Continue if our crash is one of the ignorred ones. - if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode) -@@ -403,7 +421,7 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + // merge the corpus . + JobExecuted++; +@@ -488,7 +507,7 @@ void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, // Use the exit code from the last child process. Printf("INFO: exiting: %d time: %zds\n", ExitCode, Env.secondsSinceProcessStartUp()); @@ -433,7 +404,7 @@ index d9e6b79443e0..ee2a99a250c1 100644 } // namespace fuzzer diff --git a/FuzzerFork.h b/FuzzerFork.h -index b29a43e13fbc..1352171ad49d 100644 +index fc3e9d636cbc..520e40af6f20 100644 --- a/FuzzerFork.h +++ b/FuzzerFork.h @@ -16,7 +16,7 @@ @@ -442,17 +413,17 @@ index b29a43e13fbc..1352171ad49d 100644 namespace fuzzer { -void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, +int FuzzWithFork(Random &Rand, const FuzzingOptions &Options, - const Vector<std::string> &Args, - const Vector<std::string> &CorpusDirs, int NumJobs); + const std::vector<std::string> &Args, + const std::vector<std::string> &CorpusDirs, int NumJobs); } // namespace fuzzer diff --git a/FuzzerIO.cpp b/FuzzerIO.cpp -index 0053ef39f2b9..6be2be67c691 100644 +index 8d5b3ae3e1ae..0c77dba7e992 100644 --- a/FuzzerIO.cpp +++ b/FuzzerIO.cpp -@@ -82,7 +82,9 @@ void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, - long *Epoch, size_t MaxSize, bool ExitOnError) { +@@ -103,7 +103,9 @@ void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V, long *Epoch, + std::vector<std::string> *VPaths) { long E = Epoch ? *Epoch : 0; - Vector<std::string> Files; + std::vector<std::string> Files; - ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true); + int Res = ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true); + if (ExitOnError && Res != 0) @@ -460,13 +431,13 @@ index 0053ef39f2b9..6be2be67c691 100644 size_t NumLoaded = 0; for (size_t i = 0; i < Files.size(); i++) { auto &X = Files[i]; -@@ -97,12 +99,15 @@ void ReadDirToVectorOfUnits(const char *Path, Vector<Unit> *V, +@@ -120,12 +122,15 @@ void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V, long *Epoch, + } } - --void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V) { -+int GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V) { - Vector<std::string> Files; +-void GetSizedFilesFromDir(const std::string &Dir, std::vector<SizedFile> *V) { ++int GetSizedFilesFromDir(const std::string &Dir, std::vector<SizedFile> *V) { + std::vector<std::string> Files; - ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/true); + int Res = ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/true); + if (Res != 0) @@ -479,29 +450,29 @@ index 0053ef39f2b9..6be2be67c691 100644 std::string DirPlusFile(const std::string &DirPath, diff --git a/FuzzerIO.h b/FuzzerIO.h -index 6e4368b971fa..6c90ba637322 100644 +index 874caad1baed..659996e6eb36 100644 --- a/FuzzerIO.h +++ b/FuzzerIO.h -@@ -60,7 +60,7 @@ void RawPrint(const char *Str); - bool IsFile(const std::string &Path); +@@ -70,7 +70,7 @@ bool IsFile(const std::string &Path); + bool IsDirectory(const std::string &Path); size_t FileSize(const std::string &Path); -void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, +int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, - Vector<std::string> *V, bool TopDir); + std::vector<std::string> *V, bool TopDir); - void RmDirRecursive(const std::string &Dir); -@@ -79,7 +79,7 @@ struct SizedFile { + bool MkDirRecursive(const std::string &Dir); +@@ -90,7 +90,7 @@ struct SizedFile { bool operator<(const SizedFile &B) const { return Size < B.Size; } }; --void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V); -+int GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V); +-void GetSizedFilesFromDir(const std::string &Dir, std::vector<SizedFile> *V); ++int GetSizedFilesFromDir(const std::string &Dir, std::vector<SizedFile> *V); char GetSeparator(); - // Similar to the basename utility: returns the file name w/o the dir prefix. + bool IsSeparator(char C); diff --git a/FuzzerIOPosix.cpp b/FuzzerIOPosix.cpp -index 4b453d286c80..1a50295c010f 100644 +index f145dddcbb29..353743ebdb16 100644 --- a/FuzzerIOPosix.cpp +++ b/FuzzerIOPosix.cpp @@ -53,16 +53,16 @@ std::string Basename(const std::string &Path) { @@ -510,7 +481,7 @@ index 4b453d286c80..1a50295c010f 100644 -void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, +int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, - Vector<std::string> *V, bool TopDir) { + std::vector<std::string> *V, bool TopDir) { auto E = GetEpoch(Dir); if (Epoch) - if (E && *Epoch >= E) return; @@ -542,19 +513,18 @@ index 4b453d286c80..1a50295c010f 100644 + return 0; } - + void IterateDirRecursive(const std::string &Dir, diff --git a/FuzzerIOWindows.cpp b/FuzzerIOWindows.cpp -index 651283a551cf..0e977bd02557 100644 +index 6771fc173c91..9cf6062def03 100644 --- a/FuzzerIOWindows.cpp +++ b/FuzzerIOWindows.cpp -@@ -98,11 +98,12 @@ size_t FileSize(const std::string &Path) { +@@ -110,11 +110,11 @@ size_t FileSize(const std::string &Path) { return size.QuadPart; } -void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, +int ListFilesInDirRecursive(const std::string &Dir, long *Epoch, - Vector<std::string> *V, bool TopDir) { -+ int Res; + std::vector<std::string> *V, bool TopDir) { auto E = GetEpoch(Dir); if (Epoch) - if (E && *Epoch >= E) return; @@ -562,7 +532,7 @@ index 651283a551cf..0e977bd02557 100644 std::string Path(Dir); assert(!Path.empty()); -@@ -116,9 +117,9 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, +@@ -128,9 +128,9 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, if (FindHandle == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_FILE_NOT_FOUND) @@ -574,7 +544,7 @@ index 651283a551cf..0e977bd02557 100644 } do { -@@ -131,7 +132,9 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, +@@ -143,7 +143,9 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, FindInfo.cFileName[1] == '.')) continue; @@ -585,57 +555,58 @@ index 651283a551cf..0e977bd02557 100644 } else if (IsFile(FileName, FindInfo.dwFileAttributes)) V->push_back(FileName); -@@ -145,6 +148,7 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, +@@ -157,6 +159,7 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, if (Epoch && TopDir) *Epoch = E; + return 0; } - + void IterateDirRecursive(const std::string &Dir, diff --git a/FuzzerInternal.h b/FuzzerInternal.h -index 1f7d671ed848..cc2650b58ef1 100644 +index 88504705137a..a9d97ef590ee 100644 --- a/FuzzerInternal.h +++ b/FuzzerInternal.h -@@ -35,8 +35,8 @@ public: +@@ -34,8 +34,8 @@ public: Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, - FuzzingOptions Options); - ~Fuzzer(); -- void Loop(Vector<SizedFile> &CorporaFiles); -- void ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles); -+ int Loop(Vector<SizedFile> &CorporaFiles); -+ int ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles); + const FuzzingOptions &Options); + ~Fuzzer() = delete; +- void Loop(std::vector<SizedFile> &CorporaFiles); +- void ReadAndExecuteSeedCorpora(std::vector<SizedFile> &CorporaFiles); ++ int Loop(std::vector<SizedFile> &CorporaFiles); ++ int ReadAndExecuteSeedCorpora(std::vector<SizedFile> &CorporaFiles); void MinimizeCrashLoop(const Unit &U); void RereadOutputCorpus(size_t MaxSize); -@@ -65,13 +65,16 @@ public: +@@ -64,6 +64,9 @@ public: static void StaticFileSizeExceedCallback(); static void StaticGracefulExitCallback(); + static void GracefullyExit(); + static bool isGracefulExitRequested(); + - int ExecuteCallback(const uint8_t *Data, size_t Size); - bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false, - InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr); + // Executes the target callback on {Data, Size} once. + // Returns false if the input was rejected by the target (target returned -1), + // and true otherwise. +@@ -75,7 +78,7 @@ public: // Merge Corpora[1:] into Corpora[0]. - void Merge(const Vector<std::string> &Corpora); -- void CrashResistantMergeInternalStep(const std::string &ControlFilePath); -+ int CrashResistantMergeInternalStep(const std::string &ControlFilePath); + void Merge(const std::vector<std::string> &Corpora); +- void CrashResistantMergeInternalStep(const std::string &ControlFilePath, ++ int CrashResistantMergeInternalStep(const std::string &ControlFilePath, + bool IsSetCoverMerge); MutationDispatcher &GetMD() { return MD; } void PrintFinalStats(); - void SetMaxInputLen(size_t MaxInputLen); -@@ -84,7 +87,7 @@ public: +@@ -89,7 +92,7 @@ public: bool DuringInitialCorpusExecution); void HandleMalloc(size_t Size); - static void MaybeExitGracefully(); + static bool MaybeExitGracefully(); + static int InterruptExitCode(); std::string WriteToOutputCorpus(const Unit &U); - private: -@@ -93,7 +96,7 @@ private: +@@ -99,7 +102,7 @@ private: void ExitCallback(); void CrashOnOverwrittenData(); void InterruptCallback(); @@ -645,10 +616,10 @@ index 1f7d671ed848..cc2650b58ef1 100644 void ReportNewCoverage(InputInfo *II, const Unit &U); void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size); diff --git a/FuzzerLoop.cpp b/FuzzerLoop.cpp -index 4c4e8c271b1f..e7dfc187dbfe 100644 +index 263140c99f57..eff687380f15 100644 --- a/FuzzerLoop.cpp +++ b/FuzzerLoop.cpp -@@ -254,12 +254,20 @@ void Fuzzer::ExitCallback() { +@@ -252,12 +252,20 @@ void Fuzzer::ExitCallback() { _Exit(Options.ErrorExitCode); } @@ -671,8 +642,8 @@ index 4c4e8c271b1f..e7dfc187dbfe 100644 + return F->GracefulExitRequested; } - void Fuzzer::InterruptCallback() { -@@ -663,7 +671,7 @@ void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + int Fuzzer::InterruptExitCode() { +@@ -719,7 +727,7 @@ void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, } } @@ -681,7 +652,7 @@ index 4c4e8c271b1f..e7dfc187dbfe 100644 MD.StartMutationSequence(); auto &II = Corpus.ChooseUnitToMutate(MD.GetRand()); -@@ -685,7 +693,7 @@ void Fuzzer::MutateAndTestOne() { +@@ -744,7 +752,7 @@ void Fuzzer::MutateAndTestOne() { for (int i = 0; i < Options.MutateDepth; i++) { if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) break; @@ -690,7 +661,7 @@ index 4c4e8c271b1f..e7dfc187dbfe 100644 size_t NewSize = 0; if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() && Size <= CurrentMaxMutationLen) -@@ -719,6 +727,7 @@ void Fuzzer::MutateAndTestOne() { +@@ -778,6 +786,7 @@ void Fuzzer::MutateAndTestOne() { } II.NeedsEnergyUpdate = true; @@ -698,27 +669,24 @@ index 4c4e8c271b1f..e7dfc187dbfe 100644 } void Fuzzer::PurgeAllocator() { -@@ -736,7 +745,7 @@ void Fuzzer::PurgeAllocator() { +@@ -795,7 +804,7 @@ void Fuzzer::PurgeAllocator() { LastAllocatorPurgeAttemptTime = system_clock::now(); } --void Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) { -+int Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) { +-void Fuzzer::ReadAndExecuteSeedCorpora(std::vector<SizedFile> &CorporaFiles) { ++int Fuzzer::ReadAndExecuteSeedCorpora(std::vector<SizedFile> &CorporaFiles) { const size_t kMaxSaneLen = 1 << 20; const size_t kMinDefaultLen = 4096; size_t MaxSize = 0; -@@ -795,16 +804,23 @@ void Fuzzer::ReadAndExecuteSeedCorpora(Vector<SizedFile> &CorporaFiles) { - if (Corpus.empty() && Options.MaxNumberOfRuns) { - Printf("ERROR: no interesting inputs were found. " - "Is the code instrumented for coverage? Exiting.\n"); -- exit(1); -+ return 1; +@@ -865,14 +874,21 @@ void Fuzzer::ReadAndExecuteSeedCorpora(std::vector<SizedFile> &CorporaFiles) { + /*TimeOfUnit=*/duration_cast<microseconds>(0s), {0}, DFT, + /*BaseII*/ nullptr); } + return 0; } --void Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) { -+int Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) { +-void Fuzzer::Loop(std::vector<SizedFile> &CorporaFiles) { ++int Fuzzer::Loop(std::vector<SizedFile> &CorporaFiles) { auto FocusFunctionOrAuto = Options.FocusFunction; - DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles, + int Res = DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles, @@ -736,7 +704,7 @@ index 4c4e8c271b1f..e7dfc187dbfe 100644 DFT.Clear(); // No need for DFT any more. TPC.SetPrintNewPCs(Options.PrintNewCovPcs); TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs); -@@ -842,13 +858,15 @@ void Fuzzer::Loop(Vector<SizedFile> &CorporaFiles) { +@@ -910,13 +926,15 @@ void Fuzzer::Loop(std::vector<SizedFile> &CorporaFiles) { } // Perform several mutations and runs. @@ -754,7 +722,7 @@ index 4c4e8c271b1f..e7dfc187dbfe 100644 void Fuzzer::MinimizeCrashLoop(const Unit &U) { diff --git a/FuzzerMerge.cpp b/FuzzerMerge.cpp -index 919eea848580..0a185c7325bb 100644 +index 69e71135a3e4..30ecc796b41d 100644 --- a/FuzzerMerge.cpp +++ b/FuzzerMerge.cpp @@ -28,11 +28,12 @@ bool Merger::Parse(const std::string &Str, bool ParseCoverage) { @@ -772,12 +740,13 @@ index 919eea848580..0a185c7325bb 100644 } // The control file example: -@@ -194,11 +195,13 @@ Set<uint32_t> Merger::AllFeatures() const { +@@ -201,12 +202,14 @@ std::set<uint32_t> Merger::AllFeatures() const { } // Inner process. May crash if the target crashes. --void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { -+int Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { +-void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath, ++int Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath, + bool IsSetCoverMerge) { Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str()); Merger M; std::ifstream IF(CFPath); @@ -788,9 +757,9 @@ index 919eea848580..0a185c7325bb 100644 IF.close(); if (!M.LastFailure.empty()) Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n", -@@ -216,7 +219,8 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { +@@ -224,7 +227,8 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath, }; - Set<const TracePC::PCTableEntry *> AllPCs; + std::set<const TracePC::PCTableEntry *> AllPCs; for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) { - Fuzzer::MaybeExitGracefully(); + if (Fuzzer::MaybeExitGracefully()) @@ -798,24 +767,29 @@ index 919eea848580..0a185c7325bb 100644 auto U = FileToVector(M.Files[i].Name); if (U.size() > MaxInputLen) { U.resize(MaxInputLen); -@@ -261,12 +265,14 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { +@@ -270,6 +274,7 @@ void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath, OF.flush(); } PrintStatsWrapper("DONE "); + return 0; } --static size_t WriteNewControlFile(const std::string &CFPath, -+static int WriteNewControlFile(const std::string &CFPath, - const Vector<SizedFile> &OldCorpus, - const Vector<SizedFile> &NewCorpus, -- const Vector<MergeFileInfo> &KnownFiles) { -+ const Vector<MergeFileInfo> &KnownFiles, -+ size_t &NumFiles) { + // Merges all corpora into the first corpus. A file is added into +@@ -393,11 +398,12 @@ size_t Merger::SetCoverMerge(const std::set<uint32_t> &InitialFeatures, + return NewFeatures->size(); + } + +-static size_t ++static int + WriteNewControlFile(const std::string &CFPath, + const std::vector<SizedFile> &OldCorpus, + const std::vector<SizedFile> &NewCorpus, + const std::vector<MergeFileInfo> &KnownFiles) { ++ size_t &NumFiles) { std::unordered_set<std::string> FilesToSkip; for (auto &SF: KnownFiles) FilesToSkip.insert(SF.Name); -@@ -292,14 +298,15 @@ static size_t WriteNewControlFile(const std::string &CFPath, +@@ -423,14 +429,15 @@ WriteNewControlFile(const std::string &CFPath, if (!ControlFile) { Printf("MERGE-OUTER: failed to write to the control file: %s\n", CFPath.c_str()); @@ -829,23 +803,23 @@ index 919eea848580..0a185c7325bb 100644 } // Outer process. Does not call the target code and thus should not fail. --void CrashResistantMerge(const Vector<std::string> &Args, -+int CrashResistantMerge(const Vector<std::string> &Args, - const Vector<SizedFile> &OldCorpus, - const Vector<SizedFile> &NewCorpus, - Vector<std::string> *NewFiles, -@@ -309,8 +316,9 @@ void CrashResistantMerge(const Vector<std::string> &Args, - Set<uint32_t> *NewCov, - const std::string &CFPath, - bool V /*Verbose*/) { +-void CrashResistantMerge(const std::vector<std::string> &Args, ++int CrashResistantMerge(const std::vector<std::string> &Args, + const std::vector<SizedFile> &OldCorpus, + const std::vector<SizedFile> &NewCorpus, + std::vector<std::string> *NewFiles, +@@ -440,8 +447,9 @@ void CrashResistantMerge(const std::vector<std::string> &Args, + std::set<uint32_t> *NewCov, const std::string &CFPath, + bool V, /*Verbose*/ + bool IsSetCoverMerge) { - if (NewCorpus.empty() && OldCorpus.empty()) return; // Nothing to merge. + if (NewCorpus.empty() && OldCorpus.empty()) return 0; // Nothing to merge. size_t NumAttempts = 0; + int Res; - Vector<MergeFileInfo> KnownFiles; + std::vector<MergeFileInfo> KnownFiles; if (FileSize(CFPath)) { VPrintf(V, "MERGE-OUTER: non-empty control file provided: '%s'\n", -@@ -331,7 +339,8 @@ void CrashResistantMerge(const Vector<std::string> &Args, +@@ -462,7 +470,8 @@ void CrashResistantMerge(const std::vector<std::string> &Args, VPrintf( V, "MERGE-OUTER: nothing to do, merge has been completed before\n"); @@ -855,7 +829,7 @@ index 919eea848580..0a185c7325bb 100644 } // Number of input files likely changed, start merge from scratch, but -@@ -356,7 +365,9 @@ void CrashResistantMerge(const Vector<std::string> &Args, +@@ -487,7 +496,9 @@ void CrashResistantMerge(const std::vector<std::string> &Args, "%zd files, %zd in the initial corpus, %zd processed earlier\n", OldCorpus.size() + NewCorpus.size(), OldCorpus.size(), KnownFiles.size()); @@ -866,7 +840,7 @@ index 919eea848580..0a185c7325bb 100644 } // Execute the inner process until it passes. -@@ -366,7 +377,8 @@ void CrashResistantMerge(const Vector<std::string> &Args, +@@ -498,7 +509,8 @@ void CrashResistantMerge(const std::vector<std::string> &Args, BaseCmd.removeFlag("fork"); BaseCmd.removeFlag("collect_data_flow"); for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) { @@ -876,7 +850,7 @@ index 919eea848580..0a185c7325bb 100644 VPrintf(V, "MERGE-OUTER: attempt %zd\n", Attempt); Command Cmd(BaseCmd); Cmd.addFlag("merge_control_file", CFPath); -@@ -388,7 +400,9 @@ void CrashResistantMerge(const Vector<std::string> &Args, +@@ -522,7 +534,9 @@ void CrashResistantMerge(const std::vector<std::string> &Args, VPrintf(V, "MERGE-OUTER: the control file has %zd bytes\n", (size_t)IF.tellg()); IF.seekg(0, IF.beg); @@ -887,7 +861,7 @@ index 919eea848580..0a185c7325bb 100644 IF.close(); VPrintf(V, "MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n", -@@ -399,6 +413,7 @@ void CrashResistantMerge(const Vector<std::string> &Args, +@@ -536,6 +550,7 @@ void CrashResistantMerge(const std::vector<std::string> &Args, VPrintf(V, "MERGE-OUTER: %zd new files with %zd new features added; " "%zd new coverage edges\n", NewFiles->size(), NewFeatures->size(), NewCov->size()); @@ -896,32 +870,32 @@ index 919eea848580..0a185c7325bb 100644 } // namespace fuzzer diff --git a/FuzzerMerge.h b/FuzzerMerge.h -index e0c6bc539bdb..6dc1c4c45abf 100644 +index 42f798e1da18..1b5f6f69b0dd 100644 --- a/FuzzerMerge.h +++ b/FuzzerMerge.h -@@ -63,7 +63,7 @@ struct Merger { +@@ -64,7 +64,7 @@ struct Merger { bool Parse(std::istream &IS, bool ParseCoverage); bool Parse(const std::string &Str, bool ParseCoverage); - void ParseOrExit(std::istream &IS, bool ParseCoverage); + int ParseOrExit(std::istream &IS, bool ParseCoverage); - size_t Merge(const Set<uint32_t> &InitialFeatures, Set<uint32_t> *NewFeatures, - const Set<uint32_t> &InitialCov, Set<uint32_t> *NewCov, - Vector<std::string> *NewFiles); -@@ -71,7 +71,7 @@ struct Merger { - Set<uint32_t> AllFeatures() const; + size_t Merge(const std::set<uint32_t> &InitialFeatures, + std::set<uint32_t> *NewFeatures, + const std::set<uint32_t> &InitialCov, std::set<uint32_t> *NewCov, +@@ -78,7 +78,7 @@ struct Merger { + std::set<uint32_t> AllFeatures() const; }; --void CrashResistantMerge(const Vector<std::string> &Args, -+int CrashResistantMerge(const Vector<std::string> &Args, - const Vector<SizedFile> &OldCorpus, - const Vector<SizedFile> &NewCorpus, - Vector<std::string> *NewFiles, +-void CrashResistantMerge(const std::vector<std::string> &Args, ++int CrashResistantMerge(const std::vector<std::string> &Args, + const std::vector<SizedFile> &OldCorpus, + const std::vector<SizedFile> &NewCorpus, + std::vector<std::string> *NewFiles, diff --git a/FuzzerTracePC.cpp b/FuzzerTracePC.cpp -index b2ca7693e540..fbceda39bc22 100644 +index 7bd1a0870c59..d083e725a26b 100644 --- a/FuzzerTracePC.cpp +++ b/FuzzerTracePC.cpp -@@ -238,13 +238,13 @@ void TracePC::IterateCoveredFunctions(CallBack CB) { +@@ -251,13 +251,13 @@ void TracePC::IterateCoveredFunctions(CallBack CB) { } } @@ -937,7 +911,7 @@ index b2ca7693e540..fbceda39bc22 100644 for (size_t M = 0; M < NumModules; M++) { auto &PCTE = ModulePCTable[M]; size_t N = PCTE.Stop - PCTE.Start; -@@ -256,13 +256,13 @@ void TracePC::SetFocusFunction(const std::string &FuncName) { +@@ -269,13 +269,13 @@ void TracePC::SetFocusFunction(const std::string &FuncName) { if (FuncName != Name) continue; Printf("INFO: Focus function is set to '%s'\n", Name.c_str()); FocusFunctionCounterPtr = Modules[M].Start() + I; @@ -954,7 +928,7 @@ index b2ca7693e540..fbceda39bc22 100644 bool TracePC::ObservedFocusFunction() { diff --git a/FuzzerTracePC.h b/FuzzerTracePC.h -index 501f3b544971..b46ebb909dbf 100644 +index af1f9d81e950..507ea6a4abfc 100644 --- a/FuzzerTracePC.h +++ b/FuzzerTracePC.h @@ -116,7 +116,7 @@ class TracePC { diff --git a/tools/fuzzing/moz.build b/tools/fuzzing/moz.build @@ -6,6 +6,9 @@ DIRS += [ "interface", + # Include the libfuzzer directory to all fuzzing builds so that + # FuzzedDataProvider is accessible to AFL++ builds + "libfuzzer", "registry", ] @@ -26,8 +29,3 @@ if not CONFIG["JS_STANDALONE"]: DIRS += [ "rust", ] - -if CONFIG["LIBFUZZER"]: - DIRS += [ - "libfuzzer", - ]