diff options
| author | Max Moroz <mmoroz@chromium.org> | 2019-08-08 19:49:37 +0000 |
|---|---|---|
| committer | Max Moroz <mmoroz@chromium.org> | 2019-08-08 19:49:37 +0000 |
| commit | df3b465c9c4fed5045470d3eb5b65c14e6de71e6 (patch) | |
| tree | 528a6cdb7323baa88acf92645cd2b94443ea3b5a | |
| parent | d9cbd2acfa7d205788c43abdc66f6ee9680748ce (diff) | |
| download | bcm5719-llvm-df3b465c9c4fed5045470d3eb5b65c14e6de71e6.tar.gz bcm5719-llvm-df3b465c9c4fed5045470d3eb5b65c14e6de71e6.zip | |
[compiler-rt] Add ConsumeProbability and ConsumeFloatingPoint methods to FDP.
Summary:
Also slightly cleaned up the comments and changed the header's extension
back to `.h` as per comments on https://reviews.llvm.org/D65812.
New methods added:
* `ConsumeProbability` returns [0.0, 1.0] by consuming an unsigned integer value
from the input data and dividing that value by the integer's max value.
* `ConsumeFloatingPointInRange` returns a floating point value in the given
range. Relies on `ConsumeProbability` method. This method does not have the
limitation of `std::uniform_real_distribution` that requires the given range
to be <= the floating point type's max. If the range is too large, this
implementation will additionally call `ConsumeBool` to decide whether the
result will be in the first or the second half of the range.
* `ConsumeFloatingPoint` returns a floating point value in the range
`[std::numeric_limits<T>::lowest(), std::numeric_limits<T>::min()]`.
Tested on Linux, Mac, Windows.
Reviewers: morehouse
Reviewed By: morehouse
Subscribers: kubamracek, mgorny, dberris, delcypher, #sanitizers, llvm-commits
Tags: #llvm, #sanitizers
Differential Revision: https://reviews.llvm.org/D65905
llvm-svn: 368331
| -rw-r--r-- | compiler-rt/include/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | compiler-rt/include/fuzzer/FuzzedDataProvider.h (renamed from compiler-rt/include/fuzzer/FuzzedDataProvider.hpp) | 65 | ||||
| -rw-r--r-- | compiler-rt/lib/fuzzer/tests/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | compiler-rt/lib/fuzzer/tests/FuzzedDataProviderUnittest.cpp | 53 | ||||
| -rw-r--r-- | compiler-rt/lib/fuzzer/utils/FuzzedDataProvider.h | 2 | ||||
| -rwxr-xr-x | compiler-rt/lib/sanitizer_common/scripts/check_lint.sh | 2 |
6 files changed, 116 insertions, 10 deletions
diff --git a/compiler-rt/include/CMakeLists.txt b/compiler-rt/include/CMakeLists.txt index d2b2fa0dc1e..57ed6c3ade2 100644 --- a/compiler-rt/include/CMakeLists.txt +++ b/compiler-rt/include/CMakeLists.txt @@ -15,7 +15,7 @@ if (COMPILER_RT_BUILD_SANITIZERS) sanitizer/tsan_interface_atomic.h ) set(FUZZER_HEADERS - fuzzer/FuzzedDataProvider.hpp + fuzzer/FuzzedDataProvider.h ) endif(COMPILER_RT_BUILD_SANITIZERS) diff --git a/compiler-rt/include/fuzzer/FuzzedDataProvider.hpp b/compiler-rt/include/fuzzer/FuzzedDataProvider.h index d6e2d1f762a..47cc4993500 100644 --- a/compiler-rt/include/fuzzer/FuzzedDataProvider.hpp +++ b/compiler-rt/include/fuzzer/FuzzedDataProvider.h @@ -1,4 +1,4 @@ -//===- FuzzedDataProvider.hpp - Utility header for fuzz targets -*- C++ -* ===// +//===- 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. @@ -25,6 +25,8 @@ #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 @@ -143,9 +145,9 @@ class FuzzedDataProvider { return ConsumeBytes<T>(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. - // Returns a std::vector containing all remaining bytes of the input data. std::string ConsumeRemainingBytesAsString() { return ConsumeBytesAsString(remaining_bytes_); } @@ -161,7 +163,7 @@ class FuzzedDataProvider { // Reads one byte and returns a bool, or false when no data remains. bool ConsumeBool() { return 1 & ConsumeIntegral<uint8_t>(); } - // Returns a copy of a value selected from a fixed-size |array|. + // 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."); @@ -170,11 +172,14 @@ class FuzzedDataProvider { template <typename T> T PickValueInArray(std::initializer_list<const T> list) { - // static_assert(list.size() > 0, "The array must be non empty."); + // 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)); } - // Return an enum value. The enum must start at 0 and be contiguous. It must + // 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() { @@ -183,6 +188,56 @@ class FuzzedDataProvider { 0, static_cast<uint32_t>(T::kMaxValue))); } + // 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."); + + // 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; + } + + // 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()); + } + + // 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; + } + + return result + range * ConsumeProbability<T>(); + } + // Reports the remaining bytes available for fuzzed input. size_t remaining_bytes() { return remaining_bytes_; } diff --git a/compiler-rt/lib/fuzzer/tests/CMakeLists.txt b/compiler-rt/lib/fuzzer/tests/CMakeLists.txt index 69e67ab0d50..cfb039c77d0 100644 --- a/compiler-rt/lib/fuzzer/tests/CMakeLists.txt +++ b/compiler-rt/lib/fuzzer/tests/CMakeLists.txt @@ -79,7 +79,7 @@ if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST FUZZER_SUPPORTED_ARCH) generate_compiler_rt_tests(FuzzedDataProviderTestObjects FuzzedDataProviderUnitTests "FuzzerUtils-${arch}-Test" ${arch} SOURCES FuzzedDataProviderUnittest.cpp ${COMPILER_RT_GTEST_SOURCE} - DEPS gtest ${LIBFUZZER_TEST_RUNTIME_DEPS} + DEPS gtest ${LIBFUZZER_TEST_RUNTIME_DEPS} ${COMPILER_RT_SOURCE_DIR}/include/fuzzer/FuzzedDataProvider.h CFLAGS ${LIBFUZZER_UNITTEST_CFLAGS} ${LIBFUZZER_TEST_RUNTIME_CFLAGS} LINK_FLAGS ${LIBFUZZER_UNITTEST_LINK_FLAGS} ${LIBFUZZER_TEST_RUNTIME_LINK_FLAGS}) set_target_properties(FuzzedDataProviderUnitTests PROPERTIES diff --git a/compiler-rt/lib/fuzzer/tests/FuzzedDataProviderUnittest.cpp b/compiler-rt/lib/fuzzer/tests/FuzzedDataProviderUnittest.cpp index 36f2090b1ec..222283434eb 100644 --- a/compiler-rt/lib/fuzzer/tests/FuzzedDataProviderUnittest.cpp +++ b/compiler-rt/lib/fuzzer/tests/FuzzedDataProviderUnittest.cpp @@ -6,7 +6,7 @@ #include <cstdint> #include <cstdlib> -#include <fuzzer/FuzzedDataProvider.hpp> +#include <fuzzer/FuzzedDataProvider.h> // The test is intentionally extensive, as behavior of |FuzzedDataProvider| must // not be broken, given than many fuzz targets depend on it. Changing the @@ -348,6 +348,57 @@ TEST(FuzzedDataProvider, remaining_bytes) { EXPECT_EQ(size_t(0), DataProv.remaining_bytes()); } +TEST(FuzzedDataProvider, ConsumeProbability) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + ASSERT_FLOAT_EQ(float(0.28969181), DataProv.ConsumeProbability<float>()); + ASSERT_DOUBLE_EQ(double(0.086814121166605432), + DataProv.ConsumeProbability<double>()); + ASSERT_FLOAT_EQ(float(0.30104411), DataProv.ConsumeProbability<float>()); + ASSERT_DOUBLE_EQ(double(0.96218831486039413), + DataProv.ConsumeProbability<double>()); + ASSERT_FLOAT_EQ(float(0.67005056), DataProv.ConsumeProbability<float>()); + ASSERT_DOUBLE_EQ(double(0.69210584173832279), + DataProv.ConsumeProbability<double>()); + + // Exhaust the buffer. + EXPECT_EQ(std::vector<uint8_t>(Data, Data + sizeof(Data) - 36), + DataProv.ConsumeRemainingBytes<uint8_t>()); + ASSERT_FLOAT_EQ(float(0.0), DataProv.ConsumeProbability<float>()); +} + +TEST(FuzzedDataProvider, ConsumeFloatingPoint) { + FuzzedDataProvider DataProv(Data, sizeof(Data)); + ASSERT_FLOAT_EQ(float(-2.8546307e+38), + DataProv.ConsumeFloatingPoint<float>()); + ASSERT_DOUBLE_EQ(double(8.0940194040236032e+307), + DataProv.ConsumeFloatingPoint<double>()); + ASSERT_FLOAT_EQ(float(271.49084), + DataProv.ConsumeFloatingPointInRange<float>(123.0, 777.0)); + ASSERT_DOUBLE_EQ(double(30.859126145478349), + DataProv.ConsumeFloatingPointInRange<double>(13.37, 31.337)); + ASSERT_FLOAT_EQ( + float(-903.47729), + DataProv.ConsumeFloatingPointInRange<float>(-999.9999, -777.77)); + ASSERT_DOUBLE_EQ( + double(24.561393182922771), + DataProv.ConsumeFloatingPointInRange<double>(-13.37, 31.337)); + ASSERT_FLOAT_EQ(float(1.0), + DataProv.ConsumeFloatingPointInRange<float>(1.0, 1.0)); + ASSERT_DOUBLE_EQ(double(-1.0), + DataProv.ConsumeFloatingPointInRange<double>(-1.0, -1.0)); + + // Exhaust the buffer. + EXPECT_EQ((std::vector<uint8_t>(Data, Data + sizeof(Data) - 50)).size(), + DataProv.ConsumeRemainingBytes<uint8_t>().size()); + ASSERT_FLOAT_EQ(float(0.0), DataProv.ConsumeProbability<float>()); + ASSERT_NEAR(std::numeric_limits<double>::lowest(), + DataProv.ConsumeFloatingPoint<double>(), 1e-10); + ASSERT_FLOAT_EQ(float(123.0), + DataProv.ConsumeFloatingPointInRange<float>(123.0, 777.0)); + ASSERT_DOUBLE_EQ(double(-13.37), DataProv.ConsumeFloatingPointInRange<double>( + -13.37, 31.337)); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/compiler-rt/lib/fuzzer/utils/FuzzedDataProvider.h b/compiler-rt/lib/fuzzer/utils/FuzzedDataProvider.h index dd2ea20b906..5692060c7f7 100644 --- a/compiler-rt/lib/fuzzer/utils/FuzzedDataProvider.h +++ b/compiler-rt/lib/fuzzer/utils/FuzzedDataProvider.h @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// This a temporary copy of compiler-rt/include/fuzzer/FuzzedDataProvider.hpp. +// This a temporary copy of compiler-rt/include/fuzzer/FuzzedDataProvider.h. // TODO(mmoroz@chromium.org): delete this copy. // 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 diff --git a/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh b/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh index 8a4c1247fd3..d78fb72f36f 100755 --- a/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh +++ b/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh @@ -68,7 +68,7 @@ LIT_TESTS=${COMPILER_RT}/test SANITIZER_INCLUDES=${COMPILER_RT}/include/sanitizer FUZZER_INCLUDES=${COMPILER_RT}/include/fuzzer run_lint ${SANITIZER_INCLUDES_LINT_FILTER} ${SANITIZER_INCLUDES}/*.h \ - ${FUZZER_INCLUDES}/*.hpp & + ${FUZZER_INCLUDES}/*.h & # Sanitizer_common COMMON_RTL=${COMPILER_RT}/lib/sanitizer_common |

