mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-14 03:11:29 +00:00
This toggle controls if the fuzzer will throw a fatal error in the case that the shader becomes invalid. Currently the fuzzers do no guarantee that the options that are provided are correct/valid, so there are many uninteresting cases that become invalid due to the limited nature of the fuzzers, not due to bugs in the code. The default off state of this toggle will suppress this noise. Once https://bugs.chromium.org/p/tint/issues/detail?id=1356 is implemented this toggle can be default on. BUG=tint:1357,chromium:1294533 Change-Id: I7170e5a30691105c97e20d8337aadf81ac2bc3bc Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/79840 Reviewed-by: Ben Clayton <bclayton@google.com> Auto-Submit: Ryan Harrison <rharrison@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Ryan Harrison <rharrison@chromium.org>
354 lines
12 KiB
C++
354 lines
12 KiB
C++
// 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/tint_common_fuzzer.h"
|
|
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#if TINT_BUILD_SPV_READER
|
|
#include "spirv-tools/libspirv.hpp"
|
|
#endif // TINT_BUILD_SPV_READER
|
|
|
|
#include "src/ast/module.h"
|
|
#include "src/diagnostic/formatter.h"
|
|
#include "src/program.h"
|
|
#include "src/utils/hash.h"
|
|
|
|
namespace tint {
|
|
namespace fuzzers {
|
|
|
|
namespace {
|
|
|
|
// A macro is used to avoid FATAL_ERROR creating its own stack frame. This leads
|
|
// to better de-duplication of bug reports, because ClusterFuzz only uses the
|
|
// top few stack frames for de-duplication, and a FATAL_ERROR stack frame
|
|
// provides no useful information.
|
|
#define FATAL_ERROR(diags, msg_string) \
|
|
do { \
|
|
std::string msg = msg_string; \
|
|
auto printer = tint::diag::Printer::create(stderr, true); \
|
|
if (!msg.empty()) { \
|
|
printer->write(msg + "\n", {diag::Color::kRed, true}); \
|
|
} \
|
|
tint::diag::Formatter().format(diags, printer.get()); \
|
|
__builtin_trap(); \
|
|
} while (false)
|
|
|
|
[[noreturn]] void TintInternalCompilerErrorReporter(
|
|
const tint::diag::List& diagnostics) {
|
|
FATAL_ERROR(diagnostics, "");
|
|
}
|
|
|
|
// Wrapping in a macro, so it can be a one-liner in the code, but not
|
|
// introduce another level in the stack trace. This will help with de-duping
|
|
// ClusterFuzz issues.
|
|
#define CHECK_INSPECTOR(program, inspector) \
|
|
do { \
|
|
if ((inspector).has_error()) { \
|
|
if (!enforce_validity) { \
|
|
return; \
|
|
} \
|
|
FATAL_ERROR((program)->Diagnostics(), \
|
|
"Inspector failed: " + (inspector).error()); \
|
|
} \
|
|
} while (false)
|
|
|
|
// Wrapping in a macro to make code more readable and help with issue de-duping.
|
|
#define VALIDITY_ERROR(diags, msg_string) \
|
|
do { \
|
|
if (!enforce_validity) { \
|
|
return 0; \
|
|
} \
|
|
FATAL_ERROR(diags, msg_string); \
|
|
} while (false)
|
|
|
|
bool SPIRVToolsValidationCheck(const tint::Program& program,
|
|
const std::vector<uint32_t>& spirv) {
|
|
spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_1);
|
|
const tint::diag::List& diags = program.Diagnostics();
|
|
tools.SetMessageConsumer([diags](spv_message_level_t, const char*,
|
|
const spv_position_t& pos, const char* msg) {
|
|
std::stringstream out;
|
|
out << "Unexpected spirv-val error:\n"
|
|
<< (pos.line + 1) << ":" << (pos.column + 1) << ": " << msg
|
|
<< std::endl;
|
|
|
|
auto printer = tint::diag::Printer::create(stderr, true);
|
|
printer->write(out.str(), {diag::Color::kYellow, false});
|
|
tint::diag::Formatter().format(diags, printer.get());
|
|
});
|
|
|
|
return tools.Validate(spirv.data(), spirv.size(),
|
|
spvtools::ValidatorOptions());
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void GenerateSpirvOptions(DataBuilder* b, writer::spirv::Options* options) {
|
|
*options = b->build<writer::spirv::Options>();
|
|
}
|
|
|
|
void GenerateWgslOptions(DataBuilder* b, writer::wgsl::Options* options) {
|
|
*options = b->build<writer::wgsl::Options>();
|
|
}
|
|
|
|
void GenerateHlslOptions(DataBuilder* b, writer::hlsl::Options* options) {
|
|
*options = b->build<writer::hlsl::Options>();
|
|
}
|
|
|
|
void GenerateMslOptions(DataBuilder* b, writer::msl::Options* options) {
|
|
*options = b->build<writer::msl::Options>();
|
|
}
|
|
|
|
CommonFuzzer::CommonFuzzer(InputFormat input, OutputFormat output)
|
|
: input_(input), output_(output) {}
|
|
|
|
CommonFuzzer::~CommonFuzzer() = default;
|
|
|
|
int CommonFuzzer::Run(const uint8_t* data, size_t size) {
|
|
tint::SetInternalCompilerErrorReporter(&TintInternalCompilerErrorReporter);
|
|
|
|
#if TINT_BUILD_WGSL_WRITER
|
|
tint::Program::printer = [](const tint::Program* program) {
|
|
auto result = tint::writer::wgsl::Generate(program, {});
|
|
if (!result.error.empty()) {
|
|
return "error: " + result.error;
|
|
}
|
|
return result.wgsl;
|
|
};
|
|
#endif // TINT_BUILD_WGSL_WRITER
|
|
|
|
Program program;
|
|
|
|
#if TINT_BUILD_SPV_READER
|
|
std::vector<uint32_t> spirv_input(size / sizeof(uint32_t));
|
|
|
|
#endif // TINT_BUILD_SPV_READER
|
|
|
|
#if TINT_BUILD_WGSL_READER || TINT_BUILD_SPV_READER
|
|
auto dump_input_data = [&](auto& content, const char* extension) {
|
|
size_t hash = utils::Hash(content);
|
|
auto filename = "fuzzer_input_" + std::to_string(hash) + extension; //
|
|
std::ofstream fout(filename, std::ios::binary);
|
|
fout.write(reinterpret_cast<const char*>(data),
|
|
static_cast<std::streamsize>(size));
|
|
std::cout << "Dumped input data to " << filename << std::endl;
|
|
};
|
|
#endif
|
|
|
|
switch (input_) {
|
|
#if TINT_BUILD_WGSL_READER
|
|
case InputFormat::kWGSL: {
|
|
// Clear any existing diagnostics, as these will hold pointers to file_,
|
|
// which we are about to release.
|
|
diagnostics_ = {};
|
|
std::string str(reinterpret_cast<const char*>(data), size);
|
|
file_ = std::make_unique<Source::File>("test.wgsl", str);
|
|
if (dump_input_) {
|
|
dump_input_data(str, ".wgsl");
|
|
}
|
|
program = reader::wgsl::Parse(file_.get());
|
|
break;
|
|
}
|
|
#endif // TINT_BUILD_WGSL_READER
|
|
#if TINT_BUILD_SPV_READER
|
|
case InputFormat::kSpv: {
|
|
// `spirv_input` has been initialized with the capacity to store `size /
|
|
// sizeof(uint32_t)` uint32_t values. If `size` is not a multiple of
|
|
// sizeof(uint32_t) then not all of `data` can be copied into
|
|
// `spirv_input`, and any trailing bytes are discarded.
|
|
std::memcpy(spirv_input.data(), data,
|
|
spirv_input.size() * sizeof(uint32_t));
|
|
if (spirv_input.empty()) {
|
|
return 0;
|
|
}
|
|
if (dump_input_) {
|
|
dump_input_data(spirv_input, ".spv");
|
|
}
|
|
program = reader::spirv::Parse(spirv_input);
|
|
break;
|
|
}
|
|
#endif // TINT_BUILD_SPV_READER
|
|
}
|
|
|
|
if (!program.IsValid()) {
|
|
diagnostics_ = program.Diagnostics();
|
|
return 0;
|
|
}
|
|
|
|
#if TINT_BUILD_SPV_READER
|
|
if (input_ == InputFormat::kSpv &&
|
|
!SPIRVToolsValidationCheck(program, spirv_input)) {
|
|
FATAL_ERROR(
|
|
program.Diagnostics(),
|
|
"Fuzzing detected invalid input spirv not being caught by Tint");
|
|
}
|
|
#endif // TINT_BUILD_SPV_READER
|
|
|
|
RunInspector(&program);
|
|
diagnostics_ = program.Diagnostics();
|
|
|
|
if (transform_manager_) {
|
|
auto out = transform_manager_->Run(&program, *transform_inputs_);
|
|
if (!out.program.IsValid()) {
|
|
// Transforms can produce error messages for bad input.
|
|
// Catch ICEs and errors from non transform systems.
|
|
for (const auto& diag : out.program.Diagnostics()) {
|
|
if (diag.severity > diag::Severity::Error ||
|
|
diag.system != diag::System::Transform) {
|
|
VALIDITY_ERROR(program.Diagnostics(),
|
|
"Fuzzing detected valid input program being "
|
|
"transformed into an invalid output program");
|
|
}
|
|
}
|
|
}
|
|
|
|
program = std::move(out.program);
|
|
RunInspector(&program);
|
|
}
|
|
|
|
switch (output_) {
|
|
case OutputFormat::kWGSL: {
|
|
#if TINT_BUILD_WGSL_WRITER
|
|
auto result = writer::wgsl::Generate(&program, options_wgsl_);
|
|
generated_wgsl_ = std::move(result.wgsl);
|
|
if (!result.success) {
|
|
VALIDITY_ERROR(
|
|
program.Diagnostics(),
|
|
"WGSL writer errored on validated input:\n" + result.error);
|
|
}
|
|
#endif // TINT_BUILD_WGSL_WRITER
|
|
break;
|
|
}
|
|
case OutputFormat::kSpv: {
|
|
#if TINT_BUILD_SPV_WRITER
|
|
auto result = writer::spirv::Generate(&program, options_spirv_);
|
|
generated_spirv_ = std::move(result.spirv);
|
|
if (!result.success) {
|
|
VALIDITY_ERROR(
|
|
program.Diagnostics(),
|
|
"SPIR-V writer errored on validated input:\n" + result.error);
|
|
}
|
|
|
|
if (!SPIRVToolsValidationCheck(program, generated_spirv_)) {
|
|
VALIDITY_ERROR(program.Diagnostics(),
|
|
"Fuzzing detected invalid spirv being emitted by Tint");
|
|
}
|
|
|
|
#endif // TINT_BUILD_SPV_WRITER
|
|
break;
|
|
}
|
|
case OutputFormat::kHLSL: {
|
|
#if TINT_BUILD_HLSL_WRITER
|
|
auto result = writer::hlsl::Generate(&program, options_hlsl_);
|
|
generated_hlsl_ = std::move(result.hlsl);
|
|
if (!result.success) {
|
|
VALIDITY_ERROR(
|
|
program.Diagnostics(),
|
|
"HLSL writer errored on validated input:\n" + result.error);
|
|
}
|
|
#endif // TINT_BUILD_HLSL_WRITER
|
|
break;
|
|
}
|
|
case OutputFormat::kMSL: {
|
|
#if TINT_BUILD_MSL_WRITER
|
|
auto result = writer::msl::Generate(&program, options_msl_);
|
|
generated_msl_ = std::move(result.msl);
|
|
if (!result.success) {
|
|
VALIDITY_ERROR(
|
|
program.Diagnostics(),
|
|
"MSL writer errored on validated input:\n" + result.error);
|
|
}
|
|
#endif // TINT_BUILD_MSL_WRITER
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CommonFuzzer::RunInspector(Program* program) {
|
|
inspector::Inspector inspector(program);
|
|
diagnostics_ = program->Diagnostics();
|
|
|
|
auto entry_points = inspector.GetEntryPoints();
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
auto constant_ids = inspector.GetConstantIDs();
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
auto constant_name_to_id = inspector.GetConstantNameToIdMap();
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
for (auto& ep : entry_points) {
|
|
inspector.GetRemappedNameForEntryPoint(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetStorageSize(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetResourceBindings(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetUniformBufferResourceBindings(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetStorageBufferResourceBindings(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetReadOnlyStorageBufferResourceBindings(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetSamplerResourceBindings(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetComparisonSamplerResourceBindings(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetSampledTextureResourceBindings(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetMultisampledTextureResourceBindings(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetWriteOnlyStorageTextureResourceBindings(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetDepthTextureResourceBindings(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetDepthMultisampledTextureResourceBindings(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetExternalTextureResourceBindings(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetSamplerTextureUses(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
|
|
inspector.GetWorkgroupStorageSize(ep.name);
|
|
CHECK_INSPECTOR(program, inspector);
|
|
}
|
|
}
|
|
|
|
} // namespace fuzzers
|
|
} // namespace tint
|