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 <rharrison@chromium.org>
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/fuzzers/BUILD.gn b/fuzzers/BUILD.gn
index 1aeacfd..74943fa 100644
--- a/fuzzers/BUILD.gn
+++ b/fuzzers/BUILD.gn
@@ -102,6 +102,11 @@
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 @@
":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 d5adea6..b5ef5bf 100644
--- a/fuzzers/CMakeLists.txt
+++ b/fuzzers/CMakeLists.txt
@@ -39,6 +39,7 @@
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 2e9d94c..bb096f4 100644
--- a/fuzzers/tint_all_transforms_fuzzer.cc
+++ b/fuzzers/tint_all_transforms_fuzzer.cc
@@ -18,27 +18,17 @@
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<transform::BoundArrayAccessors>();
config->manager
@@ -48,15 +38,14 @@
config->manager.Add<transform::BindingRemapper>();
config->manager.Add<transform::Renamer>();
config->manager.Add<tint::transform::SingleEntryPoint>();
+ config->manager.Add<tint::transform::VertexPulling>();
- 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 @@
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 @@
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 @@
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 @@
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 492174d..b1b566e 100644
--- a/fuzzers/tint_binding_remapper_fuzzer.cc
+++ b/fuzzers/tint_binding_remapper_fuzzer.cc
@@ -20,8 +20,10 @@
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 @@
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 b47a327..38d4a7c 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 @@
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<transform::VertexFormat>(
+ static_cast<uint8_t>(transform::VertexFormat::kLastEntry) + 1);
+ desc.offset = r->read<uint64_t>();
+ desc.shader_location = r->read<uint32_t>();
+ return desc;
+}
+
+transform::VertexBufferLayoutDescriptor ExtractVertexBufferLayoutDescriptor(
+ Reader* r) {
+ transform::VertexBufferLayoutDescriptor desc;
+ desc.array_stride = r->read<uint64_t>();
+ desc.step_mode = r->enum_class<transform::InputStepMode>(
+ static_cast<uint8_t>(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<uint8_t>();
+ if (failed_ || size_ < count) {
+ mark_failed();
+ return "";
}
+ std::string out(data_, data_ + count);
+ data_ += count;
+ size_ -= count;
+ return out;
+}
- auto count = *reinterpret_cast<const uint8_t*>(*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<Config> configs(count);
-
- memcpy(configs.data(), *data, count * sizeof(Config));
-
- (*data) += count * sizeof(Config);
- (*size) -= count * sizeof(Config);
-
+ std::vector<Config> configs = r->vector<Config>();
transform::BindingRemapper::BindingPoints binding_points;
transform::BindingRemapper::AccessControls access_controls;
for (const auto& config : configs) {
@@ -82,59 +115,33 @@
inputs->Add<transform::BindingRemapper::Remappings>(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<Config>();
inputs->Add<tint::transform::FirstIndexOffset::BindingPoint>(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<const uint8_t*>(*data);
- (*data) += sizeof(uint8_t);
- (*size) -= sizeof(uint8_t);
-
- if ((*size) < count) {
- return false;
- }
-
- auto* c = reinterpret_cast<const char*>(*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<transform::SingleEntryPoint::Config>(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<uint32_t>();
+ inputs->Add<transform::VertexPulling::Config>(cfg);
}
CommonFuzzer::CommonFuzzer(InputFormat input, OutputFormat output)
@@ -158,7 +165,6 @@
#if TINT_BUILD_WGSL_READER
case InputFormat::kWGSL: {
std::string str(reinterpret_cast<const char*>(data), size);
-
file = std::make_unique<Source::File>("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 7c3cd6b..3b9c137 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 <string>
#include <utility>
+#include <vector>
#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 <typename T>
+ T read() {
+ T out{};
+ read(&out, sizeof(T));
+ return out;
+ }
+
+ std::string string();
+
+ template <typename T>
+ std::vector<T> vector() {
+ auto count = read<uint8_t>();
+ if (failed_ || size_ < count) {
+ mark_failed();
+ return {};
+ }
+ std::vector<T> out(count);
+ memcpy(out.data(), data_, count * sizeof(T));
+ data_ += count * sizeof(T);
+ size_ -= count * sizeof(T);
+ return out;
+ }
+
+ template <typename T>
+ std::vector<T> vector(T (*extract)(Reader*)) {
+ auto count = read<uint8_t>();
+ if (size_ < count) {
+ mark_failed();
+ return {};
+ }
+ std::vector<T> out(count);
+ for (uint8_t i = 0; i < count; i++) {
+ out[i] = extract(this);
+ }
+ return out;
+ }
+ template <typename T>
+ T enum_class(uint8_t count) {
+ auto val = read<uint8_t>();
+ return static_cast<T>(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 8f441f1..78d6164 100644
--- a/fuzzers/tint_first_index_offset_fuzzer.cc
+++ b/fuzzers/tint_first_index_offset_fuzzer.cc
@@ -20,8 +20,10 @@
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 @@
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 906dac4..41a6aa3 100644
--- a/fuzzers/tint_single_entry_point_fuzzer.cc
+++ b/fuzzers/tint_single_entry_point_fuzzer.cc
@@ -20,8 +20,10 @@
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 @@
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 0000000..62a68ab
--- /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::transform::VertexPulling>();
+
+ 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 88b16a4..31523b4 100644
--- a/src/transform/vertex_pulling.cc
+++ b/src/transform/vertex_pulling.cc
@@ -28,6 +28,7 @@
namespace tint {
namespace transform {
+
namespace {
struct State {
diff --git a/src/transform/vertex_pulling.h b/src/transform/vertex_pulling.h
index 7dded37..15ea211 100644
--- a/src/transform/vertex_pulling.h
+++ b/src/transform/vertex_pulling.h
@@ -56,12 +56,13 @@
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 {