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 "common/hash_containers.h"
18 #include "compiler/translator/Compiler.h"
19 #include "compiler/translator/util.h"
20
21 using namespace sh;
22
23 namespace
24 {
25 struct TranslatorCacheKey
26 {
operator ==__anon9f0e5fbc0111::TranslatorCacheKey27 bool operator==(const TranslatorCacheKey &other) const
28 {
29 return type == other.type && spec == other.spec && output == other.output;
30 }
31
32 uint32_t type = 0;
33 uint32_t spec = 0;
34 uint32_t output = 0;
35 };
36 } // anonymous namespace
37
38 namespace std
39 {
40
41 template <>
42 struct hash<TranslatorCacheKey>
43 {
operator ()std::hash44 std::size_t operator()(const TranslatorCacheKey &k) const
45 {
46 return (hash<uint32_t>()(k.type) << 1) ^ (hash<uint32_t>()(k.spec) >> 1) ^
47 hash<uint32_t>()(k.output);
48 }
49 };
50 } // namespace std
51
52 struct TCompilerDeleter
53 {
operator ()TCompilerDeleter54 void operator()(TCompiler *compiler) const { DeleteCompiler(compiler); }
55 };
56
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)57 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
58 {
59 ShaderDumpHeader header{};
60 if (size <= sizeof(header))
61 {
62 return 0;
63 }
64
65 // Make sure the rest of data will be a valid C string so that we don't have to copy it.
66 if (data[size - 1] != 0)
67 {
68 return 0;
69 }
70
71 memcpy(&header, data, sizeof(header));
72 ShCompileOptions options{};
73 memcpy(&options, &header.basicCompileOptions, offsetof(ShCompileOptions, metal));
74 memcpy(&options.metal, &header.metalCompileOptions, sizeof(options.metal));
75 memcpy(&options.pls, &header.plsCompileOptions, sizeof(options.pls));
76 size -= sizeof(header);
77 data += sizeof(header);
78 uint32_t type = header.type;
79 uint32_t spec = header.spec;
80
81 if (type != GL_FRAGMENT_SHADER && type != GL_VERTEX_SHADER)
82 {
83 return 0;
84 }
85
86 if (spec != SH_GLES2_SPEC && type != SH_WEBGL_SPEC && spec != SH_GLES3_SPEC &&
87 spec != SH_WEBGL2_SPEC)
88 {
89 return 0;
90 }
91
92 ShShaderOutput shaderOutput = static_cast<ShShaderOutput>(header.output);
93
94 bool hasUnsupportedOptions = false;
95
96 const bool hasMacGLSLOptions = options.rewriteFloatUnaryMinusOperator ||
97 options.addAndTrueToLoopCondition ||
98 options.rewriteDoWhileLoops || options.unfoldShortCircuit ||
99 options.rewriteRowMajorMatrices;
100
101 if (!IsOutputGLSL(shaderOutput) && !IsOutputESSL(shaderOutput))
102 {
103 hasUnsupportedOptions =
104 hasUnsupportedOptions || options.emulateAtan2FloatFunction || options.clampFragDepth ||
105 options.regenerateStructNames || options.rewriteRepeatedAssignToSwizzled ||
106 options.useUnusedStandardSharedBlocks || options.selectViewInNvGLSLVertexShader;
107
108 hasUnsupportedOptions = hasUnsupportedOptions || hasMacGLSLOptions;
109 }
110 else
111 {
112 #if !defined(ANGLE_PLATFORM_APPLE)
113 hasUnsupportedOptions = hasUnsupportedOptions || hasMacGLSLOptions;
114 #endif
115 }
116 if (!IsOutputSPIRV(shaderOutput))
117 {
118 hasUnsupportedOptions = hasUnsupportedOptions || options.useSpecializationConstant ||
119 options.addVulkanXfbEmulationSupportCode ||
120 options.roundOutputAfterDithering ||
121 options.addAdvancedBlendEquationsEmulation;
122 }
123 if (!IsOutputHLSL(shaderOutput))
124 {
125 hasUnsupportedOptions = hasUnsupportedOptions ||
126 options.expandSelectHLSLIntegerPowExpressions ||
127 options.allowTranslateUniformBlockToStructuredBuffer ||
128 options.rewriteIntegerUnaryMinusOperator;
129 }
130
131 // If there are any options not supported with this output, don't attempt to run the translator.
132 if (hasUnsupportedOptions)
133 {
134 return 0;
135 }
136
137 // Make sure the rest of the options are in a valid range.
138 options.pls.fragmentSyncType = static_cast<ShFragmentSynchronizationType>(
139 static_cast<uint32_t>(options.pls.fragmentSyncType) %
140 static_cast<uint32_t>(ShFragmentSynchronizationType::InvalidEnum));
141
142 std::vector<uint32_t> validOutputs;
143 validOutputs.push_back(SH_ESSL_OUTPUT);
144 validOutputs.push_back(SH_GLSL_COMPATIBILITY_OUTPUT);
145 validOutputs.push_back(SH_GLSL_130_OUTPUT);
146 validOutputs.push_back(SH_GLSL_140_OUTPUT);
147 validOutputs.push_back(SH_GLSL_150_CORE_OUTPUT);
148 validOutputs.push_back(SH_GLSL_330_CORE_OUTPUT);
149 validOutputs.push_back(SH_GLSL_400_CORE_OUTPUT);
150 validOutputs.push_back(SH_GLSL_410_CORE_OUTPUT);
151 validOutputs.push_back(SH_GLSL_420_CORE_OUTPUT);
152 validOutputs.push_back(SH_GLSL_430_CORE_OUTPUT);
153 validOutputs.push_back(SH_GLSL_440_CORE_OUTPUT);
154 validOutputs.push_back(SH_GLSL_450_CORE_OUTPUT);
155 validOutputs.push_back(SH_SPIRV_VULKAN_OUTPUT);
156 validOutputs.push_back(SH_HLSL_3_0_OUTPUT);
157 validOutputs.push_back(SH_HLSL_4_1_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.ARM_shader_framebuffer_fetch_depth_stencil = 1;
210 resources.EXT_YUV_target = 1;
211 resources.APPLE_clip_distance = 1;
212 resources.MaxDualSourceDrawBuffers = 1;
213 resources.EXT_gpu_shader5 = 1;
214 resources.MaxClipDistances = 1;
215 resources.EXT_shadow_samplers = 1;
216 resources.EXT_clip_cull_distance = 1;
217 resources.ANGLE_clip_cull_distance = 1;
218 resources.EXT_primitive_bounding_box = 1;
219 resources.OES_primitive_bounding_box = 1;
220
221 if (!translator->Init(resources))
222 {
223 return 0;
224 }
225
226 (*translators)[key] = std::move(translator);
227 }
228
229 auto &translator = (*translators)[key];
230
231 options.limitExpressionComplexity = true;
232 const char *shaderStrings[] = {reinterpret_cast<const char *>(data)};
233 translator->compile(shaderStrings, 1, options);
234
235 return 0;
236 }
237