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:
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",
- ]