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>
This commit is contained in:
Ryan Harrison 2021-05-06 15:43:33 +00:00 committed by Commit Bot service account
parent 4f34e7897b
commit a0174e613f
11 changed files with 215 additions and 112 deletions

View File

@ -102,6 +102,11 @@ if (build_with_chromium) {
deps = [ ":tint_fuzzer_common" ] 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") { fuzzer_test("tint_wgsl_reader_spv_writer_fuzzer") {
sources = [ "tint_wgsl_reader_spv_writer_fuzzer.cc" ] sources = [ "tint_wgsl_reader_spv_writer_fuzzer.cc" ]
deps = [ ":tint_fuzzer_common" ] deps = [ ":tint_fuzzer_common" ]
@ -196,6 +201,7 @@ if (build_with_chromium) {
":tint_renamer_fuzzer", ":tint_renamer_fuzzer",
":tint_single_entry_point_fuzzer", ":tint_single_entry_point_fuzzer",
":tint_spirv_transform_fuzzer", ":tint_spirv_transform_fuzzer",
":tint_vertex_pulling_fuzzer",
":tint_wgsl_reader_spv_writer_fuzzer", ":tint_wgsl_reader_spv_writer_fuzzer",
] ]
} }

View File

@ -39,6 +39,7 @@ if (${TINT_BUILD_WGSL_READER} AND ${TINT_BUILD_SPV_WRITER})
add_tint_fuzzer(tint_renamer_fuzzer) add_tint_fuzzer(tint_renamer_fuzzer)
add_tint_fuzzer(tint_single_entry_point_fuzzer) add_tint_fuzzer(tint_single_entry_point_fuzzer)
add_tint_fuzzer(tint_spirv_transform_fuzzer) add_tint_fuzzer(tint_spirv_transform_fuzzer)
add_tint_fuzzer(tint_vertex_pulling_fuzzer)
add_tint_fuzzer(tint_wgsl_reader_spv_writer_fuzzer) add_tint_fuzzer(tint_wgsl_reader_spv_writer_fuzzer)
endif() endif()

View File

@ -18,27 +18,17 @@ namespace tint {
namespace fuzzers { namespace fuzzers {
struct Config { struct Config {
const uint8_t* data; Config(const uint8_t* data, size_t size) : reader(data, size) {}
size_t size; Reader reader;
transform::Manager manager; transform::Manager manager;
transform::DataMap inputs; transform::DataMap inputs;
}; };
bool AddPlatformIndependentPasses(Config* config) { bool AddPlatformIndependentPasses(Config* config) {
if (!ExtractFirstIndexOffsetInputs(&config->data, &config->size, ExtractFirstIndexOffsetInputs(&config->reader, &config->inputs);
&config->inputs)) { ExtractBindingRemapperInputs(&config->reader, &config->inputs);
return false; ExtractSingleEntryPointInputs(&config->reader, &config->inputs);
} ExtractVertexPullingInputs(&config->reader, &config->inputs);
if (!ExtractBindingRemapperInputs(&config->data, &config->size,
&config->inputs)) {
return false;
}
if (!ExtractSingleEntryPointInputs(&config->data, &config->size,
&config->inputs)) {
return 0;
}
config->manager.Add<transform::BoundArrayAccessors>(); config->manager.Add<transform::BoundArrayAccessors>();
config->manager config->manager
@ -48,15 +38,14 @@ bool AddPlatformIndependentPasses(Config* config) {
config->manager.Add<transform::BindingRemapper>(); config->manager.Add<transform::BindingRemapper>();
config->manager.Add<transform::Renamer>(); config->manager.Add<transform::Renamer>();
config->manager.Add<tint::transform::SingleEntryPoint>(); 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) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
{ {
Config config; Config config(data, size);
config.data = data;
config.size = size;
if (!AddPlatformIndependentPasses(&config)) { if (!AddPlatformIndependentPasses(&config)) {
return 0; return 0;
@ -65,14 +54,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv); fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv);
fuzzer.SetTransformManager(&(config.manager), std::move(config.inputs)); 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 #if TINT_BUILD_HLSL_WRITER
{ {
Config config; Config config(data, size);
config.data = data;
config.size = size;
if (!AddPlatformIndependentPasses(&config)) { if (!AddPlatformIndependentPasses(&config)) {
return 0; return 0;
@ -83,15 +70,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kHLSL); fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kHLSL);
fuzzer.SetTransformManager(&config.manager, std::move(config.inputs)); 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 #endif // TINT_BUILD_HLSL_WRITER
#if TINT_BUILD_MSL_WRITER #if TINT_BUILD_MSL_WRITER
{ {
Config config; Config config(data, size);
config.data = data;
config.size = size;
if (!AddPlatformIndependentPasses(&config)) { if (!AddPlatformIndependentPasses(&config)) {
return 0; return 0;
@ -102,14 +87,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kMSL); fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kMSL);
fuzzer.SetTransformManager(&config.manager, std::move(config.inputs)); 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 #endif // TINT_BUILD_MSL_WRITER
#if TINT_BUILD_SPV_WRITER #if TINT_BUILD_SPV_WRITER
{ {
Config config; Config config(data, size);
config.data = data;
config.size = size;
if (!AddPlatformIndependentPasses(&config)) { if (!AddPlatformIndependentPasses(&config)) {
return 0; return 0;
@ -120,7 +103,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv); fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv);
fuzzer.SetTransformManager(&config.manager, std::move(config.inputs)); 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 #endif // TINT_BUILD_SPV_WRITER

View File

@ -20,8 +20,10 @@ namespace fuzzers {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
transform::Manager transform_manager; transform::Manager transform_manager;
transform::DataMap transform_inputs; transform::DataMap transform_inputs;
Reader r(data, size);
if (!ExtractBindingRemapperInputs(&data, &size, &transform_inputs)) { ExtractBindingRemapperInputs(&r, &transform_inputs);
if (r.failed()) {
return 0; return 0;
} }
@ -30,7 +32,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv); fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv);
fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs)); fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs));
return fuzzer.Run(data, size); return fuzzer.Run(r.data(), r.size());
} }
} // namespace fuzzers } // namespace fuzzers

View File

@ -26,6 +26,8 @@
namespace tint { namespace tint {
namespace fuzzers { namespace fuzzers {
namespace {
[[noreturn]] void TintInternalCompilerErrorReporter( [[noreturn]] void TintInternalCompilerErrorReporter(
const tint::diag::List& diagnostics) { const tint::diag::List& diagnostics) {
auto printer = tint::diag::Printer::create(stderr, true); auto printer = tint::diag::Printer::create(stderr, true);
@ -37,41 +39,72 @@ namespace fuzzers {
auto printer = tint::diag::Printer::create(stderr, true); auto printer = tint::diag::Printer::create(stderr, true);
printer->write( printer->write(
"Fuzzing detected valid input program being transformed into an invalid " "Fuzzing detected valid input program being transformed into an invalid "
"output progam", "output progam\n",
{diag::Color::kRed, true}); {diag::Color::kRed, true});
__builtin_trap(); __builtin_trap();
} }
bool ExtractBindingRemapperInputs(const uint8_t** data, transform::VertexAttributeDescriptor ExtractVertexAttributeDescriptor(
size_t* size, Reader* r) {
tint::transform::DataMap* inputs) { transform::VertexAttributeDescriptor desc;
if ((*size) < sizeof(uint8_t)) { desc.format = r->enum_class<transform::VertexFormat>(
return false; 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); void Reader::mark_failed() {
(*data) += sizeof(uint8_t); size_ = 0;
(*size) -= sizeof(uint8_t); 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 { struct Config {
uint32_t old_group; uint8_t old_group;
uint32_t old_binding; uint8_t old_binding;
uint32_t new_group; uint8_t new_group;
uint32_t new_binding; uint8_t new_binding;
ast::AccessControl::Access new_ac; ast::AccessControl::Access new_ac;
}; };
if ((*size) < count * sizeof(Config)) { std::vector<Config> configs = r->vector<Config>();
return false;
}
std::vector<Config> configs(count);
memcpy(configs.data(), *data, count * sizeof(Config));
(*data) += count * sizeof(Config);
(*size) -= count * sizeof(Config);
transform::BindingRemapper::BindingPoints binding_points; transform::BindingRemapper::BindingPoints binding_points;
transform::BindingRemapper::AccessControls access_controls; transform::BindingRemapper::AccessControls access_controls;
for (const auto& config : configs) { for (const auto& config : configs) {
@ -82,59 +115,33 @@ bool ExtractBindingRemapperInputs(const uint8_t** data,
inputs->Add<transform::BindingRemapper::Remappings>(binding_points, inputs->Add<transform::BindingRemapper::Remappings>(binding_points,
access_controls); access_controls);
return true;
} }
bool ExtractFirstIndexOffsetInputs(const uint8_t** data, void ExtractFirstIndexOffsetInputs(Reader* r,
size_t* size,
tint::transform::DataMap* inputs) { tint::transform::DataMap* inputs) {
struct Config { struct Config {
uint32_t group; uint32_t group;
uint32_t binding; uint32_t binding;
}; };
if ((*size) < sizeof(Config)) { Config config = r->read<Config>();
return false;
}
Config config;
memcpy(&config, data, sizeof(config));
(*data) += sizeof(Config);
(*size) -= sizeof(Config);
inputs->Add<tint::transform::FirstIndexOffset::BindingPoint>(config.binding, inputs->Add<tint::transform::FirstIndexOffset::BindingPoint>(config.binding,
config.group); config.group);
return true;
} }
bool ExtractSingleEntryPointInputs(const uint8_t** data, void ExtractSingleEntryPointInputs(Reader* r,
size_t* size,
tint::transform::DataMap* inputs) { tint::transform::DataMap* inputs) {
if ((*size) < sizeof(uint8_t)) { std::string input = r->string();
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);
transform::SingleEntryPoint::Config cfg(input); transform::SingleEntryPoint::Config cfg(input);
inputs->Add<transform::SingleEntryPoint::Config>(cfg); 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) 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 #if TINT_BUILD_WGSL_READER
case InputFormat::kWGSL: { case InputFormat::kWGSL: {
std::string str(reinterpret_cast<const char*>(data), size); std::string str(reinterpret_cast<const char*>(data), size);
file = std::make_unique<Source::File>("test.wgsl", str); file = std::make_unique<Source::File>("test.wgsl", str);
program = reader::wgsl::Parse(file.get()); program = reader::wgsl::Parse(file.get());
break; break;

View File

@ -15,23 +15,80 @@
#ifndef FUZZERS_TINT_COMMON_FUZZER_H_ #ifndef FUZZERS_TINT_COMMON_FUZZER_H_
#define FUZZERS_TINT_COMMON_FUZZER_H_ #define FUZZERS_TINT_COMMON_FUZZER_H_
#include <string>
#include <utility> #include <utility>
#include <vector>
#include "include/tint/tint.h" #include "include/tint/tint.h"
namespace tint { namespace tint {
namespace fuzzers { namespace fuzzers {
bool ExtractBindingRemapperInputs(const uint8_t** data, class Reader {
size_t* size, public:
tint::transform::DataMap* inputs); Reader(const uint8_t* data, size_t size);
bool ExtractFirstIndexOffsetInputs(const uint8_t** data,
size_t* size,
tint::transform::DataMap* inputs);
bool ExtractSingleEntryPointInputs(const uint8_t** data, bool failed() { return failed_; }
size_t* size, const uint8_t* data() { return data_; }
tint::transform::DataMap* inputs); 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 }; enum class InputFormat { kWGSL, kSpv, kNone };

View File

@ -20,8 +20,10 @@ namespace fuzzers {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
tint::transform::Manager transform_manager; tint::transform::Manager transform_manager;
tint::transform::DataMap transform_inputs; tint::transform::DataMap transform_inputs;
Reader r(data, size);
if (!ExtractFirstIndexOffsetInputs(&data, &size, &transform_inputs)) { ExtractFirstIndexOffsetInputs(&r, &transform_inputs);
if (r.failed()) {
return 0; 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); tint::fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv);
fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs)); fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs));
return fuzzer.Run(data, size); return fuzzer.Run(r.data(), r.size());
} }
} // namespace fuzzers } // namespace fuzzers

View File

@ -20,8 +20,10 @@ namespace fuzzers {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
tint::transform::Manager transform_manager; tint::transform::Manager transform_manager;
tint::transform::DataMap transform_inputs; tint::transform::DataMap transform_inputs;
Reader r(data, size);
if (!ExtractSingleEntryPointInputs(&data, &size, &transform_inputs)) { ExtractSingleEntryPointInputs(&r, &transform_inputs);
if (r.failed()) {
return 0; 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); tint::fuzzers::CommonFuzzer fuzzer(InputFormat::kWGSL, OutputFormat::kSpv);
fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs)); fuzzer.SetTransformManager(&transform_manager, std::move(transform_inputs));
return fuzzer.Run(data, size); return fuzzer.Run(r.data(), r.size());
} }
} // namespace fuzzers } // namespace fuzzers

View File

@ -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

View File

@ -28,6 +28,7 @@ TINT_INSTANTIATE_TYPEINFO(tint::transform::VertexPulling::Config);
namespace tint { namespace tint {
namespace transform { namespace transform {
namespace { namespace {
struct State { struct State {

View File

@ -56,12 +56,13 @@ enum class VertexFormat {
kI32, kI32,
kVec2I32, kVec2I32,
kVec3I32, kVec3I32,
kVec4I32 kVec4I32,
kLastEntry = kVec4I32
}; };
/// Describes if a vertex attributes increments with vertex index or instance /// Describes if a vertex attributes increments with vertex index or instance
/// index /// index
enum class InputStepMode { kVertex, kInstance }; enum class InputStepMode { kVertex, kInstance, kLastEntry = kInstance };
/// Describes a vertex attribute within a buffer /// Describes a vertex attribute within a buffer
struct VertexAttributeDescriptor { struct VertexAttributeDescriptor {