1 //
2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // translator_fuzzer.cpp: A libfuzzer fuzzer for the shader translator.
8
9 #include <cstddef>
10 #include <cstdint>
11 #include <iostream>
12 #include <memory>
13 #include <unordered_map>
14
15 #include "angle_gl.h"
16 #include "anglebase/no_destructor.h"
17 #include "compiler/translator/Compiler.h"
18 #include "compiler/translator/util.h"
19
20 using namespace sh;
21
22 namespace
23 {
24 struct TranslatorCacheKey
25 {
operator ==__anonf0820b9a0111::TranslatorCacheKey26 bool operator==(const TranslatorCacheKey &other) const
27 {
28 return type == other.type && spec == other.spec && output == other.output;
29 }
30
31 uint32_t type = 0;
32 uint32_t spec = 0;
33 uint32_t output = 0;
34 };
35 } // anonymous namespace
36
37 namespace std
38 {
39
40 template <>
41 struct hash<TranslatorCacheKey>
42 {
operator ()std::hash43 std::size_t operator()(const TranslatorCacheKey &k) const
44 {
45 return (hash<uint32_t>()(k.type) << 1) ^ (hash<uint32_t>()(k.spec) >> 1) ^
46 hash<uint32_t>()(k.output);
47 }
48 };
49 } // namespace std
50
51 struct TCompilerDeleter
52 {
operator ()TCompilerDeleter53 void operator()(TCompiler *compiler) const { DeleteCompiler(compiler); }
54 };
55
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)56 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
57 {
58 ShaderDumpHeader header{};
59 if (size <= sizeof(header))
60 {
61 return 0;
62 }
63
64 // Make sure the rest of data will be a valid C string so that we don't have to copy it.
65 if (data[size - 1] != 0)
66 {
67 return 0;
68 }
69
70 memcpy(&header, data, sizeof(header));
71 ShCompileOptions options{};
72 memcpy(&options, &header.basicCompileOptions, offsetof(ShCompileOptions, metal));
73 memcpy(&options.metal, &header.metalCompileOptions, sizeof(options.metal));
74 memcpy(&options.pls, &header.plsCompileOptions, sizeof(options.pls));
75 size -= sizeof(header);
76 data += sizeof(header);
77 uint32_t type = header.type;
78 uint32_t spec = header.spec;
79
80 if (type != GL_FRAGMENT_SHADER && type != GL_VERTEX_SHADER)
81 {
82 return 0;
83 }
84
85 if (spec != SH_GLES2_SPEC && type != SH_WEBGL_SPEC && spec != SH_GLES3_SPEC &&
86 spec != SH_WEBGL2_SPEC)
87 {
88 return 0;
89 }
90
91 ShShaderOutput shaderOutput = static_cast<ShShaderOutput>(header.output);
92
93 bool hasUnsupportedOptions = false;
94
95 const bool hasMacGLSLOptions = options.rewriteFloatUnaryMinusOperator ||
96 options.addAndTrueToLoopCondition ||
97 options.rewriteDoWhileLoops || options.unfoldShortCircuit ||
98 options.rewriteRowMajorMatrices;
99
100 if (!IsOutputGLSL(shaderOutput) && !IsOutputESSL(shaderOutput))
101 {
102 hasUnsupportedOptions =
103 hasUnsupportedOptions || options.emulateAtan2FloatFunction || options.clampFragDepth ||
104 options.regenerateStructNames || options.rewriteRepeatedAssignToSwizzled ||
105 options.useUnusedStandardSharedBlocks || options.selectViewInNvGLSLVertexShader;
106
107 hasUnsupportedOptions = hasUnsupportedOptions || hasMacGLSLOptions;
108 }
109 else
110 {
111 #if !defined(ANGLE_PLATFORM_APPLE)
112 hasUnsupportedOptions = hasUnsupportedOptions || hasMacGLSLOptions;
113 #endif
114 }
115 if (!IsOutputSPIRV(shaderOutput))
116 {
117 hasUnsupportedOptions =
118 hasUnsupportedOptions || options.emulateSeamfulCubeMapSampling ||
119 options.useSpecializationConstant || options.addVulkanXfbEmulationSupportCode ||
120 options.roundOutputAfterDithering || options.addAdvancedBlendEquationsEmulation;
121 }
122 if (!IsOutputHLSL(shaderOutput))
123 {
124 hasUnsupportedOptions = hasUnsupportedOptions ||
125 options.expandSelectHLSLIntegerPowExpressions ||
126 options.allowTranslateUniformBlockToStructuredBuffer ||
127 options.rewriteIntegerUnaryMinusOperator;
128 }
129
130 // If there are any options not supported with this output, don't attempt to run the translator.
131 if (hasUnsupportedOptions)
132 {
133 return 0;
134 }
135
136 // Make sure the rest of the options are in a valid range.
137 options.pls.fragmentSyncType = static_cast<ShFragmentSynchronizationType>(
138 static_cast<uint32_t>(options.pls.fragmentSyncType) %
139 static_cast<uint32_t>(ShFragmentSynchronizationType::InvalidEnum));
140
141 std::vector<uint32_t> validOutputs;
142 validOutputs.push_back(SH_ESSL_OUTPUT);
143 validOutputs.push_back(SH_GLSL_COMPATIBILITY_OUTPUT);
144 validOutputs.push_back(SH_GLSL_130_OUTPUT);
145 validOutputs.push_back(SH_GLSL_140_OUTPUT);
146 validOutputs.push_back(SH_GLSL_150_CORE_OUTPUT);
147 validOutputs.push_back(SH_GLSL_330_CORE_OUTPUT);
148 validOutputs.push_back(SH_GLSL_400_CORE_OUTPUT);
149 validOutputs.push_back(SH_GLSL_410_CORE_OUTPUT);
150 validOutputs.push_back(SH_GLSL_420_CORE_OUTPUT);
151 validOutputs.push_back(SH_GLSL_430_CORE_OUTPUT);
152 validOutputs.push_back(SH_GLSL_440_CORE_OUTPUT);
153 validOutputs.push_back(SH_GLSL_450_CORE_OUTPUT);
154 validOutputs.push_back(SH_SPIRV_VULKAN_OUTPUT);
155 validOutputs.push_back(SH_HLSL_3_0_OUTPUT);
156 validOutputs.push_back(SH_HLSL_4_1_OUTPUT);
157 validOutputs.push_back(SH_HLSL_4_0_FL9_3_OUTPUT);
158 bool found = false;
159 for (auto valid : validOutputs)
160 {
161 found = found || (valid == shaderOutput);
162 }
163 if (!found)
164 {
165 return 0;
166 }
167
168 if (!sh::Initialize())
169 {
170 return 0;
171 }
172
173 TranslatorCacheKey key;
174 key.type = type;
175 key.spec = spec;
176 key.output = shaderOutput;
177
178 using UniqueTCompiler = std::unique_ptr<TCompiler, TCompilerDeleter>;
179 static angle::base::NoDestructor<angle::HashMap<TranslatorCacheKey, UniqueTCompiler>>
180 translators;
181
182 if (translators->find(key) == translators->end())
183 {
184 UniqueTCompiler translator(
185 ConstructCompiler(type, static_cast<ShShaderSpec>(spec), shaderOutput));
186
187 if (translator == nullptr)
188 {
189 return 0;
190 }
191
192 ShBuiltInResources resources;
193 sh::InitBuiltInResources(&resources);
194
195 // Enable all the extensions to have more coverage
196 resources.OES_standard_derivatives = 1;
197 resources.OES_EGL_image_external = 1;
198 resources.OES_EGL_image_external_essl3 = 1;
199 resources.NV_EGL_stream_consumer_external = 1;
200 resources.ARB_texture_rectangle = 1;
201 resources.EXT_blend_func_extended = 1;
202 resources.EXT_conservative_depth = 1;
203 resources.EXT_draw_buffers = 1;
204 resources.EXT_frag_depth = 1;
205 resources.EXT_shader_texture_lod = 1;
206 resources.EXT_shader_framebuffer_fetch = 1;
207 resources.NV_shader_framebuffer_fetch = 1;
208 resources.ARM_shader_framebuffer_fetch = 1;
209 resources.EXT_YUV_target = 1;
210 resources.APPLE_clip_distance = 1;
211 resources.MaxDualSourceDrawBuffers = 1;
212 resources.EXT_gpu_shader5 = 1;
213 resources.MaxClipDistances = 1;
214 resources.EXT_shadow_samplers = 1;
215 resources.EXT_clip_cull_distance = 1;
216 resources.ANGLE_clip_cull_distance = 1;
217 resources.EXT_primitive_bounding_box = 1;
218 resources.OES_primitive_bounding_box = 1;
219
220 if (!translator->Init(resources))
221 {
222 return 0;
223 }
224
225 (*translators)[key] = std::move(translator);
226 }
227
228 auto &translator = (*translators)[key];
229
230 options.limitExpressionComplexity = true;
231 const char *shaderStrings[] = {reinterpret_cast<const char *>(data)};
232 translator->compile(shaderStrings, 1, options);
233
234 return 0;
235 }
236