// 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 #include #include "fuzzers/random_generator.h" #include "fuzzers/tint_ast_fuzzer/cli.h" #include "fuzzers/tint_ast_fuzzer/mutator.h" #include "fuzzers/tint_ast_fuzzer/override_cli_params.h" #include "fuzzers/tint_common_fuzzer.h" #include "fuzzers/transform_builder.h" #include "src/reader/wgsl/parser.h" #include "src/writer/wgsl/generator.h" namespace tint { namespace fuzzers { namespace ast_fuzzer { namespace { CliParams cli_params{}; extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { // Parse CLI parameters. `ParseCliParams` will call `exit` if some parameter // is invalid. cli_params = ParseCliParams(argc, *argv); // For some fuzz targets it is desirable to force the values of certain CLI // parameters after parsing. OverrideCliParams(cli_params); return 0; } extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, size_t max_size, unsigned seed) { Source::File file("test.wgsl", {reinterpret_cast(data), size}); auto program = reader::wgsl::Parse(&file); if (!program.IsValid()) { std::cout << "Trying to mutate an invalid program:" << std::endl << program.Diagnostics().str() << std::endl; return 0; } // Run the mutator. RandomGenerator generator(seed); ProbabilityContext probability_context(&generator); program = Mutate(std::move(program), &probability_context, cli_params.enable_all_mutations, cli_params.mutation_batch_size, nullptr); if (!program.IsValid()) { std::cout << "Mutator produced invalid WGSL:" << std::endl << " seed: " << seed << std::endl << program.Diagnostics().str() << std::endl; return 0; } auto result = writer::wgsl::Generate(&program, writer::wgsl::Options()); if (!result.success) { std::cout << "Can't generate WGSL for a valid tint::Program:" << std::endl << result.error << std::endl; return 0; } if (result.wgsl.size() > max_size) { return 0; } // No need to worry about the \0 here. The reason is that if \0 is included by // developer by mistake, it will be considered a part of the string and will // cause all sorts of strange bugs. Thus, unless `data` below is used as a raw // C string, the \0 symbol should be ignored. std::memcpy( // NOLINT - clang-tidy warns about lack of null termination. data, result.wgsl.data(), result.wgsl.size()); return result.wgsl.size(); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size == 0) { return 0; } struct Target { FuzzingTarget fuzzing_target; OutputFormat output_format; const char* name; }; Target targets[] = {{FuzzingTarget::kWgsl, OutputFormat::kWGSL, "WGSL"}, {FuzzingTarget::kHlsl, OutputFormat::kHLSL, "HLSL"}, {FuzzingTarget::kMsl, OutputFormat::kMSL, "MSL"}, {FuzzingTarget::kSpv, OutputFormat::kSpv, "SPV"}}; for (auto target : targets) { if ((target.fuzzing_target & cli_params.fuzzing_target) != target.fuzzing_target) { continue; } TransformBuilder tb(data, size); tb.AddTransform(); CommonFuzzer fuzzer(InputFormat::kWGSL, target.output_format); fuzzer.EnableInspector(); fuzzer.SetTransformManager(tb.manager(), tb.data_map()); fuzzer.Run(data, size); if (fuzzer.HasErrors()) { std::cout << "Fuzzing " << target.name << " produced an error" << std::endl; auto printer = tint::diag::Printer::create(stderr, true); tint::diag::Formatter{}.format(fuzzer.Diagnostics(), printer.get()); } } return 0; } } // namespace } // namespace ast_fuzzer } // namespace fuzzers } // namespace tint