1 //
2 // Copyright 2002 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 #include <sstream>
7 #include <string>
8 #include <vector>
9 #include "GLSLANG/ShaderLang.h"
10 #include "angle_gl.h"
11 #include "gtest/gtest.h"
12
13 class ExpressionLimitTest : public testing::Test
14 {
15 protected:
16 static const int kMaxExpressionComplexity = 16;
17 static const int kMaxCallStackDepth = 16;
18 static const int kMaxFunctionParameters = 16;
19 static const char *kExpressionTooComplex;
20 static const char *kCallStackTooDeep;
21 static const char *kHasRecursion;
22 static const char *kTooManyParameters;
23 static const char *kTooComplexSwitch;
24 static const char *kGlobalVariableInit;
25
SetUp()26 virtual void SetUp()
27 {
28 memset(&resources, 0, sizeof(resources));
29
30 GenerateResources(&resources);
31 }
32
33 // Set up the per compile resources
GenerateResources(ShBuiltInResources * res)34 static void GenerateResources(ShBuiltInResources *res)
35 {
36 sh::InitBuiltInResources(res);
37
38 res->MaxVertexAttribs = 8;
39 res->MaxVertexUniformVectors = 128;
40 res->MaxVaryingVectors = 8;
41 res->MaxVertexTextureImageUnits = 0;
42 res->MaxCombinedTextureImageUnits = 8;
43 res->MaxTextureImageUnits = 8;
44 res->MaxFragmentUniformVectors = 16;
45 res->MaxDrawBuffers = 1;
46
47 res->OES_standard_derivatives = 0;
48 res->OES_EGL_image_external = 0;
49
50 res->MaxExpressionComplexity = kMaxExpressionComplexity;
51 res->MaxCallStackDepth = kMaxCallStackDepth;
52 res->MaxFunctionParameters = kMaxFunctionParameters;
53 }
54
GenerateLongExpression(int length,std::stringstream * ss)55 static void GenerateLongExpression(int length, std::stringstream *ss)
56 {
57 for (int ii = 0; ii < length; ++ii)
58 {
59 *ss << "+ vec4(" << ii << ")";
60 }
61 }
62
GenerateShaderWithLongExpression(int length)63 static std::string GenerateShaderWithLongExpression(int length)
64 {
65 static const char *shaderStart =
66 R"(precision mediump float;
67 uniform vec4 u_color;
68 void main()
69 {
70 gl_FragColor = u_color
71 )";
72
73 std::stringstream ss;
74 ss << shaderStart;
75 GenerateLongExpression(length, &ss);
76 ss << "; }";
77
78 return ss.str();
79 }
80
GenerateShaderWithUnusedLongExpression(int length)81 static std::string GenerateShaderWithUnusedLongExpression(int length)
82 {
83 static const char *shaderStart =
84 R"(precision mediump float;
85 uniform vec4 u_color;
86 void main()
87 {
88 gl_FragColor = u_color;
89 }
90 vec4 someFunction() {
91 return u_color
92 )";
93
94 std::stringstream ss;
95
96 ss << shaderStart;
97 GenerateLongExpression(length, &ss);
98 ss << "; }";
99
100 return ss.str();
101 }
102
GenerateDeepFunctionStack(int length,std::stringstream * ss)103 static void GenerateDeepFunctionStack(int length, std::stringstream *ss)
104 {
105 static const char *shaderStart =
106 R"(precision mediump float;
107 uniform vec4 u_color;
108 vec4 function0() {
109 return u_color;
110 }
111 )";
112
113 *ss << shaderStart;
114 for (int ii = 0; ii < length; ++ii)
115 {
116 *ss << "vec4 function" << (ii + 1) << "() {\n"
117 << " return function" << ii << "();\n"
118 << "}\n";
119 }
120 }
121
GenerateShaderWithDeepFunctionStack(int length)122 static std::string GenerateShaderWithDeepFunctionStack(int length)
123 {
124 std::stringstream ss;
125
126 GenerateDeepFunctionStack(length, &ss);
127
128 ss << "void main() {\n"
129 << " gl_FragColor = function" << length << "();\n"
130 << "}";
131
132 return ss.str();
133 }
134
GenerateShaderWithUnusedDeepFunctionStack(int length)135 static std::string GenerateShaderWithUnusedDeepFunctionStack(int length)
136 {
137 std::stringstream ss;
138
139 GenerateDeepFunctionStack(length, &ss);
140
141 ss << "void main() {\n"
142 << " gl_FragColor = vec4(0,0,0,0);\n"
143 << "}";
144
145 return ss.str();
146 }
147
GenerateShaderWithFunctionParameters(int parameters)148 static std::string GenerateShaderWithFunctionParameters(int parameters)
149 {
150 std::stringstream ss;
151
152 ss << "precision mediump float;\n"
153 << "\n"
154 << "float foo(";
155 for (int i = 0; i < parameters; ++i)
156 {
157 ss << "float f" << i;
158 if (i + 1 < parameters)
159 {
160 ss << ", ";
161 }
162 }
163 ss << ")\n"
164 << "{\n"
165 << " return f0;\n"
166 << "}\n"
167 << "\n"
168 << "void main()\n"
169 << "{\n"
170 << " gl_FragColor = vec4(0,0,0,0);\n"
171 << "}";
172
173 return ss.str();
174 }
175
GenerateShaderWithNestingInsideSwitch(int nesting)176 static std::string GenerateShaderWithNestingInsideSwitch(int nesting)
177 {
178 std::stringstream shaderString;
179 shaderString <<
180 R"(#version 300 es
181 uniform int u;
182
183 void main()
184 {
185 int x;
186 switch (u)
187 {
188 case 0:
189 x = x)";
190 for (int i = 0; i < nesting; ++i)
191 {
192 shaderString << " + x";
193 }
194 shaderString <<
195 R"(;
196 } // switch (u)
197 })";
198 return shaderString.str();
199 }
200
GenerateShaderWithNestingInsideGlobalInitializer(int nesting)201 static std::string GenerateShaderWithNestingInsideGlobalInitializer(int nesting)
202 {
203 std::stringstream shaderString;
204 shaderString <<
205 R"(uniform int u;
206 int x = u)";
207
208 for (int i = 0; i < nesting; ++i)
209 {
210 shaderString << " + u";
211 }
212 shaderString << R"(;
213 void main()
214 {
215 gl_FragColor = vec4(0.0);
216 })";
217 return shaderString.str();
218 }
219
220 // Compiles a shader and if there's an error checks for a specific
221 // substring in the error log. This way we know the error is specific
222 // to the issue we are testing.
CheckShaderCompilation(ShHandle compiler,const char * source,ShCompileOptions compileOptions,const char * expected_error)223 bool CheckShaderCompilation(ShHandle compiler,
224 const char *source,
225 ShCompileOptions compileOptions,
226 const char *expected_error)
227 {
228 bool success = sh::Compile(compiler, &source, 1, compileOptions) != 0;
229 if (success)
230 {
231 success = !expected_error;
232 }
233 else
234 {
235 std::string log = sh::GetInfoLog(compiler);
236 if (expected_error)
237 success = log.find(expected_error) != std::string::npos;
238
239 EXPECT_TRUE(success) << log << "\n----shader----\n" << source;
240 }
241 return success;
242 }
243
244 ShBuiltInResources resources;
245 };
246
247 const char *ExpressionLimitTest::kExpressionTooComplex = "Expression too complex";
248 const char *ExpressionLimitTest::kCallStackTooDeep = "Call stack too deep";
249 const char *ExpressionLimitTest::kHasRecursion =
250 "Recursive function call in the following call chain";
251 const char *ExpressionLimitTest::kTooManyParameters = "Function has too many parameters";
252 const char *ExpressionLimitTest::kTooComplexSwitch =
253 "too complex expressions inside a switch statement";
254 const char *ExpressionLimitTest::kGlobalVariableInit =
255 "global variable initializers must be constant expressions";
256
TEST_F(ExpressionLimitTest,ExpressionComplexity)257 TEST_F(ExpressionLimitTest, ExpressionComplexity)
258 {
259 ShShaderSpec spec = SH_WEBGL_SPEC;
260 ShShaderOutput output = SH_ESSL_OUTPUT;
261 ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
262 ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
263
264 // Test expression under the limit passes.
265 EXPECT_TRUE(CheckShaderCompilation(
266 vertexCompiler, GenerateShaderWithLongExpression(kMaxExpressionComplexity - 10).c_str(),
267 compileOptions, nullptr));
268 // Test expression over the limit fails.
269 EXPECT_TRUE(CheckShaderCompilation(
270 vertexCompiler, GenerateShaderWithLongExpression(kMaxExpressionComplexity + 10).c_str(),
271 compileOptions, kExpressionTooComplex));
272 // Test expression over the limit without a limit does not fail.
273 EXPECT_TRUE(CheckShaderCompilation(
274 vertexCompiler, GenerateShaderWithLongExpression(kMaxExpressionComplexity + 10).c_str(),
275 compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr));
276 sh::Destruct(vertexCompiler);
277 }
278
TEST_F(ExpressionLimitTest,UnusedExpressionComplexity)279 TEST_F(ExpressionLimitTest, UnusedExpressionComplexity)
280 {
281 ShShaderSpec spec = SH_WEBGL_SPEC;
282 ShShaderOutput output = SH_ESSL_OUTPUT;
283 ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
284 ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
285
286 // Test expression under the limit passes.
287 EXPECT_TRUE(CheckShaderCompilation(
288 vertexCompiler,
289 GenerateShaderWithUnusedLongExpression(kMaxExpressionComplexity - 10).c_str(),
290 compileOptions, nullptr));
291 // Test expression over the limit fails.
292 EXPECT_TRUE(CheckShaderCompilation(
293 vertexCompiler,
294 GenerateShaderWithUnusedLongExpression(kMaxExpressionComplexity + 10).c_str(),
295 compileOptions, kExpressionTooComplex));
296 // Test expression over the limit without a limit does not fail.
297 EXPECT_TRUE(CheckShaderCompilation(
298 vertexCompiler,
299 GenerateShaderWithUnusedLongExpression(kMaxExpressionComplexity + 10).c_str(),
300 compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr));
301 sh::Destruct(vertexCompiler);
302 }
303
TEST_F(ExpressionLimitTest,CallStackDepth)304 TEST_F(ExpressionLimitTest, CallStackDepth)
305 {
306 ShShaderSpec spec = SH_WEBGL_SPEC;
307 ShShaderOutput output = SH_ESSL_OUTPUT;
308 ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
309 ShCompileOptions compileOptions = SH_LIMIT_CALL_STACK_DEPTH;
310
311 // Test call stack under the limit passes.
312 EXPECT_TRUE(CheckShaderCompilation(
313 vertexCompiler, GenerateShaderWithDeepFunctionStack(kMaxCallStackDepth - 10).c_str(),
314 compileOptions, nullptr));
315 // Test call stack over the limit fails.
316 EXPECT_TRUE(CheckShaderCompilation(
317 vertexCompiler, GenerateShaderWithDeepFunctionStack(kMaxCallStackDepth + 10).c_str(),
318 compileOptions, kCallStackTooDeep));
319 // Test call stack over the limit without limit does not fail.
320 EXPECT_TRUE(CheckShaderCompilation(
321 vertexCompiler, GenerateShaderWithDeepFunctionStack(kMaxCallStackDepth + 10).c_str(),
322 compileOptions & ~SH_LIMIT_CALL_STACK_DEPTH, nullptr));
323 sh::Destruct(vertexCompiler);
324 }
325
TEST_F(ExpressionLimitTest,UnusedCallStackDepth)326 TEST_F(ExpressionLimitTest, UnusedCallStackDepth)
327 {
328 ShShaderSpec spec = SH_WEBGL_SPEC;
329 ShShaderOutput output = SH_ESSL_OUTPUT;
330 ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
331 ShCompileOptions compileOptions = SH_LIMIT_CALL_STACK_DEPTH;
332
333 // Test call stack under the limit passes.
334 EXPECT_TRUE(CheckShaderCompilation(
335 vertexCompiler, GenerateShaderWithUnusedDeepFunctionStack(kMaxCallStackDepth - 10).c_str(),
336 compileOptions, nullptr));
337 // Test call stack over the limit fails.
338 EXPECT_TRUE(CheckShaderCompilation(
339 vertexCompiler, GenerateShaderWithUnusedDeepFunctionStack(kMaxCallStackDepth + 10).c_str(),
340 compileOptions, kCallStackTooDeep));
341 // Test call stack over the limit without limit does not fail.
342 EXPECT_TRUE(CheckShaderCompilation(
343 vertexCompiler, GenerateShaderWithUnusedDeepFunctionStack(kMaxCallStackDepth + 10).c_str(),
344 compileOptions & ~SH_LIMIT_CALL_STACK_DEPTH, nullptr));
345 sh::Destruct(vertexCompiler);
346 }
347
TEST_F(ExpressionLimitTest,Recursion)348 TEST_F(ExpressionLimitTest, Recursion)
349 {
350 ShShaderSpec spec = SH_WEBGL_SPEC;
351 ShShaderOutput output = SH_ESSL_OUTPUT;
352 ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
353 ShCompileOptions compileOptions = 0;
354
355 static const char *shaderWithRecursion0 =
356 R"(precision mediump float;
357 uniform vec4 u_color;
358 vec4 someFunc() {
359 return someFunc();
360 }
361
362 void main() {
363 gl_FragColor = u_color * someFunc();
364 }
365 )";
366
367 static const char *shaderWithRecursion1 =
368 R"(precision mediump float;
369 uniform vec4 u_color;
370
371 vec4 someFunc();
372
373 vec4 someFunc1() {
374 return someFunc();
375 }
376
377 vec4 someFunc() {
378 return someFunc1();
379 }
380
381 void main() {
382 gl_FragColor = u_color * someFunc();
383 }
384 )";
385
386 static const char *shaderWithRecursion2 =
387 R"(precision mediump float;
388 uniform vec4 u_color;
389 vec4 someFunc() {
390 if (u_color.x > 0.5) {
391 return someFunc();
392 } else {
393 return vec4(1);
394 }
395 }
396
397 void main() {
398 gl_FragColor = someFunc();
399 }
400 )";
401
402 static const char *shaderWithRecursion3 =
403 R"(precision mediump float;
404 uniform vec4 u_color;
405 vec4 someFunc() {
406 if (u_color.x > 0.5) {
407 return vec4(1);
408 } else {
409 return someFunc();
410 }
411 }
412
413 void main() {
414 gl_FragColor = someFunc();
415 }
416 )";
417
418 static const char *shaderWithRecursion4 =
419 R"(precision mediump float;
420 uniform vec4 u_color;
421 vec4 someFunc() {
422 return (u_color.x > 0.5) ? vec4(1) : someFunc();
423 }
424
425 void main() {
426 gl_FragColor = someFunc();
427 }
428 )";
429
430 static const char *shaderWithRecursion5 =
431 R"(precision mediump float;
432 uniform vec4 u_color;
433 vec4 someFunc() {
434 return (u_color.x > 0.5) ? someFunc() : vec4(1);
435 }
436
437 void main() {
438 gl_FragColor = someFunc();
439 }
440 )";
441
442 static const char *shaderWithRecursion6 =
443 R"(precision mediump float;
444 uniform vec4 u_color;
445 vec4 someFunc() {
446 return someFunc();
447 }
448
449 void main() {
450 gl_FragColor = u_color;
451 }
452 )";
453
454 static const char *shaderWithNoRecursion =
455 R"(precision mediump float;
456 uniform vec4 u_color;
457
458 vec3 rgb(int r, int g, int b) {
459 return vec3(float(r) / 255.0, float(g) / 255.0, float(b) / 255.0);
460 }
461
462 void main() {
463 vec3 hairColor0 = rgb(151, 200, 234);
464 vec3 faceColor2 = rgb(183, 148, 133);
465 gl_FragColor = u_color + vec4(hairColor0 + faceColor2, 0);
466 }
467 )";
468
469 static const char *shaderWithRecursion7 =
470 R"(precision mediump float;
471 uniform vec4 u_color;
472
473 vec4 function2() {
474 return u_color;
475 }
476
477 vec4 function1() {
478 vec4 a = function2();
479 vec4 b = function1();
480 return a + b;
481 }
482
483 void main() {
484 gl_FragColor = function1();
485 }
486 )";
487
488 static const char *shaderWithRecursion8 =
489 R"(precision mediump float;
490 uniform vec4 u_color;
491
492 vec4 function1();
493
494 vec4 function3() {
495 return function1();
496 }
497
498 vec4 function2() {
499 return function3();
500 }
501
502 vec4 function1() {
503 return function2();
504 }
505
506 void main() {
507 gl_FragColor = function1();
508 }
509 )";
510
511 // Check simple recursions fails.
512 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion0, compileOptions,
513 kHasRecursion));
514 // Check simple recursions fails.
515 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion1, compileOptions,
516 kHasRecursion));
517 // Check if recursions fails.
518 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion2, compileOptions,
519 kHasRecursion));
520 // Check if recursions fails.
521 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion3, compileOptions,
522 kHasRecursion));
523 // Check ternary recursions fails.
524 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion4, compileOptions,
525 kHasRecursion));
526 // Check ternary recursions fails.
527 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion5, compileOptions,
528 kHasRecursion));
529
530 // Check some more forms of recursion
531 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion6, compileOptions,
532 kHasRecursion));
533 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion7, compileOptions,
534 kHasRecursion));
535 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion8, compileOptions,
536 kHasRecursion));
537 // Check unused recursions fails if limiting call stack
538 // since we check all paths.
539 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion6,
540 compileOptions | SH_LIMIT_CALL_STACK_DEPTH, kHasRecursion));
541
542 // Check unused recursions passes.
543 EXPECT_TRUE(
544 CheckShaderCompilation(vertexCompiler, shaderWithNoRecursion, compileOptions, nullptr));
545 // Check unused recursions passes if limiting call stack.
546 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithNoRecursion,
547 compileOptions | SH_LIMIT_CALL_STACK_DEPTH, nullptr));
548 sh::Destruct(vertexCompiler);
549 }
550
TEST_F(ExpressionLimitTest,FunctionParameterCount)551 TEST_F(ExpressionLimitTest, FunctionParameterCount)
552 {
553 ShShaderSpec spec = SH_WEBGL_SPEC;
554 ShShaderOutput output = SH_ESSL_OUTPUT;
555 ShHandle compiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
556 ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
557
558 // Test parameters under the limit succeeds.
559 EXPECT_TRUE(CheckShaderCompilation(
560 compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters).c_str(),
561 compileOptions, nullptr));
562 // Test parameters over the limit fails.
563 EXPECT_TRUE(CheckShaderCompilation(
564 compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters + 1).c_str(),
565 compileOptions, kTooManyParameters));
566 // Test parameters over the limit without limit does not fail.
567 EXPECT_TRUE(CheckShaderCompilation(
568 compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters + 1).c_str(),
569 compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr));
570 sh::Destruct(compiler);
571 }
572
TEST_F(ExpressionLimitTest,NestingInsideSwitch)573 TEST_F(ExpressionLimitTest, NestingInsideSwitch)
574 {
575 ShShaderSpec spec = SH_WEBGL2_SPEC;
576 ShShaderOutput output = SH_ESSL_OUTPUT;
577 ShHandle compiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
578 ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
579
580 // Test nesting over the limit fails.
581 EXPECT_TRUE(CheckShaderCompilation(
582 compiler, GenerateShaderWithNestingInsideSwitch(kMaxExpressionComplexity + 1).c_str(),
583 compileOptions, kExpressionTooComplex));
584 // Test nesting over the limit without limit does not fail.
585 EXPECT_TRUE(CheckShaderCompilation(
586 compiler, GenerateShaderWithNestingInsideSwitch(kMaxExpressionComplexity + 1).c_str(),
587 compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr));
588 // Test that nesting way over the limit doesn't cause stack overflow but is handled
589 // gracefully.
590 EXPECT_TRUE(CheckShaderCompilation(compiler,
591 GenerateShaderWithNestingInsideSwitch(5000).c_str(),
592 compileOptions, kTooComplexSwitch));
593 sh::Destruct(compiler);
594 }
595
TEST_F(ExpressionLimitTest,NestingInsideGlobalInitializer)596 TEST_F(ExpressionLimitTest, NestingInsideGlobalInitializer)
597 {
598 ShShaderSpec spec = SH_WEBGL_SPEC;
599 ShShaderOutput output = SH_ESSL_OUTPUT;
600 ShHandle compiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
601 ShCompileOptions compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
602
603 // Test nesting over the limit fails.
604 EXPECT_TRUE(CheckShaderCompilation(
605 compiler,
606 GenerateShaderWithNestingInsideGlobalInitializer(kMaxExpressionComplexity + 1).c_str(),
607 compileOptions, kExpressionTooComplex));
608 // Test nesting over the limit without limit does not fail.
609 EXPECT_TRUE(CheckShaderCompilation(
610 compiler,
611 GenerateShaderWithNestingInsideGlobalInitializer(kMaxExpressionComplexity + 1).c_str(),
612 compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr));
613 // Test that nesting way over the limit doesn't cause stack overflow but is handled
614 // gracefully.
615 EXPECT_TRUE(CheckShaderCompilation(
616 compiler, GenerateShaderWithNestingInsideGlobalInitializer(5000).c_str(), compileOptions,
617 kGlobalVariableInit));
618 sh::Destruct(compiler);
619 }
620