diff --git a/fuzzers/BUILD.gn b/fuzzers/BUILD.gn index ebcfde35c6..eb017d3cc3 100644 --- a/fuzzers/BUILD.gn +++ b/fuzzers/BUILD.gn @@ -54,7 +54,7 @@ if (build_with_chromium) { # fuzzer_test doesn't have configs members, so need to define them in an empty # source_set. - source_set("tint_fuzzer_common") { + source_set("tint_fuzzer_common_src") { public_configs = [ "${tint_root_dir}/src:tint_config", "${tint_root_dir}/src:tint_common_config", @@ -67,8 +67,12 @@ if (build_with_chromium) { sources = [ "data_builder.h", + "mersenne_twister_engine.cc", + "mersenne_twister_engine.h", "random_generator.cc", "random_generator.h", + "random_generator_engine.cc", + "random_generator_engine.h", "tint_common_fuzzer.cc", "tint_common_fuzzer.h", "tint_reader_writer_fuzzer.h", @@ -76,8 +80,8 @@ if (build_with_chromium) { ] } - source_set("tint_fuzzer_common_with_init") { - public_deps = [ ":tint_fuzzer_common" ] + source_set("tint_fuzzer_common_with_init_src") { + public_deps = [ ":tint_fuzzer_common_src" ] sources = [ "cli.cc", @@ -90,7 +94,7 @@ if (build_with_chromium) { if (tint_build_wgsl_reader && tint_build_wgsl_writer) { fuzzer_test("tint_ast_clone_fuzzer") { sources = [ "tint_ast_clone_fuzzer.cc" ] - deps = [ ":tint_fuzzer_common_with_init" ] + deps = [ ":tint_fuzzer_common_with_init_src" ] dict = "dictionary.txt" libfuzzer_options = tint_fuzzer_common_libfuzzer_options seed_corpus = fuzzer_corpus_wgsl_dir @@ -115,7 +119,7 @@ if (build_with_chromium) { fuzzer_test("tint_wgsl_reader_wgsl_writer_fuzzer") { sources = [ "tint_wgsl_reader_wgsl_writer_fuzzer.cc" ] - deps = [ ":tint_fuzzer_common_with_init" ] + deps = [ ":tint_fuzzer_common_with_init_src" ] dict = "dictionary.txt" libfuzzer_options = tint_fuzzer_common_libfuzzer_options seed_corpus = fuzzer_corpus_wgsl_dir @@ -126,7 +130,7 @@ if (build_with_chromium) { if (tint_build_wgsl_reader && tint_build_spv_writer) { fuzzer_test("tint_all_transforms_fuzzer") { sources = [ "tint_all_transforms_fuzzer.cc" ] - deps = [ ":tint_fuzzer_common_with_init" ] + deps = [ ":tint_fuzzer_common_with_init_src" ] dict = "dictionary.txt" libfuzzer_options = tint_fuzzer_common_libfuzzer_options seed_corpus = fuzzer_corpus_wgsl_dir @@ -143,7 +147,7 @@ if (build_with_chromium) { fuzzer_test("tint_binding_remapper_fuzzer") { sources = [ "tint_binding_remapper_fuzzer.cc" ] - deps = [ ":tint_fuzzer_common_with_init" ] + deps = [ ":tint_fuzzer_common_with_init_src" ] dict = "dictionary.txt" libfuzzer_options = tint_fuzzer_common_libfuzzer_options seed_corpus = fuzzer_corpus_wgsl_dir @@ -152,7 +156,7 @@ if (build_with_chromium) { fuzzer_test("tint_first_index_offset_fuzzer") { sources = [ "tint_first_index_offset_fuzzer.cc" ] - deps = [ ":tint_fuzzer_common_with_init" ] + deps = [ ":tint_fuzzer_common_with_init_src" ] dict = "dictionary.txt" libfuzzer_options = tint_fuzzer_common_libfuzzer_options seed_corpus = fuzzer_corpus_wgsl_dir @@ -169,7 +173,7 @@ if (build_with_chromium) { fuzzer_test("tint_renamer_fuzzer") { sources = [ "tint_renamer_fuzzer.cc" ] - deps = [ ":tint_fuzzer_common_with_init" ] + deps = [ ":tint_fuzzer_common_with_init_src" ] dict = "dictionary.txt" libfuzzer_options = tint_fuzzer_common_libfuzzer_options seed_corpus = fuzzer_corpus_wgsl_dir @@ -178,7 +182,7 @@ if (build_with_chromium) { fuzzer_test("tint_robustness_fuzzer") { sources = [ "tint_robustness_fuzzer.cc" ] - deps = [ ":tint_fuzzer_common_with_init" ] + deps = [ ":tint_fuzzer_common_with_init_src" ] dict = "dictionary.txt" libfuzzer_options = tint_fuzzer_common_libfuzzer_options seed_corpus = fuzzer_corpus_wgsl_dir @@ -187,7 +191,7 @@ if (build_with_chromium) { fuzzer_test("tint_single_entry_point_fuzzer") { sources = [ "tint_single_entry_point_fuzzer.cc" ] - deps = [ ":tint_fuzzer_common_with_init" ] + deps = [ ":tint_fuzzer_common_with_init_src" ] dict = "dictionary.txt" libfuzzer_options = tint_fuzzer_common_libfuzzer_options seed_corpus = fuzzer_corpus_wgsl_dir @@ -196,7 +200,7 @@ if (build_with_chromium) { fuzzer_test("tint_vertex_pulling_fuzzer") { sources = [ "tint_vertex_pulling_fuzzer.cc" ] - deps = [ ":tint_fuzzer_common_with_init" ] + deps = [ ":tint_fuzzer_common_with_init_src" ] dict = "dictionary.txt" libfuzzer_options = tint_fuzzer_common_libfuzzer_options seed_corpus = fuzzer_corpus_wgsl_dir @@ -205,7 +209,7 @@ if (build_with_chromium) { fuzzer_test("tint_wgsl_reader_spv_writer_fuzzer") { sources = [ "tint_wgsl_reader_spv_writer_fuzzer.cc" ] - deps = [ ":tint_fuzzer_common_with_init" ] + deps = [ ":tint_fuzzer_common_with_init_src" ] dict = "dictionary.txt" libfuzzer_options = tint_fuzzer_common_libfuzzer_options seed_corpus = fuzzer_corpus_wgsl_dir @@ -232,7 +236,7 @@ if (build_with_chromium) { fuzzer_test("tint_wgsl_reader_hlsl_writer_fuzzer") { sources = [ "tint_wgsl_reader_hlsl_writer_fuzzer.cc" ] - deps = [ ":tint_fuzzer_common_with_init" ] + deps = [ ":tint_fuzzer_common_with_init_src" ] dict = "dictionary.txt" libfuzzer_options = tint_fuzzer_common_libfuzzer_options seed_corpus = fuzzer_corpus_wgsl_dir @@ -259,7 +263,7 @@ if (build_with_chromium) { fuzzer_test("tint_wgsl_reader_msl_writer_fuzzer") { sources = [ "tint_wgsl_reader_msl_writer_fuzzer.cc" ] - deps = [ ":tint_fuzzer_common_with_init" ] + deps = [ ":tint_fuzzer_common_with_init_src" ] dict = "dictionary.txt" libfuzzer_options = tint_fuzzer_common_libfuzzer_options seed_corpus = fuzzer_corpus_wgsl_dir @@ -272,7 +276,7 @@ if (build_with_chromium) { tint_build_wgsl_writer) { executable("tint_black_box_fuzz_target") { sources = [ "tint_black_box_fuzz_target.cc" ] - deps = [ ":tint_fuzzer_common" ] + deps = [ ":tint_fuzzer_common_src" ] } } diff --git a/fuzzers/CMakeLists.txt b/fuzzers/CMakeLists.txt index 25e53a3e2e..36d9e1f096 100644 --- a/fuzzers/CMakeLists.txt +++ b/fuzzers/CMakeLists.txt @@ -20,8 +20,12 @@ function(add_tint_fuzzer NAME) data_builder.h fuzzer_init.cc fuzzer_init.h + mersenne_twister_engine.cc + mersenne_twister_engine.h random_generator.cc random_generator.h + random_generator_engine.cc + random_generator_engine.h tint_common_fuzzer.cc tint_common_fuzzer.h tint_reader_writer_fuzzer.h @@ -93,9 +97,13 @@ if (${TINT_BUILD_WGSL_READER} AND ${TINT_BUILD_SPV_WRITER} AND ${TINT_BUILD_WGSL_WRITER}) add_executable(tint_black_box_fuzz_target + mersenne_twister_engine.cc + mersenne_twister_engine.h random_generator.cc random_generator.h - tint_black_box_fuzz_target + random_generator_engine.cc + random_generator_engine.h + tint_black_box_fuzz_target.cc tint_common_fuzzer.cc tint_common_fuzzer.h ) diff --git a/fuzzers/data_builder.h b/fuzzers/data_builder.h index 47a4fea8ee..39625ef545 100644 --- a/fuzzers/data_builder.h +++ b/fuzzers/data_builder.h @@ -36,7 +36,7 @@ class DataBuilder { /// @param data - data fuzzer to calculate seed from /// @param size - size of data buffer explicit DataBuilder(const uint8_t* data, size_t size) - : generator_(data, size) { + : generator_(RandomGenerator::CalculateSeed(data, size)) { assert(data != nullptr && "|data| must be !nullptr"); } diff --git a/fuzzers/mersenne_twister_engine.cc b/fuzzers/mersenne_twister_engine.cc new file mode 100644 index 0000000000..951b70671c --- /dev/null +++ b/fuzzers/mersenne_twister_engine.cc @@ -0,0 +1,59 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fuzzers/mersenne_twister_engine.h" + +#include +#include + +#include "src/utils/hash.h" + +namespace tint { +namespace fuzzers { + +namespace { + +/// Generate integer from uniform distribution +/// @tparam I - integer type +/// @param engine - random number engine to use +/// @param lower - Lower bound of integer generated +/// @param upper - Upper bound of integer generated +/// @returns i, where lower <= i < upper +template +I RandomInteger(std::mt19937_64* engine, I lower, I upper) { + assert(lower < upper && "|lower| must be strictly less than |upper|"); + return std::uniform_int_distribution(lower, upper - 1)(*engine); +} + +} // namespace + +MersenneTwisterEngine::MersenneTwisterEngine(uint64_t seed) : engine_(seed) {} + +uint32_t MersenneTwisterEngine::RandomUInt32(uint32_t lower, uint32_t upper) { + return RandomInteger(&engine_, lower, upper); +} + +uint64_t MersenneTwisterEngine::RandomUInt64(uint64_t lower, uint64_t upper) { + return RandomInteger(&engine_, lower, upper); +} + +void MersenneTwisterEngine::RandomNBytes(uint8_t* dest, size_t n) { + assert(dest && "|dest| must not be nullptr"); + std::generate( + dest, dest + n, + std::independent_bits_engine(engine_)); +} + +} // namespace fuzzers +} // namespace tint diff --git a/fuzzers/mersenne_twister_engine.h b/fuzzers/mersenne_twister_engine.h new file mode 100644 index 0000000000..f384cac4be --- /dev/null +++ b/fuzzers/mersenne_twister_engine.h @@ -0,0 +1,62 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FUZZERS_MERSENNE_TWISTER_ENGINE_H_ +#define FUZZERS_MERSENNE_TWISTER_ENGINE_H_ + +#include + +#include "fuzzers/random_generator_engine.h" + +namespace tint { +namespace fuzzers { + +/// Standard MT based random number generation +class MersenneTwisterEngine : public RandomGeneratorEngine { + public: + /// @brief Initializes using provided seed + /// @param seed - seed value to use + explicit MersenneTwisterEngine(uint64_t seed); + ~MersenneTwisterEngine() override = default; + + /// Generate random uint32_t value from uniform distribution. + /// @param lower - lower bound of integer generated + /// @param upper - upper bound of integer generated + /// @returns i, where lower <= i < upper + uint32_t RandomUInt32(uint32_t lower, uint32_t upper) override; + + /// Get random uint64_t value from uniform distribution. + /// @param lower - lower bound of integer generated + /// @param upper - upper bound of integer generated + /// @returns i, where lower <= i < upper + uint64_t RandomUInt64(uint64_t lower, uint64_t upper) override; + + /// Get N bytes of pseudo-random data + /// @param dest - memory location to store data + /// @param n - number of bytes of data to generate + void RandomNBytes(uint8_t* dest, size_t n) override; + + private: + // Disallow copy & assign + MersenneTwisterEngine(const MersenneTwisterEngine&) = delete; + MersenneTwisterEngine& operator=(const MersenneTwisterEngine&) = delete; + + std::mt19937_64 engine_; + +}; // class MersenneTwisterEngine + +} // namespace fuzzers +} // namespace tint + +#endif // FUZZERS_MERSENNE_TWISTER_ENGINE_H_ diff --git a/fuzzers/random_generator.cc b/fuzzers/random_generator.cc index f97633a7ca..c535450a6e 100644 --- a/fuzzers/random_generator.cc +++ b/fuzzers/random_generator.cc @@ -16,8 +16,10 @@ #include #include -#include +#include +#include "fuzzers/mersenne_twister_engine.h" +#include "fuzzers/random_generator_engine.h" #include "src/utils/hash.h" namespace tint { @@ -25,19 +27,6 @@ namespace fuzzers { namespace { -/// Generate integer from uniform distribution -/// @tparam I - integer type -/// @param engine - random number engine to use -/// @param lower - Lower bound of integer generated -/// @param upper - Upper bound of integer generated -/// @returns i, where lower <= i < upper -template -I RandomUInt(std::mt19937_64* engine, I lower, I upper) { - assert(lower < upper && "|lower| must be stictly less than |upper|"); - - return std::uniform_int_distribution(lower, upper - 1)(*engine); -} - /// Calculate the hash for the contents of a c-style data buffer /// This is intentionally not implemented as a generic override of HashCombine /// in "src/utils/hash.h", because it conflicts with the vardiac override for @@ -56,55 +45,58 @@ size_t HashBuffer(const uint8_t* data, const size_t size) { } // namespace -RandomGenerator::RandomGenerator(uint64_t seed) : engine_(seed) {} +RandomGenerator::RandomGenerator(std::unique_ptr engine) + : engine_(std::move(engine)) {} -RandomGenerator::RandomGenerator(const uint8_t* data, size_t size) - : engine_(RandomGenerator::CalculateSeed(data, size)) { - assert(data != nullptr && "|data| must be !nullptr"); -} +RandomGenerator::RandomGenerator(uint64_t seed) + : RandomGenerator(std::make_unique(seed)) {} uint32_t RandomGenerator::GetUInt32(uint32_t lower, uint32_t upper) { - return RandomUInt(&engine_, lower, upper); + assert(lower < upper && "|lower| must be strictly less than |upper|"); + return engine_->RandomUInt32(lower, upper); } uint32_t RandomGenerator::GetUInt32(uint32_t bound) { assert(bound > 0 && "|bound| must be greater than 0"); - return RandomUInt(&engine_, 0u, bound); + return engine_->RandomUInt32(0u, bound); } uint64_t RandomGenerator::GetUInt64(uint64_t lower, uint64_t upper) { - return RandomUInt(&engine_, lower, upper); + assert(lower < upper && "|lower| must be strictly less than |upper|"); + return engine_->RandomUInt64(lower, upper); } uint64_t RandomGenerator::GetUInt64(uint64_t bound) { assert(bound > 0 && "|bound| must be greater than 0"); - return RandomUInt(&engine_, static_cast(0), bound); + return engine_->RandomUInt64(static_cast(0), bound); } uint8_t RandomGenerator::GetByte() { - return std::independent_bits_engine(engine_)(); + uint8_t result; + engine_->RandomNBytes(&result, 1); + return result; } uint32_t RandomGenerator::Get4Bytes() { - return std::independent_bits_engine(engine_)(); + uint32_t result; + engine_->RandomNBytes(reinterpret_cast(&result), 4); + return result; } void RandomGenerator::GetNBytes(uint8_t* dest, size_t n) { assert(dest && "|dest| must not be nullptr"); - std::generate( - dest, dest + n, - std::independent_bits_engine(engine_)); + engine_->RandomNBytes(dest, n); } bool RandomGenerator::GetBool() { - return RandomUInt(&engine_, 0u, 2u); + return engine_->RandomUInt32(0u, 2u); } bool RandomGenerator::GetWeightedBool(uint32_t percentage) { static const uint32_t kMaxPercentage = 100; assert(percentage <= kMaxPercentage && "|percentage| needs to be within [0, 100]"); - return RandomUInt(&engine_, 0u, kMaxPercentage) < percentage; + return engine_->RandomUInt32(0u, kMaxPercentage) < percentage; } uint64_t RandomGenerator::CalculateSeed(const uint8_t* data, size_t size) { @@ -119,16 +111,14 @@ uint64_t RandomGenerator::CalculateSeed(const uint8_t* data, size_t size) { static const int64_t kHashDesiredMinBytes = 4; // Maximum number of bytes we want to use in the hash. static const int64_t kHashDesiredMaxBytes = 32; - int64_t size_i64 = static_cast(size); - int64_t hash_begin_i64 = + auto size_i64 = static_cast(size); + auto hash_begin_i64 = std::min(kHashDesiredLeadingSkipBytes, std::max(size_i64 - kHashDesiredMinBytes, 0)); - int64_t hash_end_i64 = - std::min(hash_begin_i64 + kHashDesiredMaxBytes, size_i64); - size_t hash_begin = static_cast(hash_begin_i64); - size_t hash_size = static_cast(hash_end_i64) - hash_begin; + auto hash_end_i64 = std::min(hash_begin_i64 + kHashDesiredMaxBytes, size_i64); + auto hash_begin = static_cast(hash_begin_i64); + auto hash_size = static_cast(hash_end_i64) - hash_begin; return HashBuffer(data + hash_begin, hash_size); } - } // namespace fuzzers } // namespace tint diff --git a/fuzzers/random_generator.h b/fuzzers/random_generator.h index 5a61baed4f..6f37f8e3b6 100644 --- a/fuzzers/random_generator.h +++ b/fuzzers/random_generator.h @@ -15,23 +15,25 @@ #ifndef FUZZERS_RANDOM_GENERATOR_H_ #define FUZZERS_RANDOM_GENERATOR_H_ +#include #include #include +#include "fuzzers/random_generator_engine.h" + namespace tint { namespace fuzzers { /// Pseudo random generator utility class for fuzzing class RandomGenerator { public: - /// @brief Initializes the internal engine - /// @param seed - seed value passed to engine - explicit RandomGenerator(uint64_t seed); + /// @brief Initializes using provided engine + /// @param engine - engine implementation to use + explicit RandomGenerator(std::unique_ptr engine); - /// @brief Wrapper that invokes CalculateSeed for caller - /// @param data - data fuzzer to calculate seed from - /// @param size - size of data buffer - explicit RandomGenerator(const uint8_t* data, size_t size); + /// @brief Creates a MersenneTwisterEngine and initializes using that + /// @param seed - seed value to use for engine + explicit RandomGenerator(uint64_t seed); ~RandomGenerator() = default; RandomGenerator(RandomGenerator&&) = default; @@ -70,7 +72,7 @@ class RandomGenerator { /// Get N bytes of pseudo-random data /// @param dest - memory location to store data - /// @param n - number of bytes of data to generate + /// @param n - number of bytes of data to get void GetNBytes(uint8_t* dest, size_t n); /// Get random bool with even odds @@ -83,6 +85,14 @@ class RandomGenerator { /// of the time. bool GetWeightedBool(uint32_t percentage); + /// Returns a randomly-chosen element from vector v. + /// @param v - the vector from which the random element will be selected. + /// @return a random element of vector v. + template + inline T GetRandomElement(const std::vector& v) { + return v[GetUInt64(0, v.size())]; + } + /// Calculate a seed value based on a blob of data. /// Currently hashes bytes near the front of the buffer, after skipping N /// bytes. @@ -90,21 +100,13 @@ class RandomGenerator { /// @param size - number of elements in |data|, must be > 0 static uint64_t CalculateSeed(const uint8_t* data, size_t size); - /// Returns a randomly-chosen element from vector v. - /// @param v - the vector from which the random element will be selected. - /// @return a random element of vector v. - template - inline T GetRandomElement(const std::vector& v) { - return v[GetUInt64(0, v.size() - 1)]; - } - private: - std::mt19937_64 engine_; - // Disallow copy & assign RandomGenerator(const RandomGenerator&) = delete; RandomGenerator& operator=(const RandomGenerator&) = delete; + std::unique_ptr engine_; + }; // class RandomGenerator } // namespace fuzzers diff --git a/fuzzers/random_generator_engine.cc b/fuzzers/random_generator_engine.cc new file mode 100644 index 0000000000..6c14178646 --- /dev/null +++ b/fuzzers/random_generator_engine.cc @@ -0,0 +1,26 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fuzzers/random_generator_engine.h" + +namespace tint { +namespace fuzzers { + +// Not in header to avoid weak vtable warnings from clang +RandomGeneratorEngine::RandomGeneratorEngine() = default; +RandomGeneratorEngine::~RandomGeneratorEngine() = default; +RandomGeneratorEngine::RandomGeneratorEngine(RandomGeneratorEngine&&) = default; + +} // namespace fuzzers +} // namespace tint diff --git a/fuzzers/random_generator_engine.h b/fuzzers/random_generator_engine.h new file mode 100644 index 0000000000..5df64fa891 --- /dev/null +++ b/fuzzers/random_generator_engine.h @@ -0,0 +1,58 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FUZZERS_RANDOM_GENERATOR_ENGINE_H_ +#define FUZZERS_RANDOM_GENERATOR_ENGINE_H_ + +#include +#include +#include + +namespace tint { +namespace fuzzers { + +/// Wrapper interface around STL random number engine +class RandomGeneratorEngine { + public: + RandomGeneratorEngine(); + virtual ~RandomGeneratorEngine(); + RandomGeneratorEngine(RandomGeneratorEngine&&); + + /// Generate random uint32_t value from uniform distribution. + /// @param lower - lower bound of integer generated + /// @param upper - upper bound of integer generated + /// @returns i, where lower <= i < upper + virtual uint32_t RandomUInt32(uint32_t lower, uint32_t upper) = 0; + + /// Get random uint64_t value from uniform distribution. + /// @param lower - lower bound of integer generated + /// @param upper - upper bound of integer generated + /// @returns i, where lower <= i < upper + virtual uint64_t RandomUInt64(uint64_t lower, uint64_t upper) = 0; + + /// Get N bytes of pseudo-random data + /// @param dest - memory location to store data + /// @param n - number of bytes of data to generate + virtual void RandomNBytes(uint8_t* dest, size_t n) = 0; + + private: + // Disallow copy & assign + RandomGeneratorEngine(const RandomGeneratorEngine&) = delete; + RandomGeneratorEngine& operator=(const RandomGeneratorEngine&) = delete; +}; // class RandomGeneratorEngine + +} // namespace fuzzers +} // namespace tint + +#endif // FUZZERS_RANDOM_GENERATOR_ENGINE_H_ diff --git a/fuzzers/random_generator_test.cc b/fuzzers/random_generator_test.cc new file mode 100644 index 0000000000..53bc5cb22c --- /dev/null +++ b/fuzzers/random_generator_test.cc @@ -0,0 +1,202 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fuzzers/random_generator.h" + +#include + +#include "gtest/gtest.h" + +#include "fuzzers/mersenne_twister_engine.h" + +namespace tint { +namespace fuzzers { +namespace { + +/// Implementation of RandomGeneratorEngine that just returns a stream of +/// monotonically increasing numbers. +class MonotonicEngine : public RandomGeneratorEngine { + public: + uint32_t RandomUInt32(uint32_t, uint32_t) override { return next_++; } + + uint64_t RandomUInt64(uint64_t, uint64_t) override { return next_++; } + + void RandomNBytes(uint8_t*, size_t) override { + assert(false && "MonotonicDelegate does not implement RandomNBytes"); + } + + private: + uint32_t next_ = 0; +}; + +class RandomGeneratorTest : public testing::Test { + public: + void SetUp() override { rng_ = std::make_unique(0); } + + void TearDown() override {} + + protected: + std::unique_ptr rng_; +}; + +#ifndef NDEBUG +TEST_F(RandomGeneratorTest, GetUInt32ReversedBoundsCrashes) { + EXPECT_DEATH(rng_->GetUInt32(10, 5), ".*"); +} + +TEST_F(RandomGeneratorTest, GetUInt32EmptyBoundsCrashes) { + EXPECT_DEATH(rng_->GetUInt32(5, 5), ".*"); +} + +TEST_F(RandomGeneratorTest, GetUInt32ZeroBoundCrashes) { + EXPECT_DEATH(rng_->GetUInt32(0u), ".*"); +} +#endif // NDEBUG + +TEST_F(RandomGeneratorTest, GetUInt32SingularReturnsOneValue) { + { + uint32_t result = rng_->GetUInt32(5u, 6u); + ASSERT_EQ(5u, result); + } + { + uint32_t result = rng_->GetUInt32(1u); + ASSERT_EQ(0u, result); + } +} + +TEST_F(RandomGeneratorTest, GetUInt32StaysInBounds) { + { + uint32_t result = rng_->GetUInt32(5u, 10u); + ASSERT_LE(5u, result); + ASSERT_GT(10u, result); + } + { + uint32_t result = rng_->GetUInt32(10u); + ASSERT_LE(0u, result); + ASSERT_GT(10u, result); + } +} + +#ifndef NDEBUG +TEST_F(RandomGeneratorTest, GetUInt64ReversedBoundsCrashes) { + EXPECT_DEATH(rng_->GetUInt64(10, 5), ".*"); +} + +TEST_F(RandomGeneratorTest, GetUInt64EmptyBoundsCrashes) { + EXPECT_DEATH(rng_->GetUInt64(5, 5), ".*"); +} + +TEST_F(RandomGeneratorTest, GetUInt64ZeroBoundCrashes) { + EXPECT_DEATH(rng_->GetUInt64(0u), ".*"); +} +#endif // NDEBUG + +TEST_F(RandomGeneratorTest, GetUInt64SingularReturnsOneValue) { + { + uint64_t result = rng_->GetUInt64(5u, 6u); + ASSERT_EQ(5u, result); + } + { + uint64_t result = rng_->GetUInt64(1u); + ASSERT_EQ(0u, result); + } +} + +TEST_F(RandomGeneratorTest, GetUInt64StaysInBounds) { + { + uint64_t result = rng_->GetUInt64(5u, 10u); + ASSERT_LE(5u, result); + ASSERT_GT(10u, result); + } + { + uint64_t result = rng_->GetUInt64(10u); + ASSERT_LE(0u, result); + ASSERT_GT(10u, result); + } +} + +TEST_F(RandomGeneratorTest, GetByte) { + rng_->GetByte(); +} + +#ifndef NDEBUG +TEST_F(RandomGeneratorTest, GetNBytesNullDataBufferCrashes) { + EXPECT_DEATH(rng_->GetNBytes(nullptr, 5), ".*"); +} +#endif // NDEBUG + +TEST_F(RandomGeneratorTest, GetNBytes) { + std::vector data; + for (uint32_t i = 25; i < 1000u; i = i + 25) { + data.resize(i); + rng_->GetNBytes(data.data(), data.size()); + } +} + +TEST_F(RandomGeneratorTest, GetBool) { + rng_->GetBool(); +} + +TEST_F(RandomGeneratorTest, GetWeightedBoolZeroAlwaysFalse) { + ASSERT_FALSE(rng_->GetWeightedBool(0)); +} + +TEST_F(RandomGeneratorTest, GetWeightedBoolHundredAlwaysTrue) { + ASSERT_TRUE(rng_->GetWeightedBool(100)); +} + +#ifndef NDEBUG +TEST_F(RandomGeneratorTest, GetWeightedBoolAboveHundredCrashes) { + EXPECT_DEATH(rng_->GetWeightedBool(101), ".*"); + EXPECT_DEATH(rng_->GetWeightedBool(500), ".*"); +} +#endif // NDEBUG + +TEST_F(RandomGeneratorTest, GetWeightedBool) { + for (uint32_t i = 0; i <= 100; i++) { + rng_ = + std::make_unique(std::make_unique()); + for (uint32_t j = 0; j <= 100; j++) { + if (j < i) { + ASSERT_TRUE(rng_->GetWeightedBool(i)); + } else { + ASSERT_FALSE(rng_->GetWeightedBool(i)); + } + } + } +} + +#ifndef NDEBUG +TEST_F(RandomGeneratorTest, GetRandomElementEmptyVectorCrashes) { + std::vector v; + EXPECT_DEATH(rng_->GetRandomElement(v), ".*"); +} +#endif // NDEBUG + +TEST_F(RandomGeneratorTest, GetRandomElement) { + std::vector v; + for (uint32_t i = 25; i < 100u; i = i + 25) { + rng_ = + std::make_unique(std::make_unique()); + v.resize(i); + std::iota(v.begin(), v.end(), 0); + for (uint32_t j = 0; j < i; j++) { + EXPECT_EQ(j, rng_->GetRandomElement(v)); + } + } +} + +} // namespace +} // namespace fuzzers +} // namespace tint diff --git a/fuzzers/tint_ast_fuzzer/BUILD.gn b/fuzzers/tint_ast_fuzzer/BUILD.gn index 1b8a1f525b..aedb1abfad 100644 --- a/fuzzers/tint_ast_fuzzer/BUILD.gn +++ b/fuzzers/tint_ast_fuzzer/BUILD.gn @@ -34,7 +34,7 @@ if (build_with_chromium) { deps = [ ":tint_ast_fuzzer_proto", - "${tint_root_dir}/fuzzers:tint_fuzzer_common", + "${tint_root_dir}/fuzzers:tint_fuzzer_common_src", "//third_party/protobuf:protobuf_full", ] diff --git a/fuzzers/tint_ast_fuzzer/CMakeLists.txt b/fuzzers/tint_ast_fuzzer/CMakeLists.txt index 1dcb668a97..757cd5320a 100644 --- a/fuzzers/tint_ast_fuzzer/CMakeLists.txt +++ b/fuzzers/tint_ast_fuzzer/CMakeLists.txt @@ -35,7 +35,9 @@ add_custom_command( COMMENT "Generate protobuf sources from proto definition file.") set(LIBTINT_AST_FUZZER_SOURCES + ../mersenne_twister_engine.h ../random_generator.h + ../random_generator_engine.h mutation.h mutation_finder.h mutation_finders/replace_identifiers.h @@ -48,7 +50,9 @@ set(LIBTINT_AST_FUZZER_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/protobufs/tint_ast_fuzzer.pb.h) set(LIBTINT_AST_FUZZER_SOURCES ${LIBTINT_AST_FUZZER_SOURCES} + ../mersenne_twister_engine.cc ../random_generator.cc + ../random_generator_engine.cc mutation.cc mutation_finder.cc mutation_finders/replace_identifiers.cc diff --git a/fuzzers/tint_regex_fuzzer/BUILD.gn b/fuzzers/tint_regex_fuzzer/BUILD.gn index 7b898a9b90..646c11a520 100644 --- a/fuzzers/tint_regex_fuzzer/BUILD.gn +++ b/fuzzers/tint_regex_fuzzer/BUILD.gn @@ -22,7 +22,7 @@ if (build_with_chromium) { "${tint_root_dir}/src:tint_common_config", ] - deps = [ "${tint_root_dir}/fuzzers:tint_fuzzer_common" ] + deps = [ "${tint_root_dir}/fuzzers:tint_fuzzer_common_src" ] sources = [ "cli.cc", diff --git a/fuzzers/tint_regex_fuzzer/CMakeLists.txt b/fuzzers/tint_regex_fuzzer/CMakeLists.txt index bd3fd8c195..bcd0885bba 100644 --- a/fuzzers/tint_regex_fuzzer/CMakeLists.txt +++ b/fuzzers/tint_regex_fuzzer/CMakeLists.txt @@ -21,8 +21,12 @@ function(add_tint_regex_fuzzer NAME) endfunction() set(LIBTINT_REGEX_FUZZER_SOURCES + ../mersenne_twister_engine.cc + ../mersenne_twister_engine.h ../random_generator.cc ../random_generator.h + ../random_generator_engine.cc + ../random_generator_engine.h wgsl_mutator.cc wgsl_mutator.h) diff --git a/fuzzers/tint_spirv_tools_fuzzer/CMakeLists.txt b/fuzzers/tint_spirv_tools_fuzzer/CMakeLists.txt index 23d97417bd..c83333af79 100644 --- a/fuzzers/tint_spirv_tools_fuzzer/CMakeLists.txt +++ b/fuzzers/tint_spirv_tools_fuzzer/CMakeLists.txt @@ -13,7 +13,9 @@ # limitations under the License. set(FUZZER_SOURCES + ../mersenne_twister_engine.cc ../random_generator.cc + ../random_generator_engine.cc cli.cc fuzzer.cc mutator.cc @@ -24,7 +26,9 @@ set(FUZZER_SOURCES util.cc) set(FUZZER_SOURCES ${FUZZER_SOURCES} + ../mersenne_twister_engine.h ../random_generator.h + ../random_generator_engine.h cli.h mutator.h mutator_cache.h @@ -76,7 +80,9 @@ add_tint_spirv_tools_fuzzer(tint_spirv_tools_spv_writer_fuzzer) add_tint_spirv_tools_fuzzer(tint_spirv_tools_wgsl_writer_fuzzer) set(DEBUGGER_SOURCES + ../mersenne_twister_engine.cc ../random_generator.cc + ../random_generator_engine.cc cli.cc mutator.cc mutator_debugger.cc @@ -86,7 +92,9 @@ set(DEBUGGER_SOURCES util.cc) set(DEBUGGER_SOURCES ${DEBUGGER_SOURCES} + ../mersenne_twister_engine.h ../random_generator.h + ../random_generator_engine.h cli.h mutator.h spirv_fuzz_mutator.h diff --git a/src/BUILD.gn b/src/BUILD.gn index 2fd70f53ef..6ccac4ac4c 100644 --- a/src/BUILD.gn +++ b/src/BUILD.gn @@ -409,9 +409,9 @@ libtint_source_set("libtint_core_all_src") { "sem/storage_texture_type.h", "sem/switch_statement.h", "sem/texture_type.h", + "sem/type.h", "sem/type_constructor.h", "sem/type_conversion.h", - "sem/type.h", "sem/type_manager.h", "sem/type_mappings.h", "sem/u32_type.h", @@ -581,12 +581,12 @@ libtint_source_set("libtint_sem_src") { "sem/switch_statement.h", "sem/texture_type.cc", "sem/texture_type.h", + "sem/type.cc", + "sem/type.h", "sem/type_constructor.cc", "sem/type_constructor.h", "sem/type_conversion.cc", "sem/type_conversion.h", - "sem/type.cc", - "sem/type.h", "sem/type_manager.cc", "sem/type_manager.h", "sem/type_mappings.h", diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0e8c98eeda..fc5d3f2fcf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1083,6 +1083,18 @@ if(${TINT_BUILD_TESTS}) ) endif() + if (${TINT_BUILD_FUZZERS}) + list(APPEND TINT_TEST_SRCS + ../fuzzers/mersenne_twister_engine.cc + ../fuzzers/mersenne_twister_engine.h + ../fuzzers/random_generator.cc + ../fuzzers/random_generator.h + ../fuzzers/random_generator_engine.cc + ../fuzzers/random_generator_engine.h + ../fuzzers/random_generator_test.cc + ) + endif() + add_executable(tint_unittests ${TINT_TEST_SRCS}) if(NOT MSVC) diff --git a/test/BUILD.gn b/test/BUILD.gn index a634ea416e..bfaa498f0e 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -169,9 +169,9 @@ tint_unittests_source_set("tint_unittests_ast_src") { "../src/ast/function_test.cc", "../src/ast/group_decoration_test.cc", "../src/ast/i32_test.cc", - "../src/ast/index_accessor_expression_test.cc", "../src/ast/identifier_expression_test.cc", "../src/ast/if_statement_test.cc", + "../src/ast/index_accessor_expression_test.cc", "../src/ast/int_literal_expression_test.cc", "../src/ast/interpolate_decoration_test.cc", "../src/ast/intrinsic_texture_helper_test.cc", @@ -213,7 +213,6 @@ tint_unittests_source_set("tint_unittests_ast_src") { ] } - tint_unittests_source_set("tint_unittests_diagnostic_src") { sources = [ "../src/diagnostic/formatter_test.cc", @@ -269,9 +268,7 @@ tint_unittests_source_set("tint_unittests_resolver_src") { "../src/resolver/var_let_test.cc", "../src/resolver/var_let_validation_test.cc", ] - deps = [ - ":tint_unittests_ast_src", - ] + deps = [ ":tint_unittests_ast_src" ] } tint_unittests_source_set("tint_unittests_sem_src") { @@ -397,9 +394,7 @@ tint_unittests_source_set("tint_unittests_spv_reader_src") { "../src/reader/spirv/usage_test.cc", ] - deps = [ - "${tint_root_dir}/src:libtint_spv_reader_src", - ] + deps = [ "${tint_root_dir}/src:libtint_spv_reader_src" ] } tint_unittests_source_set("tint_unittests_spv_writer_src") { @@ -524,9 +519,7 @@ tint_unittests_source_set("tint_unittests_wgsl_reader_src") { "../src/reader/wgsl/token_test.cc", ] - deps = [ - "${tint_root_dir}/src:libtint_wgsl_reader_src", - ] + deps = [ "${tint_root_dir}/src:libtint_wgsl_reader_src" ] } tint_unittests_source_set("tint_unittests_wgsl_writer_src") { @@ -705,9 +698,18 @@ tint_unittests_source_set("tint_unittests_core_src") { "../src/traits_test.cc", ] - deps = [ - ":tint_unittests_ast_src", - ] + deps = [ ":tint_unittests_ast_src" ] +} + +if (build_with_chromium) { + tint_unittests_source_set("tint_unittests_fuzzer_src") { + sources = [ "../fuzzers/random_generator_test.cc" ] + + deps = [ + ":tint_unittests_core_src", + "${tint_root_dir}/fuzzers:tint_fuzzer_common_src", + ] + } } source_set("tint_unittests_src") { @@ -755,6 +757,10 @@ source_set("tint_unittests_src") { deps += [ ":tint_unittests_glsl_writer_src" ] } + if (build_with_chromium) { + deps += [ ":tint_unittests_fuzzer_src" ] + } + configs += [ ":tint_unittests_config" ] if (build_with_chromium) {