Add option to limit the number of function parameters
Trying to compile user-defined functions that have thousands of
parameters introduces some instability in native compilers, so it is
better to reject shaders with large numbers of function parameters
in ANGLE.
The check is only enabled if the SH_LIMIT_EXPRESSION_COMPLEXITY flag
is turned on. The default limit for the number of parameters is 1024,
but it can also be configured.
BUG=angleproject:1338
TEST=angle_unittests
Change-Id: I5c9b7a4e97e67f36e77f969368336fa8fffba1c3
Reviewed-on: https://chromium-review.googlesource.com/331970
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler.gypi b/src/compiler.gypi
index 36a3597..c588913 100644
--- a/src/compiler.gypi
+++ b/src/compiler.gypi
@@ -98,6 +98,8 @@
'compiler/translator/ValidateGlobalInitializer.h',
'compiler/translator/ValidateLimitations.cpp',
'compiler/translator/ValidateLimitations.h',
+ 'compiler/translator/ValidateMaxParameters.h',
+ 'compiler/translator/ValidateMaxParameters.cpp',
'compiler/translator/ValidateOutputs.cpp',
'compiler/translator/ValidateOutputs.h',
'compiler/translator/ValidateSwitch.cpp',
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 18524ce..77360d2 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -20,6 +20,7 @@
#include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
#include "compiler/translator/UnfoldShortCircuitAST.h"
#include "compiler/translator/ValidateLimitations.h"
+#include "compiler/translator/ValidateMaxParameters.h"
#include "compiler/translator/ValidateOutputs.h"
#include "compiler/translator/VariablePacker.h"
#include "compiler/translator/depgraph/DependencyGraph.h"
@@ -141,6 +142,7 @@
maxUniformVectors(0),
maxExpressionComplexity(0),
maxCallStackDepth(0),
+ maxFunctionParameters(0),
fragmentPrecisionHigh(false),
clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC),
builtInFunctionEmulator(),
@@ -169,7 +171,8 @@
resources.MaxVertexUniformVectors :
resources.MaxFragmentUniformVectors;
maxExpressionComplexity = resources.MaxExpressionComplexity;
- maxCallStackDepth = resources.MaxCallStackDepth;
+ maxCallStackDepth = resources.MaxCallStackDepth;
+ maxFunctionParameters = resources.MaxFunctionParameters;
SetGlobalPoolAllocator(&allocator);
@@ -486,6 +489,7 @@
<< ":FragmentPrecisionHigh:" << compileResources.FragmentPrecisionHigh
<< ":MaxExpressionComplexity:" << compileResources.MaxExpressionComplexity
<< ":MaxCallStackDepth:" << compileResources.MaxCallStackDepth
+ << ":MaxFunctionParameters:" << compileResources.MaxFunctionParameters
<< ":EXT_blend_func_extended:" << compileResources.EXT_blend_func_extended
<< ":EXT_frag_depth:" << compileResources.EXT_frag_depth
<< ":EXT_shader_texture_lod:" << compileResources.EXT_shader_texture_lod
@@ -747,6 +751,12 @@
return false;
}
+ if (!ValidateMaxParameters::validate(root, maxFunctionParameters))
+ {
+ infoSink.info << "Function has too many parameters.";
+ return false;
+ }
+
return true;
}
diff --git a/src/compiler/translator/Compiler.h b/src/compiler/translator/Compiler.h
index c00a8f9..99c155c 100644
--- a/src/compiler/translator/Compiler.h
+++ b/src/compiler/translator/Compiler.h
@@ -208,6 +208,7 @@
int maxUniformVectors;
int maxExpressionComplexity;
int maxCallStackDepth;
+ int maxFunctionParameters;
ShBuiltInResources compileResources;
std::string builtInResourcesString;
diff --git a/src/compiler/translator/ShaderLang.cpp b/src/compiler/translator/ShaderLang.cpp
index e257f93..04be236 100644
--- a/src/compiler/translator/ShaderLang.cpp
+++ b/src/compiler/translator/ShaderLang.cpp
@@ -178,7 +178,8 @@
resources->ArrayIndexClampingStrategy = SH_CLAMP_WITH_CLAMP_INTRINSIC;
resources->MaxExpressionComplexity = 256;
- resources->MaxCallStackDepth = 256;
+ resources->MaxCallStackDepth = 256;
+ resources->MaxFunctionParameters = 1024;
}
//
diff --git a/src/compiler/translator/ValidateMaxParameters.cpp b/src/compiler/translator/ValidateMaxParameters.cpp
new file mode 100644
index 0000000..00b3c9b
--- /dev/null
+++ b/src/compiler/translator/ValidateMaxParameters.cpp
@@ -0,0 +1,35 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ValidateMaxParameters checks if function definitions have more than a set number of parameters.
+
+#include "compiler/translator/ValidateMaxParameters.h"
+
+ValidateMaxParameters::ValidateMaxParameters(unsigned int maxParameters)
+ : TIntermTraverser(true, false, false), mMaxParameters(maxParameters), mValid(true)
+{
+}
+
+bool ValidateMaxParameters::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (!mValid)
+ {
+ return false;
+ }
+
+ if (node->getOp() == EOpParameters && node->getSequence()->size() > mMaxParameters)
+ {
+ mValid = false;
+ }
+
+ return mValid;
+}
+
+bool ValidateMaxParameters::validate(TIntermNode *root, unsigned int maxParameters)
+{
+ ValidateMaxParameters argsTraverser(maxParameters);
+ root->traverse(&argsTraverser);
+ return argsTraverser.mValid;
+}
diff --git a/src/compiler/translator/ValidateMaxParameters.h b/src/compiler/translator/ValidateMaxParameters.h
new file mode 100644
index 0000000..87916af
--- /dev/null
+++ b/src/compiler/translator/ValidateMaxParameters.h
@@ -0,0 +1,29 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ValidateMaxParameters checks if function definitions have more than a set number of parameters.
+
+#ifndef COMPILER_TRANSLATOR_VALIDATEMAXPARAMETERS_H_
+#define COMPILER_TRANSLATOR_VALIDATEMAXPARAMETERS_H_
+
+#include "compiler/translator/IntermNode.h"
+
+class ValidateMaxParameters : public TIntermTraverser
+{
+ public:
+ // Returns false if maxParameters is exceeded.
+ static bool validate(TIntermNode *root, unsigned int maxParameters);
+
+ protected:
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+
+ private:
+ ValidateMaxParameters(unsigned int maxParameters);
+
+ unsigned int mMaxParameters;
+ bool mValid;
+};
+
+#endif // COMPILER_TRANSLATOR_VALIDATEMAXPARAMETERS_H_
diff --git a/src/tests/compiler_tests/ExpressionLimit_test.cpp b/src/tests/compiler_tests/ExpressionLimit_test.cpp
index da9ae36..5d80f2a 100644
--- a/src/tests/compiler_tests/ExpressionLimit_test.cpp
+++ b/src/tests/compiler_tests/ExpressionLimit_test.cpp
@@ -15,10 +15,12 @@
class ExpressionLimitTest : public testing::Test {
protected:
static const int kMaxExpressionComplexity = 16;
- static const int kMaxCallStackDepth = 16;
+ static const int kMaxCallStackDepth = 16;
+ static const int kMaxFunctionParameters = 16;
static const char* kExpressionTooComplex;
static const char* kCallStackTooDeep;
static const char* kHasRecursion;
+ static const char *kTooManyParameters;
virtual void SetUp()
{
@@ -46,6 +48,7 @@
res->MaxExpressionComplexity = kMaxExpressionComplexity;
res->MaxCallStackDepth = kMaxCallStackDepth;
+ res->MaxFunctionParameters = kMaxFunctionParameters;
}
void GenerateLongExpression(int length, std::stringstream* ss)
@@ -140,6 +143,34 @@
return ss.str();
}
+ std::string GenerateShaderWithFunctionParameters(int parameters)
+ {
+ std::stringstream ss;
+
+ ss << "precision mediump float;\n"
+ << "\n"
+ << "float foo(";
+ for (int i = 0; i < parameters; ++i)
+ {
+ ss << "float f" << i;
+ if (i + 1 < parameters)
+ {
+ ss << ", ";
+ }
+ }
+ ss << ")\n"
+ << "{\n"
+ << " return f0;\n"
+ << "}\n"
+ << "\n"
+ << "void main()\n"
+ << "{\n"
+ << " gl_FragColor = vec4(0,0,0,0);\n"
+ << "}";
+
+ return ss.str();
+ }
+
// Compiles a shader and if there's an error checks for a specific
// substring in the error log. This way we know the error is specific
// to the issue we are testing.
@@ -173,6 +204,8 @@
"Call stack too deep";
const char* ExpressionLimitTest::kHasRecursion =
"Function recursion detected";
+const char* ExpressionLimitTest::kTooManyParameters =
+ "Function has too many parameters";
TEST_F(ExpressionLimitTest, ExpressionComplexity)
{
@@ -506,3 +539,24 @@
ShDestruct(vertexCompiler);
}
+TEST_F(ExpressionLimitTest, FunctionParameterCount)
+{
+ ShShaderSpec spec = SH_WEBGL_SPEC;
+ ShShaderOutput output = SH_ESSL_OUTPUT;
+ ShHandle compiler = ShConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
+ int compileOptions = SH_LIMIT_EXPRESSION_COMPLEXITY;
+
+ // Test parameters under the limit succeeds.
+ EXPECT_TRUE(CheckShaderCompilation(
+ compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters).c_str(),
+ compileOptions, nullptr));
+ // Test parameters over the limit fails.
+ EXPECT_TRUE(CheckShaderCompilation(
+ compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters + 1).c_str(),
+ compileOptions, kTooManyParameters));
+ // Test parameters over the limit without limit does not fail.
+ EXPECT_TRUE(CheckShaderCompilation(
+ compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters + 1).c_str(),
+ compileOptions & ~SH_LIMIT_EXPRESSION_COMPLEXITY, nullptr));
+ ShDestruct(compiler);
+}