Index: compiler-rt/trunk/include/CMakeLists.txt =================================================================== --- compiler-rt/trunk/include/CMakeLists.txt +++ compiler-rt/trunk/include/CMakeLists.txt @@ -14,6 +14,9 @@ sanitizer/tsan_interface.h sanitizer/tsan_interface_atomic.h ) + set(FUZZER_HEADERS + fuzzer/FuzzedDataProvider.h + ) endif(COMPILER_RT_BUILD_SANITIZERS) if (COMPILER_RT_BUILD_XRAY) @@ -26,6 +29,7 @@ set(COMPILER_RT_HEADERS ${SANITIZER_HEADERS} + ${FUZZER_HEADERS} ${XRAY_HEADERS}) set(output_dir ${COMPILER_RT_OUTPUT_DIR}/include) @@ -51,6 +55,11 @@ COMPONENT compiler-rt-headers PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ DESTINATION ${COMPILER_RT_INSTALL_PATH}/include/sanitizer) +# Install fuzzer headers. +install(FILES ${FUZZER_HEADERS} + COMPONENT compiler-rt-headers + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + DESTINATION ${COMPILER_RT_INSTALL_PATH}/include/fuzzer) # Install xray headers. install(FILES ${XRAY_HEADERS} COMPONENT compiler-rt-headers Index: compiler-rt/trunk/include/fuzzer/FuzzedDataProvider.h =================================================================== --- compiler-rt/trunk/include/fuzzer/FuzzedDataProvider.h +++ compiler-rt/trunk/include/fuzzer/FuzzedDataProvider.h @@ -0,0 +1,245 @@ +//===- 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +class FuzzedDataProvider { + public: + // |data| is an array of length |size| that the FuzzedDataProvider wraps to + // provide more granular access. |data| must outlive the FuzzedDataProvider. + FuzzedDataProvider(const uint8_t *data, size_t size) + : data_ptr_(data), remaining_bytes_(size) {} + ~FuzzedDataProvider() = default; + + // Returns a std::vector containing |num_bytes| of input data. If fewer than + // |num_bytes| of data remain, returns a shorter std::vector containing all + // of the data that's left. Can be used with any byte sized type, such as + // char, unsigned char, uint8_t, etc. + template std::vector ConsumeBytes(size_t num_bytes) { + num_bytes = std::min(num_bytes, remaining_bytes_); + return ConsumeBytes(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 + std::vector ConsumeBytesWithTerminator(size_t num_bytes, + T terminator = 0) { + num_bytes = std::min(num_bytes, remaining_bytes_); + std::vector result = ConsumeBytes(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(data_ptr_), + num_bytes); + Advance(num_bytes); + return result; + } + + // Returns a number in the range [min, max] by consuming bytes from the + // input data. The value might not be uniformly distributed in the given + // range. If there's no input data left, always returns |min|. |min| must + // be less than or equal to |max|. + template T ConsumeIntegralInRange(T min, T max) { + static_assert(std::is_integral::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(max) - min; + uint64_t result = 0; + size_t offset = 0; + + while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 && + remaining_bytes_ != 0) { + // Pull bytes off the end of the seed data. Experimentally, this seems to + // allow the fuzzer to more easily explore the input space. This makes + // sense, since it works by modifying inputs that caused new code to run, + // and this data is often used to encode length of data read by + // |ConsumeBytes|. Separating out read lengths makes it easier modify the + // contents of the data that is actually read. + --remaining_bytes_; + result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_]; + offset += CHAR_BIT; + } + + // Avoid division by 0, in case |range + 1| results in overflow. + if (range != std::numeric_limits::max()) + result = result % (range + 1); + + return static_cast(min + result); + } + + // Returns a std::string of length from 0 to |max_length|. When it runs out of + // input data, returns what remains of the input. Designed to be more stable + // with respect to a fuzzer inserting characters than just picking a random + // length and then consuming that many bytes with |ConsumeBytes|. + std::string ConsumeRandomLengthString(size_t max_length) { + // Reads bytes from the start of |data_ptr_|. Maps "\\" to "\", and maps "\" + // followed by anything else to the end of the string. As a result of this + // logic, a fuzzer can insert characters into the string, and the string + // will be lengthened to include those new characters, resulting in a more + // stable fuzzer than picking the length of a string independently from + // picking its contents. + std::string result; + + // Reserve the anticipated capaticity to prevent several reallocations. + result.reserve(std::min(max_length, remaining_bytes_)); + for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) { + char next = ConvertUnsignedToSigned(data_ptr_[0]); + Advance(1); + if (next == '\\' && remaining_bytes_ != 0) { + next = ConvertUnsignedToSigned(data_ptr_[0]); + Advance(1); + if (next != '\\') + break; + } + result += next; + } + + result.shrink_to_fit(); + return result; + } + + // Returns a std::vector containing all remaining bytes of the input data. + template std::vector ConsumeRemainingBytes() { + return ConsumeBytes(remaining_bytes_); + } + + // Prefer using |ConsumeRemainingBytes| unless you actually need a std::string + // object. + // Returns a std::vector containing all remaining bytes of the input data. + std::string ConsumeRemainingBytesAsString() { + return ConsumeBytesAsString(remaining_bytes_); + } + + // Returns a number in the range [Type's min, Type's max]. The value might + // not be uniformly distributed in the given range. If there's no input data + // left, always returns |min|. + template T ConsumeIntegral() { + return ConsumeIntegralInRange(std::numeric_limits::min(), + std::numeric_limits::max()); + } + + // Reads one byte and returns a bool, or false when no data remains. + bool ConsumeBool() { return 1 & ConsumeIntegral(); } + + // Returns a copy of a value selected from a fixed-size |array|. + template + T PickValueInArray(const T (&array)[size]) { + static_assert(size > 0, "The array must be non empty."); + return array[ConsumeIntegralInRange(0, size - 1)]; + } + + template + T PickValueInArray(std::initializer_list list) { + // static_assert(list.size() > 0, "The array must be non empty."); + return *(list.begin() + ConsumeIntegralInRange(0, list.size() - 1)); + } + + // Return an enum value. The enum must start at 0 and be contiguous. It must + // also contain |kMaxValue| aliased to its largest (inclusive) value. Such as: + // enum class Foo { SomeValue, OtherValue, kMaxValue = OtherValue }; + template T ConsumeEnum() { + static_assert(std::is_enum::value, "|T| must be an enum type."); + return static_cast(ConsumeIntegralInRange( + 0, static_cast(T::kMaxValue))); + } + + // Reports the remaining bytes available for fuzzed input. + size_t remaining_bytes() { return remaining_bytes_; } + + private: + FuzzedDataProvider(const FuzzedDataProvider &) = delete; + FuzzedDataProvider &operator=(const FuzzedDataProvider &) = delete; + + void Advance(size_t num_bytes) { + if (num_bytes > remaining_bytes_) + abort(); + + data_ptr_ += num_bytes; + remaining_bytes_ -= num_bytes; + } + + template + std::vector 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 result(size); + std::memcpy(result.data(), data_ptr_, num_bytes_to_consume); + Advance(num_bytes_to_consume); + + // Even though |shrink_to_fit| is also implementation specific, we expect it + // to provide an additional assurance in case vector's constructor allocated + // a buffer which is larger than the actual amount of data we put inside it. + result.shrink_to_fit(); + return result; + } + + template TS ConvertUnsignedToSigned(TU value) { + static_assert(sizeof(TS) == sizeof(TU), "Incompatible data types."); + static_assert(!std::numeric_limits::is_signed, + "Source type must be unsigned."); + + // TODO(Dor1s): change to `if constexpr` once C++17 becomes mainstream. + if (std::numeric_limits::is_modulo) + return static_cast(value); + + // Avoid using implementation-defined unsigned to signer conversions. + // To learn more, see https://stackoverflow.com/questions/13150449. + if (value <= std::numeric_limits::max()) + return static_cast(value); + else { + constexpr auto TS_min = std::numeric_limits::min(); + return TS_min + static_cast(value - TS_min); + } + } + + const uint8_t *data_ptr_; + size_t remaining_bytes_; +}; + +#endif // LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ Index: compiler-rt/trunk/lib/fuzzer/FuzzerExtFunctions.def =================================================================== --- compiler-rt/trunk/lib/fuzzer/FuzzerExtFunctions.def +++ compiler-rt/trunk/lib/fuzzer/FuzzerExtFunctions.def @@ -16,12 +16,12 @@ // Optional user functions EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false); EXT_FUNC(LLVMFuzzerCustomMutator, size_t, - (uint8_t * Data, size_t Size, size_t MaxSize, unsigned int Seed), + (uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed), false); EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t, - (const uint8_t * Data1, size_t Size1, - const uint8_t * Data2, size_t Size2, - uint8_t * Out, size_t MaxOutSize, unsigned int Seed), + (const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize, unsigned int Seed), false); // Sanitizer functions Index: compiler-rt/trunk/lib/fuzzer/tests/CMakeLists.txt =================================================================== --- compiler-rt/trunk/lib/fuzzer/tests/CMakeLists.txt +++ compiler-rt/trunk/lib/fuzzer/tests/CMakeLists.txt @@ -75,8 +75,6 @@ set_target_properties(FuzzerUnitTests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - list(APPEND LIBFUZZER_UNITTEST_CFLAGS -I${COMPILER_RT_SOURCE_DIR}/lib/fuzzer/utils) - set(FuzzedDataProviderTestObjects) generate_compiler_rt_tests(FuzzedDataProviderTestObjects FuzzedDataProviderUnitTests "FuzzerUtils-${arch}-Test" ${arch} Index: compiler-rt/trunk/lib/fuzzer/tests/FuzzedDataProviderUnittest.cpp =================================================================== --- compiler-rt/trunk/lib/fuzzer/tests/FuzzedDataProviderUnittest.cpp +++ compiler-rt/trunk/lib/fuzzer/tests/FuzzedDataProviderUnittest.cpp @@ -6,7 +6,7 @@ #include #include -#include "FuzzedDataProvider.h" +#include // The test is intentionally extensive, as behavior of |FuzzedDataProvider| must // not be broken, given than many fuzz targets depend on it. Changing the Index: compiler-rt/trunk/lib/fuzzer/utils/FuzzedDataProvider.h =================================================================== --- compiler-rt/trunk/lib/fuzzer/utils/FuzzedDataProvider.h +++ compiler-rt/trunk/lib/fuzzer/utils/FuzzedDataProvider.h @@ -26,7 +26,7 @@ #include class FuzzedDataProvider { -public: + 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) @@ -186,7 +186,7 @@ // Reports the remaining bytes available for fuzzed input. size_t remaining_bytes() { return remaining_bytes_; } -private: + private: FuzzedDataProvider(const FuzzedDataProvider &) = delete; FuzzedDataProvider &operator=(const FuzzedDataProvider &) = delete; Index: compiler-rt/trunk/lib/sanitizer_common/scripts/check_lint.sh =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/scripts/check_lint.sh +++ compiler-rt/trunk/lib/sanitizer_common/scripts/check_lint.sh @@ -66,7 +66,9 @@ LIT_TESTS=${COMPILER_RT}/test # Headers SANITIZER_INCLUDES=${COMPILER_RT}/include/sanitizer -run_lint ${SANITIZER_INCLUDES_LINT_FILTER} ${SANITIZER_INCLUDES}/*.h & +FUZZER_INCLUDES=${COMPILER_RT}/include/fuzzer +run_lint ${SANITIZER_INCLUDES_LINT_FILTER} ${SANITIZER_INCLUDES}/*.h \ + ${FUZZER_INCLUDES}/*.h & # Sanitizer_common COMMON_RTL=${COMPILER_RT}/lib/sanitizer_common