1 // Copyright 2021 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <cstddef>
16 #include <cstdint>
17
18 #include "fuzzers/random_generator.h"
19 #include "fuzzers/tint_ast_fuzzer/cli.h"
20 #include "fuzzers/tint_ast_fuzzer/mutator.h"
21 #include "fuzzers/tint_ast_fuzzer/override_cli_params.h"
22 #include "fuzzers/tint_common_fuzzer.h"
23 #include "fuzzers/transform_builder.h"
24
25 #include "src/reader/wgsl/parser.h"
26 #include "src/writer/wgsl/generator.h"
27
28 namespace tint {
29 namespace fuzzers {
30 namespace ast_fuzzer {
31 namespace {
32
33 CliParams cli_params{};
34
LLVMFuzzerInitialize(int * argc,char *** argv)35 extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
36 // Parse CLI parameters. `ParseCliParams` will call `exit` if some parameter
37 // is invalid.
38 cli_params = ParseCliParams(argc, *argv);
39 // For some fuzz targets it is desirable to force the values of certain CLI
40 // parameters after parsing.
41 OverrideCliParams(cli_params);
42 return 0;
43 }
44
LLVMFuzzerCustomMutator(uint8_t * data,size_t size,size_t max_size,unsigned seed)45 extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data,
46 size_t size,
47 size_t max_size,
48 unsigned seed) {
49 Source::File file("test.wgsl", {reinterpret_cast<char*>(data), size});
50 auto program = reader::wgsl::Parse(&file);
51 if (!program.IsValid()) {
52 std::cout << "Trying to mutate an invalid program:" << std::endl
53 << program.Diagnostics().str() << std::endl;
54 return 0;
55 }
56
57 // Run the mutator.
58 RandomGenerator generator(seed);
59 ProbabilityContext probability_context(&generator);
60 program = Mutate(std::move(program), &probability_context,
61 cli_params.enable_all_mutations,
62 cli_params.mutation_batch_size, nullptr);
63
64 if (!program.IsValid()) {
65 std::cout << "Mutator produced invalid WGSL:" << std::endl
66 << " seed: " << seed << std::endl
67 << program.Diagnostics().str() << std::endl;
68 return 0;
69 }
70
71 auto result = writer::wgsl::Generate(&program, writer::wgsl::Options());
72 if (!result.success) {
73 std::cout << "Can't generate WGSL for a valid tint::Program:" << std::endl
74 << result.error << std::endl;
75 return 0;
76 }
77
78 if (result.wgsl.size() > max_size) {
79 return 0;
80 }
81
82 // No need to worry about the \0 here. The reason is that if \0 is included by
83 // developer by mistake, it will be considered a part of the string and will
84 // cause all sorts of strange bugs. Thus, unless `data` below is used as a raw
85 // C string, the \0 symbol should be ignored.
86 std::memcpy( // NOLINT - clang-tidy warns about lack of null termination.
87 data, result.wgsl.data(), result.wgsl.size());
88 return result.wgsl.size();
89 }
90
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)91 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
92 if (size == 0) {
93 return 0;
94 }
95
96 struct Target {
97 FuzzingTarget fuzzing_target;
98 OutputFormat output_format;
99 const char* name;
100 };
101
102 Target targets[] = {{FuzzingTarget::kWgsl, OutputFormat::kWGSL, "WGSL"},
103 {FuzzingTarget::kHlsl, OutputFormat::kHLSL, "HLSL"},
104 {FuzzingTarget::kMsl, OutputFormat::kMSL, "MSL"},
105 {FuzzingTarget::kSpv, OutputFormat::kSpv, "SPV"}};
106
107 for (auto target : targets) {
108 if ((target.fuzzing_target & cli_params.fuzzing_target) !=
109 target.fuzzing_target) {
110 continue;
111 }
112
113 TransformBuilder tb(data, size);
114 tb.AddTransform<tint::transform::Robustness>();
115
116 CommonFuzzer fuzzer(InputFormat::kWGSL, target.output_format);
117 fuzzer.SetTransformManager(tb.manager(), tb.data_map());
118
119 fuzzer.Run(data, size);
120 if (fuzzer.HasErrors()) {
121 std::cout << "Fuzzing " << target.name << " produced an error"
122 << std::endl;
123 auto printer = tint::diag::Printer::create(stderr, true);
124 tint::diag::Formatter{}.format(fuzzer.Diagnostics(), printer.get());
125 }
126 }
127
128 return 0;
129 }
130
131 } // namespace
132 } // namespace ast_fuzzer
133 } // namespace fuzzers
134 } // namespace tint
135