aboutsummaryrefslogtreecommitdiff
path: root/src/test/fuzz
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/fuzz')
-rw-r--r--src/test/fuzz/FuzzedDataProvider.h556
-rw-r--r--src/test/fuzz/fuzz.h2
-rw-r--r--src/test/fuzz/net.cpp21
-rw-r--r--src/test/fuzz/node_eviction.cpp44
-rw-r--r--src/test/fuzz/process_message.cpp4
-rw-r--r--src/test/fuzz/process_messages.cpp4
-rw-r--r--src/test/fuzz/script.cpp8
-rw-r--r--src/test/fuzz/util.h12
8 files changed, 386 insertions, 265 deletions
diff --git a/src/test/fuzz/FuzzedDataProvider.h b/src/test/fuzz/FuzzedDataProvider.h
index 3e069eba69..83bcd0134a 100644
--- a/src/test/fuzz/FuzzedDataProvider.h
+++ b/src/test/fuzz/FuzzedDataProvider.h
@@ -34,272 +34,354 @@ class FuzzedDataProvider {
: data_ptr_(data), remaining_bytes_(size) {}
~FuzzedDataProvider() = default;
- // Returns a std::vector containing |num_bytes| of input data. If fewer than
- // |num_bytes| of data remain, returns a shorter std::vector containing all
- // of the data that's left. Can be used with any byte sized type, such as
- // char, unsigned char, uint8_t, etc.
- template <typename T> std::vector<T> ConsumeBytes(size_t num_bytes) {
- num_bytes = std::min(num_bytes, remaining_bytes_);
- return ConsumeBytes<T>(num_bytes, num_bytes);
- }
-
- // Similar to |ConsumeBytes|, but also appends the terminator value at the end
- // of the resulting vector. Useful, when a mutable null-terminated C-string is
- // needed, for example. But that is a rare case. Better avoid it, if possible,
- // and prefer using |ConsumeBytes| or |ConsumeBytesAsString| methods.
+ // 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) {
- num_bytes = std::min(num_bytes, remaining_bytes_);
- std::vector<T> result = ConsumeBytes<T>(num_bytes + 1, num_bytes);
- result.back() = terminator;
- return result;
- }
-
- // Returns a std::string containing |num_bytes| of input data. Using this and
- // |.c_str()| on the resulting string is the best way to get an immutable
- // null-terminated C string. If fewer than |num_bytes| of data remain, returns
- // a shorter std::string containing all of the data that's left.
- std::string ConsumeBytesAsString(size_t num_bytes) {
- static_assert(sizeof(std::string::value_type) == sizeof(uint8_t),
- "ConsumeBytesAsString cannot convert the data to a string.");
-
- num_bytes = std::min(num_bytes, remaining_bytes_);
- std::string result(
- reinterpret_cast<const std::string::value_type *>(data_ptr_),
- num_bytes);
- Advance(num_bytes);
- return result;
- }
+ std::vector<T> ConsumeBytesWithTerminator(size_t num_bytes, T terminator = 0);
+ template <typename T> std::vector<T> ConsumeRemainingBytes();
- // Returns a number in the range [min, max] by consuming bytes from the
- // input data. The value might not be uniformly distributed in the given
- // range. If there's no input data left, always returns |min|. |min| must
- // be less than or equal to |max|.
- template <typename T> T ConsumeIntegralInRange(T min, T max) {
- static_assert(std::is_integral<T>::value, "An integral type is required.");
- static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type.");
+ // 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();
- if (min > max)
- abort();
+ // Methods returning integer values.
+ template <typename T> T ConsumeIntegral();
+ template <typename T> T ConsumeIntegralInRange(T min, T max);
- // Use the biggest type possible to hold the range and the result.
- uint64_t range = static_cast<uint64_t>(max) - min;
- uint64_t result = 0;
- size_t offset = 0;
-
- while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 &&
- remaining_bytes_ != 0) {
- // Pull bytes off the end of the seed data. Experimentally, this seems to
- // allow the fuzzer to more easily explore the input space. This makes
- // sense, since it works by modifying inputs that caused new code to run,
- // and this data is often used to encode length of data read by
- // |ConsumeBytes|. Separating out read lengths makes it easier modify the
- // contents of the data that is actually read.
- --remaining_bytes_;
- result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_];
- offset += CHAR_BIT;
- }
+ // Methods returning floating point values.
+ template <typename T> T ConsumeFloatingPoint();
+ template <typename T> T ConsumeFloatingPointInRange(T min, T max);
- // Avoid division by 0, in case |range + 1| results in overflow.
- if (range != std::numeric_limits<decltype(range)>::max())
- result = result % (range + 1);
+ // 0 <= return value <= 1.
+ template <typename T> T ConsumeProbability();
- return static_cast<T>(min + result);
- }
+ bool ConsumeBool();
- // Returns a std::string of length from 0 to |max_length|. When it runs out of
- // input data, returns what remains of the input. Designed to be more stable
- // with respect to a fuzzer inserting characters than just picking a random
- // length and then consuming that many bytes with |ConsumeBytes|.
- std::string ConsumeRandomLengthString(size_t max_length) {
- // Reads bytes from the start of |data_ptr_|. Maps "\\" to "\", and maps "\"
- // followed by anything else to the end of the string. As a result of this
- // logic, a fuzzer can insert characters into the string, and the string
- // will be lengthened to include those new characters, resulting in a more
- // stable fuzzer than picking the length of a string independently from
- // picking its contents.
- std::string result;
-
- // Reserve the anticipated capaticity to prevent several reallocations.
- result.reserve(std::min(max_length, remaining_bytes_));
- for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) {
- char next = ConvertUnsignedToSigned<char>(data_ptr_[0]);
- Advance(1);
- if (next == '\\' && remaining_bytes_ != 0) {
- next = ConvertUnsignedToSigned<char>(data_ptr_[0]);
- Advance(1);
- if (next != '\\')
- break;
- }
- result += next;
- }
+ // Returns a value chosen from the given enum.
+ template <typename T> T ConsumeEnum();
- result.shrink_to_fit();
- return result;
- }
+ // Returns a value from the given array.
+ template <typename T, size_t size> T PickValueInArray(const T (&array)[size]);
+ template <typename T> T PickValueInArray(std::initializer_list<const T> list);
- // Returns a std::vector containing all remaining bytes of the input data.
- template <typename T> std::vector<T> ConsumeRemainingBytes() {
- return ConsumeBytes<T>(remaining_bytes_);
- }
+ // Writes data to the given destination and returns number of bytes written.
+ size_t ConsumeData(void *destination, size_t num_bytes);
- // Returns a std::string containing all remaining bytes of the input data.
- // Prefer using |ConsumeRemainingBytes| unless you actually need a std::string
- // object.
- std::string ConsumeRemainingBytesAsString() {
- return ConsumeBytesAsString(remaining_bytes_);
- }
+ // Reports the remaining bytes available for fuzzed input.
+ size_t remaining_bytes() { return remaining_bytes_; }
- // Returns a number in the range [Type's min, Type's max]. The value might
- // not be uniformly distributed in the given range. If there's no input data
- // left, always returns |min|.
- template <typename T> T ConsumeIntegral() {
- return ConsumeIntegralInRange(std::numeric_limits<T>::min(),
- std::numeric_limits<T>::max());
- }
+ private:
+ FuzzedDataProvider(const FuzzedDataProvider &) = delete;
+ FuzzedDataProvider &operator=(const FuzzedDataProvider &) = delete;
- // Reads one byte and returns a bool, or false when no data remains.
- bool ConsumeBool() { return 1 & ConsumeIntegral<uint8_t>(); }
+ void CopyAndAdvance(void *destination, size_t num_bytes);
- // Returns a copy of the value selected from the given fixed-size |array|.
- template <typename T, size_t size>
- T PickValueInArray(const T (&array)[size]) {
- static_assert(size > 0, "The array must be non empty.");
- return array[ConsumeIntegralInRange<size_t>(0, size - 1)];
- }
+ void Advance(size_t num_bytes);
template <typename T>
- T PickValueInArray(std::initializer_list<const T> list) {
- // TODO(Dor1s): switch to static_assert once C++14 is allowed.
- if (!list.size())
- abort();
-
- return *(list.begin() + ConsumeIntegralInRange<size_t>(0, list.size() - 1));
- }
-
- // 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 ConsumeEnum() {
- static_assert(std::is_enum<T>::value, "|T| must be an enum type.");
- return static_cast<T>(ConsumeIntegralInRange<uint32_t>(
- 0, static_cast<uint32_t>(T::kMaxValue)));
- }
+ std::vector<T> ConsumeBytes(size_t size, size_t num_bytes);
- // 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 ConsumeProbability() {
- static_assert(std::is_floating_point<T>::value,
- "A floating point type is required.");
+ template <typename TS, typename TU> TS ConvertUnsignedToSigned(TU value);
- // Use different integral types for different floating point types in order
- // to provide better density of the resulting values.
- using IntegralType =
- typename std::conditional<(sizeof(T) <= sizeof(uint32_t)), uint32_t,
- uint64_t>::type;
+ const uint8_t *data_ptr_;
+ size_t remaining_bytes_;
+};
- T result = static_cast<T>(ConsumeIntegral<IntegralType>());
- result /= static_cast<T>(std::numeric_limits<IntegralType>::max());
- return result;
+// 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 capaticity to prevent several reallocations.
+ result.reserve(std::min(max_length, remaining_bytes_));
+ for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) {
+ char next = ConvertUnsignedToSigned<char>(data_ptr_[0]);
+ Advance(1);
+ if (next == '\\' && remaining_bytes_ != 0) {
+ next = ConvertUnsignedToSigned<char>(data_ptr_[0]);
+ Advance(1);
+ if (next != '\\')
+ break;
+ }
+ result += next;
}
- // 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 ConsumeFloatingPoint() {
- return ConsumeFloatingPointInRange<T>(std::numeric_limits<T>::lowest(),
- std::numeric_limits<T>::max());
+ 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<T>::value, "An integral type is required.");
+ static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type.");
+
+ if (min > max)
+ abort();
+
+ // Use the biggest type possible to hold the range and the result.
+ uint64_t range = static_cast<uint64_t>(max) - min;
+ uint64_t result = 0;
+ size_t offset = 0;
+
+ while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 &&
+ remaining_bytes_ != 0) {
+ // Pull bytes off the end of the seed data. Experimentally, this seems to
+ // allow the fuzzer to more easily explore the input space. This makes
+ // sense, since it works by modifying inputs that caused new code to run,
+ // and this data is often used to encode length of data read by
+ // |ConsumeBytes|. Separating out read lengths makes it easier modify the
+ // contents of the data that is actually read.
+ --remaining_bytes_;
+ result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_];
+ offset += CHAR_BIT;
}
- // 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 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;
+ // Avoid division by 0, in case |range + 1| results in overflow.
+ if (range != std::numeric_limits<decltype(range)>::max())
+ result = result % (range + 1);
+
+ return static_cast<T>(min + result);
+}
+
+// Returns a 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;
}
-
- return result + range * ConsumeProbability<T>();
+ } else {
+ range = max - min;
}
- // Reports the remaining bytes available for fuzzed input.
- size_t remaining_bytes() { return remaining_bytes_; }
-
- private:
- FuzzedDataProvider(const FuzzedDataProvider &) = delete;
- FuzzedDataProvider &operator=(const FuzzedDataProvider &) = delete;
-
- void Advance(size_t num_bytes) {
- if (num_bytes > remaining_bytes_)
+ 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<T>::value,
+ "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<(sizeof(T) <= sizeof(uint32_t)), uint32_t,
+ uint64_t>::type;
+
+ 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<T>::value, "|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>
+T FuzzedDataProvider::PickValueInArray(std::initializer_list<const T> list) {
+ // TODO(Dor1s): switch to static_assert once C++14 is allowed.
+ 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();
-
- data_ptr_ += num_bytes;
- remaining_bytes_ -= num_bytes;
- }
-
- template <typename T>
- std::vector<T> ConsumeBytes(size_t size, size_t num_bytes_to_consume) {
- static_assert(sizeof(T) == sizeof(uint8_t), "Incompatible data type.");
-
- // The point of using the size-based constructor below is to increase the
- // odds of having a vector object with capacity being equal to the length.
- // That part is always implementation specific, but at least both libc++ and
- // libstdc++ allocate the requested number of bytes in that constructor,
- // which seems to be a natural choice for other implementations as well.
- // To increase the odds even more, we also call |shrink_to_fit| below.
- std::vector<T> result(size);
- if (size == 0) {
- if (num_bytes_to_consume != 0)
- abort();
- return result;
- }
-
- std::memcpy(result.data(), data_ptr_, num_bytes_to_consume);
- Advance(num_bytes_to_consume);
-
- // Even though |shrink_to_fit| is also implementation specific, we expect it
- // to provide an additional assurance in case vector's constructor allocated
- // a buffer which is larger than the actual amount of data we put inside it.
- result.shrink_to_fit();
return result;
}
- template <typename TS, typename TU> TS ConvertUnsignedToSigned(TU value) {
- static_assert(sizeof(TS) == sizeof(TU), "Incompatible data types.");
- static_assert(!std::numeric_limits<TU>::is_signed,
- "Source type must be unsigned.");
-
- // TODO(Dor1s): change to `if constexpr` once C++17 becomes mainstream.
- if (std::numeric_limits<TS>::is_modulo)
- return static_cast<TS>(value);
-
- // Avoid using implementation-defined unsigned to signer conversions.
- // To learn more, see https://stackoverflow.com/questions/13150449.
- if (value <= std::numeric_limits<TS>::max()) {
- return static_cast<TS>(value);
- } else {
- constexpr auto TS_min = std::numeric_limits<TS>::min();
- return TS_min + static_cast<char>(value - TS_min);
- }
+ 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<char>(value - TS_min);
}
-
- const uint8_t *data_ptr_;
- size_t remaining_bytes_;
-};
+}
#endif // LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_
diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h
index 544379c0b0..73edd8220e 100644
--- a/src/test/fuzz/fuzz.h
+++ b/src/test/fuzz/fuzz.h
@@ -15,7 +15,7 @@ using TypeInitialize = std::function<void()>;
void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init);
-void FuzzFrameworkEmptyFun() {}
+inline void FuzzFrameworkEmptyFun() {}
#define FUZZ_TARGET(name) \
FUZZ_TARGET_INIT(name, FuzzFrameworkEmptyFun)
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index 66d7c512e4..8c5f18e6b6 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -28,26 +28,7 @@ FUZZ_TARGET_INIT(net, initialize_net)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider));
- const std::optional<CAddress> address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
- if (!address) {
- return;
- }
- const std::optional<CAddress> address_bind = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
- if (!address_bind) {
- return;
- }
-
- CNode node{fuzzed_data_provider.ConsumeIntegral<NodeId>(),
- static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()),
- fuzzed_data_provider.ConsumeIntegral<int>(),
- INVALID_SOCKET,
- *address,
- fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
- fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
- *address_bind,
- fuzzed_data_provider.ConsumeRandomLengthString(32),
- fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH}),
- fuzzed_data_provider.ConsumeBool()};
+ CNode node{ConsumeNode(fuzzed_data_provider)};
node.SetCommonVersion(fuzzed_data_provider.ConsumeIntegral<int>());
while (fuzzed_data_provider.ConsumeBool()) {
switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 10)) {
diff --git a/src/test/fuzz/node_eviction.cpp b/src/test/fuzz/node_eviction.cpp
new file mode 100644
index 0000000000..aaebe83c0a
--- /dev/null
+++ b/src/test/fuzz/node_eviction.cpp
@@ -0,0 +1,44 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <net.h>
+#include <optional.h>
+#include <protocol.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <optional>
+#include <vector>
+
+FUZZ_TARGET(node_eviction)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ std::vector<NodeEvictionCandidate> eviction_candidates;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ eviction_candidates.push_back({
+ fuzzed_data_provider.ConsumeIntegral<NodeId>(),
+ fuzzed_data_provider.ConsumeIntegral<int64_t>(),
+ fuzzed_data_provider.ConsumeIntegral<int64_t>(),
+ fuzzed_data_provider.ConsumeIntegral<int64_t>(),
+ fuzzed_data_provider.ConsumeIntegral<int64_t>(),
+ fuzzed_data_provider.ConsumeBool(),
+ fuzzed_data_provider.ConsumeBool(),
+ fuzzed_data_provider.ConsumeBool(),
+ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
+ fuzzed_data_provider.ConsumeBool(),
+ fuzzed_data_provider.ConsumeBool(),
+ });
+ }
+ // Make a copy since eviction_candidates may be in some valid but otherwise
+ // indeterminate state after the SelectNodeToEvict(&&) call.
+ const std::vector<NodeEvictionCandidate> eviction_candidates_copy = eviction_candidates;
+ const Optional<NodeId> node_to_evict = SelectNodeToEvict(std::move(eviction_candidates));
+ if (node_to_evict) {
+ assert(std::any_of(eviction_candidates_copy.begin(), eviction_candidates_copy.end(), [&node_to_evict](const NodeEvictionCandidate& eviction_candidate) { return *node_to_evict == eviction_candidate.id; }));
+ }
+}
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index 01de8bdbb5..8a6404f210 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -75,6 +75,10 @@ void fuzz_target(const std::vector<uint8_t>& buffer, const std::string& LIMIT_TO
GetTime<std::chrono::microseconds>(), std::atomic<bool>{false});
} catch (const std::ios_base::failure&) {
}
+ {
+ LOCK(p2p_node.cs_sendProcessing);
+ g_setup->m_node.peerman->SendMessages(&p2p_node);
+ }
SyncWithValidationInterfaceQueue();
LOCK2(::cs_main, g_cs_orphans); // See init.cpp for rationale for implicit locking order requirement
g_setup->m_node.connman->StopNodes();
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index e12e780a18..9012c1ba39 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -79,6 +79,10 @@ FUZZ_TARGET_INIT(process_messages, initialize_process_messages)
connman.ProcessMessagesOnce(random_node);
} catch (const std::ios_base::failure&) {
}
+ {
+ LOCK(random_node.cs_sendProcessing);
+ g_setup->m_node.peerman->SendMessages(&random_node);
+ }
}
SyncWithValidationInterfaceQueue();
LOCK2(::cs_main, g_cs_orphans); // See init.cpp for rationale for implicit locking order requirement
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index 892af655f6..f43689290a 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -72,6 +72,13 @@ FUZZ_TARGET_INIT(script, initialize_script)
TxoutType which_type;
(void)IsStandard(script, which_type);
+ if (which_type == TxoutType::NULL_DATA) {
+ assert(script.IsUnspendable());
+ }
+ if (script.IsUnspendable()) {
+ assert(which_type == TxoutType::NULL_DATA ||
+ which_type == TxoutType::NONSTANDARD);
+ }
(void)RecursiveDynamicUsage(script);
@@ -82,7 +89,6 @@ FUZZ_TARGET_INIT(script, initialize_script)
(void)script.IsPayToScriptHash();
(void)script.IsPayToWitnessScriptHash();
(void)script.IsPushOnly();
- (void)script.IsUnspendable();
(void)script.GetSigOpCount(/* fAccurate= */ false);
(void)FormatScript(script);
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index cf666a8b93..509864568d 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -249,7 +249,7 @@ template <class T>
return result;
}
-CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
+inline CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION});
CNetAddr net_addr;
@@ -271,22 +271,22 @@ CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
return net_addr;
}
-CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
+inline CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
}
-CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcept
+inline CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint16_t>()};
}
-CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept
+inline CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
return {ConsumeService(fuzzed_data_provider), static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()), fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
}
-CNode ConsumeNode(FuzzedDataProvider& fuzzed_data_provider) noexcept
+inline CNode ConsumeNode(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
const NodeId node_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
const ServiceFlags local_services = static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
@@ -302,7 +302,7 @@ CNode ConsumeNode(FuzzedDataProvider& fuzzed_data_provider) noexcept
return {node_id, local_services, my_starting_height, socket, address, keyed_net_group, local_host_nonce, addr_bind, addr_name, conn_type, inbound_onion};
}
-void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST)
+inline void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST)
{
static const BasicTestingSetup basic_testing_setup{chain_name, {"-nodebuglogfile"}};
}