[tint][fuzzers] Add tint_wgsl_fuzzer executable
This is the framework for all future WGSL-input fuzzers.
Change-Id: I171a473724d842afc17a66726514a9736e57c00b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/154242
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 1c719a3..133dca0 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -19,6 +19,7 @@
testonly = true
deps = [
"src/dawn/fuzzers",
+ "src/tint/cmd/fuzz/wgsl",
"src/tint/fuzzers",
]
}
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index a91da43..1513b91 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -217,7 +217,21 @@
}
###############################################################################
-# Aliases.
+# Fuzzers
+###############################################################################
+action("tint_generate_wgsl_corpus") {
+ script = "${tint_src_dir}/cmd/fuzz/wgsl/generate_wgsl_corpus.py"
+ sources = [ "${tint_src_dir}/cmd/fuzz/wgsl/generate_wgsl_corpus.py" ]
+ args = [
+ "--stamp=" + rebase_path(fuzzer_corpus_wgsl_stamp, root_build_dir),
+ rebase_path("${tint_root_dir}/test", root_build_dir),
+ rebase_path(fuzzer_corpus_wgsl_dir, root_build_dir),
+ ]
+ outputs = [ fuzzer_corpus_wgsl_stamp ]
+}
+
+###############################################################################
+# Aliases
###############################################################################
if (tint_build_unittests) {
group("tint_unittests") {
diff --git a/src/tint/cmd/BUILD.cmake b/src/tint/cmd/BUILD.cmake
index 2315cbe..889a33e 100644
--- a/src/tint/cmd/BUILD.cmake
+++ b/src/tint/cmd/BUILD.cmake
@@ -23,6 +23,7 @@
include(cmd/bench/BUILD.cmake)
include(cmd/common/BUILD.cmake)
+include(cmd/fuzz/BUILD.cmake)
include(cmd/info/BUILD.cmake)
include(cmd/loopy/BUILD.cmake)
include(cmd/remote_compile/BUILD.cmake)
diff --git a/src/tint/cmd/fuzz/BUILD.bazel b/src/tint/cmd/fuzz/BUILD.bazel
new file mode 100644
index 0000000..9f81589
--- /dev/null
+++ b/src/tint/cmd/fuzz/BUILD.bazel
@@ -0,0 +1,26 @@
+# Copyright 2023 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.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+# tools/src/cmd/gen/build/BUILD.bazel.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+# Do not modify this file directly
+################################################################################
+
+load("//src/tint:flags.bzl", "COPTS")
+load("@bazel_skylib//lib:selects.bzl", "selects")
+
diff --git a/src/tint/cmd/fuzz/BUILD.cmake b/src/tint/cmd/fuzz/BUILD.cmake
new file mode 100644
index 0000000..2f0dfdf
--- /dev/null
+++ b/src/tint/cmd/fuzz/BUILD.cmake
@@ -0,0 +1,24 @@
+# Copyright 2023 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.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+# tools/src/cmd/gen/build/BUILD.cmake.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+# Do not modify this file directly
+################################################################################
+
+include(cmd/fuzz/wgsl/BUILD.cmake)
diff --git a/src/tint/cmd/fuzz/BUILD.gn b/src/tint/cmd/fuzz/BUILD.gn
new file mode 100644
index 0000000..b8a448e
--- /dev/null
+++ b/src/tint/cmd/fuzz/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2023 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.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+# tools/src/cmd/gen/build/BUILD.gn.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+# Do not modify this file directly
+################################################################################
+
+import("../../../../scripts/tint_overrides_with_defaults.gni")
+
+import("${tint_src_dir}/tint.gni")
diff --git a/src/tint/cmd/fuzz/wgsl/BUILD.bazel b/src/tint/cmd/fuzz/wgsl/BUILD.bazel
new file mode 100644
index 0000000..9f81589
--- /dev/null
+++ b/src/tint/cmd/fuzz/wgsl/BUILD.bazel
@@ -0,0 +1,26 @@
+# Copyright 2023 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.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+# tools/src/cmd/gen/build/BUILD.bazel.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+# Do not modify this file directly
+################################################################################
+
+load("//src/tint:flags.bzl", "COPTS")
+load("@bazel_skylib//lib:selects.bzl", "selects")
+
diff --git a/src/tint/cmd/fuzz/wgsl/BUILD.cmake b/src/tint/cmd/fuzz/wgsl/BUILD.cmake
new file mode 100644
index 0000000..e8c2f84
--- /dev/null
+++ b/src/tint/cmd/fuzz/wgsl/BUILD.cmake
@@ -0,0 +1,88 @@
+# Copyright 2023 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.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+# tools/src/cmd/gen/build/BUILD.cmake.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+# Do not modify this file directly
+################################################################################
+
+################################################################################
+# Target: tint_cmd_fuzz_wgsl_fuzz_cmd
+# Kind: fuzz_cmd
+################################################################################
+tint_add_target(tint_cmd_fuzz_wgsl_fuzz_cmd fuzz_cmd
+ cmd/fuzz/wgsl/main_fuzz.cc
+)
+
+tint_target_add_dependencies(tint_cmd_fuzz_wgsl_fuzz_cmd fuzz_cmd
+ tint_cmd_fuzz_wgsl_fuzz
+ tint_lang_core
+ tint_lang_core_constant
+ tint_lang_core_type
+ tint_lang_wgsl
+ tint_lang_wgsl_ast
+ tint_lang_wgsl_program
+ tint_lang_wgsl_sem
+ tint_utils_containers
+ tint_utils_diagnostic
+ tint_utils_ice
+ tint_utils_id
+ tint_utils_macros
+ tint_utils_math
+ tint_utils_memory
+ tint_utils_result
+ tint_utils_rtti
+ tint_utils_symbol
+ tint_utils_text
+ tint_utils_traits
+)
+
+################################################################################
+# Target: tint_cmd_fuzz_wgsl_fuzz
+# Kind: fuzz
+################################################################################
+tint_add_target(tint_cmd_fuzz_wgsl_fuzz fuzz
+ cmd/fuzz/wgsl/wgsl_fuzz.cc
+ cmd/fuzz/wgsl/wgsl_fuzz.h
+)
+
+tint_target_add_dependencies(tint_cmd_fuzz_wgsl_fuzz fuzz
+ tint_api_common
+ tint_lang_core
+ tint_lang_core_constant
+ tint_lang_core_ir
+ tint_lang_core_type
+ tint_lang_wgsl
+ tint_lang_wgsl_ast
+ tint_lang_wgsl_program
+ tint_lang_wgsl_reader
+ tint_lang_wgsl_sem
+ tint_utils_containers
+ tint_utils_diagnostic
+ tint_utils_ice
+ tint_utils_id
+ tint_utils_macros
+ tint_utils_math
+ tint_utils_memory
+ tint_utils_reflection
+ tint_utils_result
+ tint_utils_rtti
+ tint_utils_symbol
+ tint_utils_text
+ tint_utils_traits
+)
diff --git a/src/tint/cmd/fuzz/wgsl/BUILD.gn b/src/tint/cmd/fuzz/wgsl/BUILD.gn
new file mode 100644
index 0000000..69db374
--- /dev/null
+++ b/src/tint/cmd/fuzz/wgsl/BUILD.gn
@@ -0,0 +1,84 @@
+# Copyright 2023 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.
+
+################################################################################
+# File generated by 'tools/src/cmd/gen' using the template:
+# tools/src/cmd/gen/build/BUILD.gn.tmpl
+#
+# To regenerate run: './tools/run gen'
+#
+# Do not modify this file directly
+################################################################################
+
+import("../../../../../scripts/tint_overrides_with_defaults.gni")
+
+import("${tint_src_dir}/tint.gni")
+
+tint_fuzz_source_set("fuzz") {
+ sources = [
+ "wgsl_fuzz.cc",
+ "wgsl_fuzz.h",
+ ]
+ deps = [
+ "${tint_src_dir}/api/common",
+ "${tint_src_dir}/lang/core",
+ "${tint_src_dir}/lang/core/constant",
+ "${tint_src_dir}/lang/core/ir",
+ "${tint_src_dir}/lang/core/type",
+ "${tint_src_dir}/lang/wgsl",
+ "${tint_src_dir}/lang/wgsl/ast",
+ "${tint_src_dir}/lang/wgsl/program",
+ "${tint_src_dir}/lang/wgsl/reader",
+ "${tint_src_dir}/lang/wgsl/sem",
+ "${tint_src_dir}/utils/containers",
+ "${tint_src_dir}/utils/diagnostic",
+ "${tint_src_dir}/utils/ice",
+ "${tint_src_dir}/utils/id",
+ "${tint_src_dir}/utils/macros",
+ "${tint_src_dir}/utils/math",
+ "${tint_src_dir}/utils/memory",
+ "${tint_src_dir}/utils/reflection",
+ "${tint_src_dir}/utils/result",
+ "${tint_src_dir}/utils/rtti",
+ "${tint_src_dir}/utils/symbol",
+ "${tint_src_dir}/utils/text",
+ "${tint_src_dir}/utils/traits",
+ ]
+}
+
+tint_fuzzer_test("wgsl") {
+ sources = [ "main_fuzz.cc" ]
+ deps = [
+ "${tint_src_dir}/cmd/fuzz/wgsl:fuzz",
+ "${tint_src_dir}/lang/core",
+ "${tint_src_dir}/lang/core/constant",
+ "${tint_src_dir}/lang/core/type",
+ "${tint_src_dir}/lang/wgsl",
+ "${tint_src_dir}/lang/wgsl/ast",
+ "${tint_src_dir}/lang/wgsl/program",
+ "${tint_src_dir}/lang/wgsl/sem",
+ "${tint_src_dir}/utils/containers",
+ "${tint_src_dir}/utils/diagnostic",
+ "${tint_src_dir}/utils/ice",
+ "${tint_src_dir}/utils/id",
+ "${tint_src_dir}/utils/macros",
+ "${tint_src_dir}/utils/math",
+ "${tint_src_dir}/utils/memory",
+ "${tint_src_dir}/utils/result",
+ "${tint_src_dir}/utils/rtti",
+ "${tint_src_dir}/utils/symbol",
+ "${tint_src_dir}/utils/text",
+ "${tint_src_dir}/utils/traits",
+ ]
+}
diff --git a/src/tint/cmd/fuzz/wgsl/dictionary.txt b/src/tint/cmd/fuzz/wgsl/dictionary.txt
new file mode 100644
index 0000000..59742d7
--- /dev/null
+++ b/src/tint/cmd/fuzz/wgsl/dictionary.txt
@@ -0,0 +1,296 @@
+"!"
+"!="
+"%"
+"%="
+"&"
+"&&"
+"&="
+"("
+")"
+"*"
+"*="
+"+"
+"++"
+"+="
+","
+"-"
+"--"
+"-="
+"->"
+"."
+"/"
+"/="
+":"
+";"
+"<"
+"<<"
+"<<="
+"<="
+"="
+"=="
+">"
+">="
+">>"
+">>="
+"@"
+"["
+"]"
+"^"
+"^="
+"_"
+"{"
+"|"
+"|="
+"||"
+"}"
+"~"
+"a"
+"abs"
+"acos"
+"acosh"
+"@align"
+"all"
+"any"
+"array"
+"arrayLength"
+"asin"
+"asinh"
+"atan"
+"atan2"
+"atanh"
+"atomic"
+"atomicAdd"
+"atomicAnd"
+"atomicLoad"
+"atomicMax"
+"atomicMin"
+"atomicOr"
+"atomicStore"
+"atomicSub"
+"atomicXor"
+"b"
+"@binding"
+"bitcast"
+"bool"
+"break"
+"@builtin"
+"@builtin(frag_depth)"
+"@builtin(front_facing)"
+"@builtin(global_invocation_id)"
+"@builtin(instance_index)"
+"@builtin(local_invocation_id)"
+"@builtin(local_invocation_index)"
+"@builtin(num_workgroups)"
+"@builtin(position)"
+"@builtin(sample_index)"
+"@builtin(sample_mask)"
+"@builtin(vertex_index)"
+"@builtin(workgroup_id)"
+"case"
+"ceil"
+"center"
+"centroid"
+"clamp"
+"@compute"
+"@const"
+"const"
+"continue"
+"continuing"
+"cos"
+"cosh"
+"countLeadingZeros"
+"countOneBits"
+"countTrailingZeros"
+"cross"
+"default"
+"degrees"
+"determinant"
+"discard"
+"distance"
+"dot"
+"dot4I8Packed"
+"dot4U8Packed"
+"dpdx"
+"dpdxCoarse"
+"dpdxFine"
+"dpdy"
+"dpdyCoarse"
+"dpdyFine"
+"else"
+"enable"
+"exp"
+"exp2"
+"extractBits"
+"f16"
+"f32"
+"faceForward"
+"fallthrough"
+"false"
+"firstLeadingBit"
+"firstTrailingBit"
+"flat"
+"floor"
+"fma"
+"fn"
+"for"
+"fract"
+"frag_depth"
+"@fragment"
+"frexp"
+"front_facing"
+"function"
+"fwidth"
+"fwidthCoarse"
+"fwidthFine"
+"g"
+"global_invocation_id"
+"@group"
+"i32"
+"@id"
+"if"
+"insertBits"
+"instance_index"
+"@interpolate"
+"@invariant"
+"inverseSqrt"
+"ldexp"
+"length"
+"let"
+"linear"
+"local_invocation_id"
+"local_invocation_index"
+"@location"
+"log"
+"log2"
+"loop"
+"mat2x2"
+"mat2x3"
+"mat2x4"
+"mat3x2"
+"mat3x3"
+"mat3x4"
+"mat4x2"
+"mat4x3"
+"mat4x4"
+"max"
+"min"
+"mix"
+"modf"
+"normalize"
+"num_workgroups"
+"override"
+"pack2x16float"
+"pack2x16snorm"
+"pack2x16unorm"
+"pack4x8snorm"
+"pack4x8unorm"
+"perspective"
+"position"
+"pow"
+"private"
+"ptr"
+"quantizeToF16"
+"r"
+"r32float"
+"r32sint"
+"r32uint"
+"radians"
+"read"
+"read_write"
+"reflect"
+"refract"
+"return"
+"reverseBits"
+"rg32float"
+"rg32sint"
+"rg32uint"
+"rgba16float"
+"rgba16sint"
+"rgba16uint"
+"rgba32float"
+"rgba32sint"
+"rgba32uint"
+"rgba8sint"
+"rgba8snorm"
+"rgba8uint"
+"rgba8unorm"
+"round"
+"sample"
+"sample_index"
+"sample_mask"
+"sampler"
+"sampler_comparison"
+"saturate"
+"select"
+"sign"
+"sin"
+"sinh"
+"@size"
+"smoothstep"
+"sqrt"
+"staticAssert"
+"step"
+"storage"
+"storageBarrier"
+"struct"
+"switch"
+"tan"
+"tanh"
+"texture_1d"
+"texture_2d"
+"texture_2d_array"
+"texture_3d"
+"texture_cube"
+"texture_cube_array"
+"texture_depth_2d"
+"texture_depth_2d_array"
+"texture_depth_cube"
+"texture_depth_cube_array"
+"texture_depth_multisampled_2d"
+"textureDimensions"
+"textureGather"
+"textureGatherCompare"
+"textureLoad"
+"texture_multisampled_2d"
+"textureNumLayers"
+"textureNumLevels"
+"textureNumSamples"
+"textureSample"
+"textureSampleBias"
+"textureSampleCompare"
+"textureSampleCompareLevel"
+"textureSampleGrad"
+"textureSampleLevel"
+"texture_storage_1d"
+"texture_storage_2d"
+"texture_storage_2d_array"
+"texture_storage_3d"
+"textureStore"
+"transpose"
+"true"
+"trunc"
+"type"
+"u32"
+"uniform"
+"unpack2x16float"
+"unpack2x16snorm"
+"unpack2x16unorm"
+"unpack4x8snorm"
+"unpack4x8unorm"
+"var"
+"vec2"
+"vec3"
+"vec4"
+"@vertex"
+"vertex_index"
+"w"
+"while"
+"workgroup"
+"workgroupBarrier"
+"workgroupUniformLoad"
+"workgroup_id"
+"@workgroup_size"
+"write"
+"x"
+"y"
+"z"
diff --git a/src/tint/cmd/fuzz/wgsl/generate_wgsl_corpus.py b/src/tint/cmd/fuzz/wgsl/generate_wgsl_corpus.py
new file mode 100644
index 0000000..983acd9
--- /dev/null
+++ b/src/tint/cmd/fuzz/wgsl/generate_wgsl_corpus.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+
+# 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.
+
+# Collect all .wgsl files under a given directory and copy them to a given
+# corpus directory, flattening their file names by replacing path
+# separators with underscores. If the output directory already exists, it
+# will be deleted and re-created. Files ending with ".expected.spvasm" are
+# skipped.
+#
+# The intended use of this script is to generate a corpus of WGSL shaders
+# for fuzzing.
+#
+# Usage:
+# generate_wgsl_corpus.py <input_dir> <corpus_dir>
+
+import optparse
+import os
+import pathlib
+import shutil
+import sys
+
+
+def list_wgsl_files(root_search_dir):
+ for root, folders, files in os.walk(root_search_dir):
+ for filename in folders + files:
+ if pathlib.Path(filename).suffix == '.wgsl':
+ yield os.path.join(root, filename)
+
+
+def main():
+ parser = optparse.OptionParser(
+ usage="usage: %prog [option] input-dir output-dir")
+ parser.add_option('--stamp', dest='stamp', help='stamp file')
+ options, args = parser.parse_args(sys.argv[1:])
+ if len(args) != 2:
+ parser.error("incorrect number of arguments")
+ input_dir: str = os.path.abspath(args[0].rstrip(os.sep))
+ corpus_dir: str = os.path.abspath(args[1])
+ if os.path.exists(corpus_dir):
+ shutil.rmtree(corpus_dir)
+ os.makedirs(corpus_dir)
+ for in_file in list_wgsl_files(input_dir):
+ if in_file.endswith(".expected.wgsl"):
+ continue
+ out_file = in_file[len(input_dir) + 1:].replace(os.sep, '_')
+ shutil.copy(in_file, corpus_dir + os.sep + out_file)
+ if options.stamp:
+ pathlib.Path(options.stamp).touch(mode=0o644, exist_ok=True)
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/src/tint/cmd/fuzz/wgsl/main_fuzz.cc b/src/tint/cmd/fuzz/wgsl/main_fuzz.cc
new file mode 100644
index 0000000..8f3c3ab
--- /dev/null
+++ b/src/tint/cmd/fuzz/wgsl/main_fuzz.cc
@@ -0,0 +1,23 @@
+// Copyright 2023 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 "src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size > 0) {
+ std::string_view wgsl(reinterpret_cast<const char*>(data), size);
+ tint::fuzz::wgsl::Run(wgsl);
+ }
+ return 0;
+}
diff --git a/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.cc b/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.cc
new file mode 100644
index 0000000..82cd3b5
--- /dev/null
+++ b/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.cc
@@ -0,0 +1,66 @@
+// Copyright 2023 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 "src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h"
+
+#include <iostream>
+
+#include "src/tint/lang/wgsl/reader/reader.h"
+#include "src/tint/utils/containers/vector.h"
+#include "src/tint/utils/macros/defer.h"
+#include "src/tint/utils/macros/static_init.h"
+
+namespace tint::fuzz::wgsl {
+namespace {
+
+Vector<ProgramFuzzer, 32> fuzzers;
+std::string_view currently_running;
+
+[[noreturn]] void TintInternalCompilerErrorReporter(const tint::InternalCompilerError& err) {
+ std::cerr << "ICE while running fuzzer: '" << currently_running << "'";
+ std::cerr << err.Error() << std::endl;
+ __builtin_trap();
+}
+
+} // namespace
+
+void Register(const ProgramFuzzer& fuzzer) {
+ fuzzers.Push(fuzzer);
+}
+
+void Run(std::string_view wgsl) {
+ tint::SetInternalCompilerErrorReporter(&TintInternalCompilerErrorReporter);
+
+ // Ensure that fuzzers are sorted. Without this, the fuzzers may be registered in any order,
+ // leading to non-determinism, which we must avoid.
+ TINT_STATIC_INIT(fuzzers.Sort([](auto& a, auto& b) { return a.name < b.name; }));
+
+ // Create a Source::File to hand to the parser.
+ tint::Source::File file("test.wgsl", wgsl);
+
+ // Parse the WGSL program.
+ auto program = tint::wgsl::reader::Parse(&file);
+ if (!program.IsValid()) {
+ return;
+ }
+
+ // Run each of the program fuzzer functions
+ TINT_DEFER(currently_running = "");
+ for (auto& fuzzer : fuzzers) {
+ currently_running = fuzzer.name;
+ fuzzer.fn(program);
+ }
+}
+
+} // namespace tint::fuzz::wgsl
diff --git a/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h b/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h
new file mode 100644
index 0000000..9fb5fd4
--- /dev/null
+++ b/src/tint/cmd/fuzz/wgsl/wgsl_fuzz.h
@@ -0,0 +1,51 @@
+// Copyright 2023 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.
+
+#ifndef SRC_TINT_CMD_FUZZ_WGSL_WGSL_FUZZ_H_
+#define SRC_TINT_CMD_FUZZ_WGSL_WGSL_FUZZ_H_
+
+#include <string>
+
+#include "src/tint/lang/wgsl/program/program.h"
+#include "src/tint/utils/containers/slice.h"
+#include "src/tint/utils/macros/static_init.h"
+
+namespace tint::fuzz::wgsl {
+
+/// ProgramFuzzer describes a fuzzer function that takes a WGSL program as input
+struct ProgramFuzzer {
+ /// The function signature
+ using Fn = void(const Program&);
+
+ /// Name of the fuzzer function
+ std::string_view name;
+ /// The fuzzer function pointer
+ Fn* fn = nullptr;
+};
+
+/// Runs all the registered WGSL fuzzers with the supplied WGSL
+/// @param wgsl the input WGSL
+void Run(std::string_view wgsl);
+
+/// Registers the fuzzer function with the WGSL fuzzer executable.
+/// @param fuzzer the fuzzer
+void Register(const ProgramFuzzer& fuzzer);
+
+/// TINT_WGSL_PROGRAM_FUZZER registers the fuzzer function to run as part of `tint_wgsl_fuzzer`
+#define TINT_WGSL_PROGRAM_FUZZER(FUNCTION) \
+ TINT_STATIC_INIT(::tint::fuzz::wgsl::Register({#FUNCTION, FUNCTION}))
+
+} // namespace tint::fuzz::wgsl
+
+#endif // SRC_TINT_CMD_FUZZ_WGSL_WGSL_FUZZ_H_
diff --git a/src/tint/tint.gni b/src/tint/tint.gni
index fc9ffeb..9833c02 100644
--- a/src/tint/tint.gni
+++ b/src/tint/tint.gni
@@ -77,6 +77,9 @@
# Fuzzers
###############################################################################
import("//testing/libfuzzer/fuzzer_test.gni")
+fuzzer_corpus_wgsl_dir = "${root_gen_dir}/fuzzers/wgsl_corpus"
+fuzzer_corpus_wgsl_stamp = "${fuzzer_corpus_wgsl_dir}.stamp"
+
template("tint_fuzz_source_set") {
source_set(target_name) {
forward_variables_from(invoker, "*", [ "configs" ])
@@ -110,5 +113,17 @@
fuzzer_test(target_name) {
forward_variables_from(invoker, "*")
exclude_main = false
+
+ if (target_name == "wgsl") {
+ dict = "dictionary.txt"
+ libfuzzer_options = [
+ "only_ascii=1", # TODO(bclayton): Remove this to fuzz unicode?
+ "max_len=10000",
+ ]
+ seed_corpus = fuzzer_corpus_wgsl_dir
+ seed_corpus_deps = [ "${tint_src_dir}:tint_generate_wgsl_corpus" ]
+ } else {
+ assert(false, "unsupported tint fuzzer target")
+ }
}
}
diff --git a/src/tint/utils/diagnostic/source.cc b/src/tint/utils/diagnostic/source.cc
index f223d62..0ea43bb 100644
--- a/src/tint/utils/diagnostic/source.cc
+++ b/src/tint/utils/diagnostic/source.cc
@@ -113,7 +113,7 @@
} // namespace
-Source::FileContent::FileContent(const std::string& body) : data(body), lines(SplitLines(data)) {}
+Source::FileContent::FileContent(std::string_view body) : data(body), lines(SplitLines(data)) {}
Source::FileContent::FileContent(const FileContent& rhs)
: data(rhs.data), lines(CopyRelativeStringViews(rhs.lines, rhs.data, data)) {}
diff --git a/src/tint/utils/diagnostic/source.h b/src/tint/utils/diagnostic/source.h
index ac3497c..6795dca 100644
--- a/src/tint/utils/diagnostic/source.h
+++ b/src/tint/utils/diagnostic/source.h
@@ -34,7 +34,7 @@
public:
/// Constructs the FileContent with the given file content.
/// @param data the file contents
- explicit FileContent(const std::string& data);
+ explicit FileContent(std::string_view data);
/// Copy constructor
/// @param rhs the FileContent to copy
@@ -55,7 +55,7 @@
/// Constructs the File with the given file path and content.
/// @param p the path for this file
/// @param c the file contents
- inline File(const std::string& p, const std::string& c) : path(p), content(c) {}
+ inline File(const std::string& p, std::string_view c) : path(p), content(c) {}
/// Copy constructor
File(const File&) = default;