From a0174e613f899979109957fe724629529be9a834 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Thu, 6 May 2021 15:43:33 +0000 Subject: [PATCH] Add fuzzing for transform::VertexPulling Includes a significant refactoring of helper functions in tint_common_fuzzer.cc/.h BUG=tint:722 Change-Id: I1fdab0113bae02c4a0bf8da0d1b7729f05a2fc5b Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49902 Commit-Queue: Ryan Harrison Auto-Submit: Ryan Harrison Reviewed-by: Ben Clayton --- fuzzers/BUILD.gn | 6 + fuzzers/CMakeLists.txt | 1 + fuzzers/tint_all_transforms_fuzzer.cc | 49 +++----- fuzzers/tint_binding_remapper_fuzzer.cc | 6 +- fuzzers/tint_common_fuzzer.cc | 130 +++++++++++----------- fuzzers/tint_common_fuzzer.h | 75 +++++++++++-- fuzzers/tint_first_index_offset_fuzzer.cc | 6 +- fuzzers/tint_single_entry_point_fuzzer.cc | 6 +- fuzzers/tint_vertex_pulling_fuzzer.cc | 42 +++++++ src/transform/vertex_pulling.cc | 1 + src/transform/vertex_pulling.h | 5 +- 11 files changed, 215 insertions(+), 112 deletions(-) create mode 100644 fuzzers/tint_vertex_pulling_fuzzer.cc diff --git a/fuzzers/BUILD.gn b/fuzzers/BUILD.gn index 1aeacfdcde..74943facba 100644 --- a/fuzzers/BUILD.gn +++ b/fuzzers/BUILD.gn @@ -102,6 +102,11 @@ if (build_with_chromium) { deps = [ ":tint_fuzzer_common" ] } + fuzzer_test("tint_vertex_pulling_fuzzer") { + sources = [ "tint_vertex_pulling_fuzzer.cc" ] + deps = [ ":tint_fuzzer_common" ] + } + fuzzer_test("tint_wgsl_reader_spv_writer_fuzzer") { sources = [ "tint_wgsl_reader_spv_writer_fuzzer.cc" ] deps = [ ":tint_fuzzer_common" ] @@ -196,6 +201,7 @@ if (build_with_chromium) { ":tint_renamer_fuzzer", ":tint_single_entry_point_fuzzer", ":tint_spirv_transform_fuzzer", + ":tint_vertex_pulling_fuzzer", ":tint_wgsl_reader_spv_writer_fuzzer", ] } diff --git a/fuzzers/CMakeLists.txt b/fuzzers/CMakeLists.txt index d5adea6975..b5ef5bf4e7 100644 --- a/fuzzers/CMakeLists.txt +++ b/fuzzers/CMakeLists.txt @@ -39,6 +39,7 @@ if (${TINT_BUILD_WGSL_READER} AND ${TINT_BUILD_SPV_WRITER}) add_tint_fuzzer(tint_renamer_fuzzer) add_tint_fuzzer(tint_single_entry_point_fuzzer) add_tint_fuzzer(tint_spirv_transform_fuzzer) + add_tint_fuzzer(tint_vertex_pulling_fuzzer) add_tint_fuzzer(tint_wgsl_reader_spv_writer_fuzzer) endif() diff --git a/fuzzers/tint_all_transforms_fuzzer.cc b/fuzzers/tint_all_transforms_fuzzer.cc index 2e9d94c420..bb096f4312 100644 --- a/fuzzers/tint_all_transforms_fuzzer.cc +++ b/fuzzers/tint_all_transforms_fuzzer.cc @@ -18,27 +18,17 @@ namespace tint { namespace fuzzers { struct Config { - const uint8_t* data; - size_t size; + Config(const uint8_t* data, size_t size) : reader(data, size) {} + Reader reader; transform::Manager manager; transform::DataMap inputs; }; bool AddPlatformIndependentPasses(Config* config) { - if (!ExtractFirstIndexOffsetInputs(&config->data, &config->size, - &config->inputs)) { - return false; - } - - if (!ExtractBindingRemapperInputs(&config->data, &config->size, - &config->inputs)) { - return false; - } - - if (!ExtractSingleEntryPointInputs(&config->data, &config->size, - &config->inputs)) { - return 0; - } + ExtractFirstIndexOffsetInputs(&config->reader, &config->inputs); + ExtractBindingRemapperInputs(&config->reader, &config->inputs); + ExtractSingleEntryPointInputs(&config->reader, &config->inputs); + ExtractVertexPullingInputs(&config->reader, &config->inputs); config->manager.Add(); config->manager @@ -48,15 +38,14 @@ bool AddPlatformIndependentPasses(Config* config) { config->manager.Add(); config->manager.Add(); config->manager.Add(); + config->manager.Add(); - return true; + return !config->reader.failed(); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { { - Config config; - config.data = data; - config.size = size; + Config config(data, size); if (!AddPlatformIndependentPasses(&config)) { return 0; @@ -65,14 +54,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv); fuzzer.SetTransformManager(&(config.manager), std::move(config.inputs)); - fuzzer.Run(config.data, config.size); + fuzzer.Run(config.reader.data(), config.reader.size()); } #if TINT_BUILD_HLSL_WRITER { - Config config; - config.data = data; - config.size = size; + Config config(data, size); if (!AddPlatformIndependentPasses(&config)) { return 0; @@ -83,15 +70,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kHLSL); fuzzer.SetTransformManager(&config.manager, std::move(config.inputs)); - fuzzer.Run(config.data, config.size); + fuzzer.Run(config.reader.data(), config.reader.size()); } #endif // TINT_BUILD_HLSL_WRITER #if TINT_BUILD_MSL_WRITER { - Config config; - config.data = data; - config.size = size; + Config config(data, size); if (!AddPlatformIndependentPasses(&config)) { return 0; @@ -102,14 +87,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kMSL); fuzzer.SetTransformManager(&config.manager, std::move(config.inputs)); - fuzzer.Run(config.data, config.size); + fuzzer.Run(config.reader.data(), config.reader.size()); } #endif // TINT_BUILD_MSL_WRITER #if TINT_BUILD_SPV_WRITER { - Config config; - config.data = data; - config.size = size; + Config config(data, size); if (!AddPlatformIndependentPasses(&config)) { return 0; @@ -120,7 +103,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv); fuzzer.SetTransformManager(&config.manager, std::move(config.inputs)); - fuzzer.Run(config.data, config.size); + fuzzer.Run(config.reader.data(), config.reader.size()); } #endif // TINT_BUILD_SPV_WRITER diff --git a/fuzzers/tint_binding_remapper_fuzzer.cc b/fuzzers/tint_binding_remapper_fuzzer.cc index 492174d5a2..b1b566e9f8 100644 --- a/fuzzers/tint_binding_remapper_fuzzer.cc +++ b/fuzzers/tint_binding_remapper_fuzzer.cc @@ -20,8 +20,10 @@ namespace fuzzers { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { transform::Manager transform_manager; transform::DataMap transform_inputs; + Reader r(data, size); - if (!ExtractBindingRemapperInputs(&data, &size, &transform_inputs)) { + ExtractBindingRemapperInputs(&r, &transform_inputs); + if (r.failed()) { return 0; } @@ -30,7 +32,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv); fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs)); - return fuzzer.Run(data, size); + return fuzzer.Run(r.data(), r.size()); } } // namespace fuzzers diff --git a/fuzzers/tint_common_fuzzer.cc b/fuzzers/tint_common_fuzzer.cc index b47a3271bc..38d4a7c73c 100644 --- a/fuzzers/tint_common_fuzzer.cc +++ b/fuzzers/tint_common_fuzzer.cc @@ -26,6 +26,8 @@ namespace tint { namespace fuzzers { +namespace { + [[noreturn]] void TintInternalCompilerErrorReporter( const tint::diag::List& diagnostics) { auto printer = tint::diag::Printer::create(stderr, true); @@ -37,41 +39,72 @@ namespace fuzzers { auto printer = tint::diag::Printer::create(stderr, true); printer->write( "Fuzzing detected valid input program being transformed into an invalid " - "output progam", + "output progam\n", {diag::Color::kRed, true}); __builtin_trap(); } -bool ExtractBindingRemapperInputs(const uint8_t** data, - size_t* size, - tint::transform::DataMap* inputs) { - if ((*size) < sizeof(uint8_t)) { - return false; +transform::VertexAttributeDescriptor ExtractVertexAttributeDescriptor( + Reader* r) { + transform::VertexAttributeDescriptor desc; + desc.format = r->enum_class( + static_cast(transform::VertexFormat::kLastEntry) + 1); + desc.offset = r->read(); + desc.shader_location = r->read(); + return desc; +} + +transform::VertexBufferLayoutDescriptor ExtractVertexBufferLayoutDescriptor( + Reader* r) { + transform::VertexBufferLayoutDescriptor desc; + desc.array_stride = r->read(); + desc.step_mode = r->enum_class( + static_cast(transform::InputStepMode::kLastEntry) + 1); + desc.attributes = r->vector(ExtractVertexAttributeDescriptor); + return desc; +} + +} // namespace + +Reader::Reader(const uint8_t* data, size_t size) : data_(data), size_(size) {} + +std::string Reader::string() { + auto count = read(); + if (failed_ || size_ < count) { + mark_failed(); + return ""; } + std::string out(data_, data_ + count); + data_ += count; + size_ -= count; + return out; +} - auto count = *reinterpret_cast(*data); - (*data) += sizeof(uint8_t); - (*size) -= sizeof(uint8_t); +void Reader::mark_failed() { + size_ = 0; + failed_ = true; +} +void Reader::read(void* out, size_t n) { + if (n > size_) { + mark_failed(); + return; + } + memcpy(&out, data_, n); + data_ += n; + size_ -= n; +} + +void ExtractBindingRemapperInputs(Reader* r, tint::transform::DataMap* inputs) { struct Config { - uint32_t old_group; - uint32_t old_binding; - uint32_t new_group; - uint32_t new_binding; + uint8_t old_group; + uint8_t old_binding; + uint8_t new_group; + uint8_t new_binding; ast::AccessControl::Access new_ac; }; - if ((*size) < count * sizeof(Config)) { - return false; - } - - std::vector configs(count); - - memcpy(configs.data(), *data, count * sizeof(Config)); - - (*data) += count * sizeof(Config); - (*size) -= count * sizeof(Config); - + std::vector configs = r->vector(); transform::BindingRemapper::BindingPoints binding_points; transform::BindingRemapper::AccessControls access_controls; for (const auto& config : configs) { @@ -82,59 +115,33 @@ bool ExtractBindingRemapperInputs(const uint8_t** data, inputs->Add(binding_points, access_controls); - - return true; } -bool ExtractFirstIndexOffsetInputs(const uint8_t** data, - size_t* size, +void ExtractFirstIndexOffsetInputs(Reader* r, tint::transform::DataMap* inputs) { struct Config { uint32_t group; uint32_t binding; }; - if ((*size) < sizeof(Config)) { - return false; - } - - Config config; - memcpy(&config, data, sizeof(config)); - - (*data) += sizeof(Config); - (*size) -= sizeof(Config); - + Config config = r->read(); inputs->Add(config.binding, config.group); - - return true; } -bool ExtractSingleEntryPointInputs(const uint8_t** data, - size_t* size, +void ExtractSingleEntryPointInputs(Reader* r, tint::transform::DataMap* inputs) { - if ((*size) < sizeof(uint8_t)) { - return false; - } - - auto count = *reinterpret_cast(*data); - (*data) += sizeof(uint8_t); - (*size) -= sizeof(uint8_t); - - if ((*size) < count) { - return false; - } - - auto* c = reinterpret_cast(*data); - std::string input(c, c + count); - - (*data) += count * sizeof(char); - (*size) -= count * sizeof(char); - + std::string input = r->string(); transform::SingleEntryPoint::Config cfg(input); inputs->Add(cfg); +} - return true; +void ExtractVertexPullingInputs(Reader* r, tint::transform::DataMap* inputs) { + transform::VertexPulling::Config cfg; + cfg.entry_point_name = r->string(); + cfg.vertex_state = r->vector(ExtractVertexBufferLayoutDescriptor); + cfg.pulling_group = r->read(); + inputs->Add(cfg); } CommonFuzzer::CommonFuzzer(InputFormat input, OutputFormat output) @@ -158,7 +165,6 @@ int CommonFuzzer::Run(const uint8_t* data, size_t size) { #if TINT_BUILD_WGSL_READER case InputFormat::kWGSL: { std::string str(reinterpret_cast(data), size); - file = std::make_unique("test.wgsl", str); program = reader::wgsl::Parse(file.get()); break; diff --git a/fuzzers/tint_common_fuzzer.h b/fuzzers/tint_common_fuzzer.h index 7c3cd6b79f..3b9c1379c5 100644 --- a/fuzzers/tint_common_fuzzer.h +++ b/fuzzers/tint_common_fuzzer.h @@ -15,23 +15,80 @@ #ifndef FUZZERS_TINT_COMMON_FUZZER_H_ #define FUZZERS_TINT_COMMON_FUZZER_H_ +#include #include +#include #include "include/tint/tint.h" namespace tint { namespace fuzzers { -bool ExtractBindingRemapperInputs(const uint8_t** data, - size_t* size, - tint::transform::DataMap* inputs); -bool ExtractFirstIndexOffsetInputs(const uint8_t** data, - size_t* size, - tint::transform::DataMap* inputs); +class Reader { + public: + Reader(const uint8_t* data, size_t size); -bool ExtractSingleEntryPointInputs(const uint8_t** data, - size_t* size, - tint::transform::DataMap* inputs); + bool failed() { return failed_; } + const uint8_t* data() { return data_; } + size_t size() { return size_; } + + template + T read() { + T out{}; + read(&out, sizeof(T)); + return out; + } + + std::string string(); + + template + std::vector vector() { + auto count = read(); + if (failed_ || size_ < count) { + mark_failed(); + return {}; + } + std::vector out(count); + memcpy(out.data(), data_, count * sizeof(T)); + data_ += count * sizeof(T); + size_ -= count * sizeof(T); + return out; + } + + template + std::vector vector(T (*extract)(Reader*)) { + auto count = read(); + if (size_ < count) { + mark_failed(); + return {}; + } + std::vector out(count); + for (uint8_t i = 0; i < count; i++) { + out[i] = extract(this); + } + return out; + } + template + T enum_class(uint8_t count) { + auto val = read(); + return static_cast(val % count); + } + + private: + void mark_failed(); + void read(void* out, size_t n); + + const uint8_t* data_; + size_t size_; + bool failed_ = false; +}; + +void ExtractBindingRemapperInputs(Reader* r, tint::transform::DataMap* inputs); +void ExtractFirstIndexOffsetInputs(Reader* r, tint::transform::DataMap* inputs); + +void ExtractSingleEntryPointInputs(Reader* r, tint::transform::DataMap* inputs); + +void ExtractVertexPullingInputs(Reader* r, tint::transform::DataMap* inputs); enum class InputFormat { kWGSL, kSpv, kNone }; diff --git a/fuzzers/tint_first_index_offset_fuzzer.cc b/fuzzers/tint_first_index_offset_fuzzer.cc index 8f441f1855..78d61646a6 100644 --- a/fuzzers/tint_first_index_offset_fuzzer.cc +++ b/fuzzers/tint_first_index_offset_fuzzer.cc @@ -20,8 +20,10 @@ namespace fuzzers { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { tint::transform::Manager transform_manager; tint::transform::DataMap transform_inputs; + Reader r(data, size); - if (!ExtractFirstIndexOffsetInputs(&data, &size, &transform_inputs)) { + ExtractFirstIndexOffsetInputs(&r, &transform_inputs); + if (r.failed()) { return 0; } @@ -30,7 +32,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { tint::fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv); fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs)); - return fuzzer.Run(data, size); + return fuzzer.Run(r.data(), r.size()); } } // namespace fuzzers diff --git a/fuzzers/tint_single_entry_point_fuzzer.cc b/fuzzers/tint_single_entry_point_fuzzer.cc index 906dac4bbc..41a6aa3e0e 100644 --- a/fuzzers/tint_single_entry_point_fuzzer.cc +++ b/fuzzers/tint_single_entry_point_fuzzer.cc @@ -20,8 +20,10 @@ namespace fuzzers { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { tint::transform::Manager transform_manager; tint::transform::DataMap transform_inputs; + Reader r(data, size); - if (!ExtractSingleEntryPointInputs(&data, &size, &transform_inputs)) { + ExtractSingleEntryPointInputs(&r, &transform_inputs); + if (r.failed()) { return 0; } @@ -30,7 +32,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { tint::fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv); fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs)); - return fuzzer.Run(data, size); + return fuzzer.Run(r.data(), r.size()); } } // namespace fuzzers diff --git a/fuzzers/tint_vertex_pulling_fuzzer.cc b/fuzzers/tint_vertex_pulling_fuzzer.cc new file mode 100644 index 0000000000..62a68abc9b --- /dev/null +++ b/fuzzers/tint_vertex_pulling_fuzzer.cc @@ -0,0 +1,42 @@ +// 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. + +// TODO(tint:753): Remove this fuzzer once that transform is only +// being used from sanitizers. + +#include "fuzzers/tint_common_fuzzer.h" + +namespace tint { +namespace fuzzers { + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + tint::transform::Manager transform_manager; + tint::transform::DataMap transform_inputs; + Reader r(data, size); + + ExtractVertexPullingInputs(&r, &transform_inputs); + if (r.failed()) { + return 0; + } + + transform_manager.Add(); + + tint::fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv); + fuzzer.SetTransformManager(&transform_manager, {}); + + return fuzzer.Run(r.data(), r.size()); +} + +} // namespace fuzzers +} // namespace tint diff --git a/src/transform/vertex_pulling.cc b/src/transform/vertex_pulling.cc index 88b16a4186..31523b4767 100644 --- a/src/transform/vertex_pulling.cc +++ b/src/transform/vertex_pulling.cc @@ -28,6 +28,7 @@ TINT_INSTANTIATE_TYPEINFO(tint::transform::VertexPulling::Config); namespace tint { namespace transform { + namespace { struct State { diff --git a/src/transform/vertex_pulling.h b/src/transform/vertex_pulling.h index 7dded37dee..15ea211f00 100644 --- a/src/transform/vertex_pulling.h +++ b/src/transform/vertex_pulling.h @@ -56,12 +56,13 @@ enum class VertexFormat { kI32, kVec2I32, kVec3I32, - kVec4I32 + kVec4I32, + kLastEntry = kVec4I32 }; /// Describes if a vertex attributes increments with vertex index or instance /// index -enum class InputStepMode { kVertex, kInstance }; +enum class InputStepMode { kVertex, kInstance, kLastEntry = kInstance }; /// Describes a vertex attribute within a buffer struct VertexAttributeDescriptor {