1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef GL_GLEXT_PROTOTYPES
6 #define GL_GLEXT_PROTOTYPES
7 #endif
8
9 #include <GLES2/gl2.h>
10 #include <GLES2/gl2ext.h>
11 #include <GLES2/gl2extchromium.h>
12 #include <GLES3/gl3.h>
13 #include <stdint.h>
14
15 #include <vector>
16
17 #include "base/at_exit.h"
18 #include "base/command_line.h"
19 #include "base/i18n/icu_util.h"
20 #include "base/logging.h"
21 #include "base/message_loop/message_pump_type.h"
22 #include "base/strings/string_split.h"
23 #include "base/task/single_thread_task_executor.h"
24 #include "gpu/command_buffer/client/gles2_lib.h"
25 #include "gpu/command_buffer/tests/gl_manager.h"
26 #include "gpu/command_buffer/tests/gl_test_utils.h"
27 #include "gpu/command_buffer/tests/lpm/gl_lpm_fuzzer.pb.h"
28 #include "gpu/command_buffer/tests/lpm/gl_lpm_shader_to_string.h"
29 #include "gpu/config/gpu_test_config.h"
30 #include "testing/libfuzzer/proto/lpm_interface.h"
31 #include "ui/gfx/extension_set.h"
32 #include "ui/gl/gl_context.h"
33 #include "ui/gl/gl_version_info.h"
34 #include "ui/gl/init/gl_factory.h"
35
36 // Enable this to log and crash on unexpected errors during shader compilation.
37 #define CHECK_FOR_UNKNOWN_ERRORS false
38
39 struct Env {
EnvEnv40 Env() {
41 CHECK(base::i18n::InitializeICU());
42 base::CommandLine::Init(0, nullptr);
43 auto* command_line = base::CommandLine::ForCurrentProcess();
44
45 // TODO(nedwill): support switches for swiftshader, etc.
46 command_line->AppendSwitchASCII(switches::kUseGL,
47 gl::kGLImplementationANGLEName);
48 command_line->AppendSwitchASCII(switches::kUseANGLE,
49 gl::kANGLEImplementationNullName);
50 base::FeatureList::InitializeInstance(std::string(), std::string());
51 base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
52 gpu::GLTestHelper::InitializeGLDefault();
53 ::gles2::Initialize();
54 }
55
56 base::AtExitManager at_exit;
57 };
58
59 class ScopedGLManager {
60 public:
ScopedGLManager()61 ScopedGLManager() {
62 gpu::GLManager::Options options;
63 options.context_type = gpu::CONTEXT_TYPE_OPENGLES3;
64 gl_.Initialize(options);
65 }
~ScopedGLManager()66 ~ScopedGLManager() { gl_.Destroy(); }
67
68 private:
69 gpu::GLManager gl_;
70 };
71
CompileShader(GLenum type,const char * shaderSrc)72 GLuint CompileShader(GLenum type, const char* shaderSrc) {
73 GLuint shader = glCreateShader(type);
74 // Load the shader source
75 glShaderSource(shader, 1, &shaderSrc, nullptr);
76 // Compile the shader
77 glCompileShader(shader);
78
79 return shader;
80 }
81
82 // TODO(nedwill): Once the grammar stabilizes, try to remove as many
83 // of these as possible by making tweaks to the grammar/code generation.
84 const char* acceptable_errors[] = {
85 "void function cannot return a value",
86 "function already has a body",
87 "undeclared identifier",
88 "l-value required",
89 "cannot convert from",
90 "main function cannot return a value",
91 "illegal use of type 'void'",
92 "boolean expression expected",
93 "Missing main()",
94 "Divide by zero error during constant folding",
95 "wrong operand types",
96 "function must have the same return type in all of its declarations",
97 "function return is not matching type",
98 "redefinition",
99 "WARNING:",
100 "can't modify void",
101 "No precision specified for",
102 "exists that takes an operand of type",
103 "Illegal use of reserved word",
104 "'double' : syntax error",
105 "Integer overflow",
106 "dimension mismatch",
107 "undeclared identifier",
108 "comparison operator only defined for scalars",
109 "Local variables can only use the const storage qualifier.",
110 "must use 'flat' interpolation here",
111 "invalid qualifier combination",
112 "No precision specified for",
113 "'out' : cannot be matrix",
114 "non-void function must return a value",
115 "function does not return a value",
116 // Uniform, const, input can't be modified
117 "can't modify a",
118 "global variable initializers must be constant expressions",
119 ("must explicitly specify all locations when using multiple fragment "
120 "outputs"),
121 };
122
123 // Filter errors which we don't think interfere with fuzzing everything.
ErrorOk(const base::StringPiece line)124 bool ErrorOk(const base::StringPiece line) {
125 for (base::StringPiece acceptable_error : acceptable_errors) {
126 if (line.find(acceptable_error) != base::StringPiece::npos) {
127 return true;
128 }
129 }
130 LOG(WARNING) << "failed due to line: " << line;
131 return false;
132 }
133
ErrorsOk(const base::StringPiece log)134 bool ErrorsOk(const base::StringPiece log) {
135 std::vector<std::string> lines = base::SplitString(
136 log, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
137 for (const auto& line : lines) {
138 if (!ErrorOk(line)) {
139 return false;
140 }
141 }
142 return true;
143 }
144
LoadShader(GLenum type,const fuzzing::Shader & shader_proto)145 GLuint LoadShader(GLenum type, const fuzzing::Shader& shader_proto) {
146 std::string shader_s = gl_lpm_fuzzer::GetShader(shader_proto);
147 if (shader_s.empty()) {
148 return 0;
149 }
150
151 GLuint shader = CompileShader(type, shader_s.c_str());
152
153 // Check the compile status
154 GLint value = 0;
155 glGetShaderiv(shader, GL_COMPILE_STATUS, &value);
156 if (value == 0) {
157 if (CHECK_FOR_UNKNOWN_ERRORS) {
158 GLint log_length = 0;
159 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
160 auto buffer = std::make_unique<GLchar[]>(log_length);
161 glGetShaderInfoLog(shader, log_length, /*length=*/nullptr, buffer.get());
162 base::StringPiece log(buffer.get(), log_length);
163 if (!ErrorsOk(log)) {
164 LOG(FATAL) << "Encountered an unexpected failure when translating:\n"
165 << log << "\nfailed to compile shader:\n"
166 << shader_proto.DebugString() << "converted:\n"
167 << shader_s;
168 }
169 }
170 glDeleteShader(shader);
171 shader = 0;
172 }
173 return shader;
174 }
175
176 // Same as GLTestHelper::SetupProgram but does not expect shaders
177 // to link successfully.
SetupProgram(GLuint vertex_shader,GLuint fragment_shader)178 GLuint SetupProgram(GLuint vertex_shader, GLuint fragment_shader) {
179 GLuint program =
180 gpu::GLTestHelper::LinkProgram(vertex_shader, fragment_shader);
181 // Check the link status
182 GLint linked = 0;
183 glGetProgramiv(program, GL_LINK_STATUS, &linked);
184 if (linked == 0) {
185 if (CHECK_FOR_UNKNOWN_ERRORS) {
186 GLint log_length = 0;
187 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
188 auto buffer = std::make_unique<GLchar[]>(log_length);
189 glGetProgramInfoLog(program, log_length, /*length=*/nullptr,
190 buffer.get());
191 base::StringPiece log(buffer.get(), log_length);
192 LOG(WARNING) << "Error linking program: " << log;
193 }
194 glDeleteProgram(program);
195 program = 0;
196 }
197 return program;
198 }
199
DEFINE_PROTO_FUZZER(const fuzzing::Session & session)200 DEFINE_PROTO_FUZZER(const fuzzing::Session& session) {
201 static Env* env = new Env();
202 CHECK(env);
203 // TODO(nedwill): Creating a new GLManager on each iteration
204 // is expensive. We should investigate ways to avoid expensive
205 // initialization.
206 ScopedGLManager scoped_gl_manager;
207
208 GLuint vertex_shader_id =
209 LoadShader(GL_VERTEX_SHADER, session.vertex_shader());
210 GLuint fragment_shader_id =
211 LoadShader(GL_FRAGMENT_SHADER, session.fragment_shader());
212 if (!vertex_shader_id || !fragment_shader_id) {
213 return;
214 }
215
216 GLuint program = SetupProgram(vertex_shader_id, fragment_shader_id);
217 if (!program) {
218 return;
219 }
220
221 glUseProgram(program);
222 // Relink program.
223 glLinkProgram(program);
224 }
225