Reject large arrays in GLSL

The compiler tries to allocate arrays of uniforms before it checks their size. So large arrays will cause the compiler to allocate all of its memory.

This change makes the compiler reject oversized arrays and returns a compilation error.

Bug chromium:872321

Change-Id: I3441232cca129bd2abd181990bec457ad7f43d05
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/29333
Tested-by: Sean Risser <srisser@google.com>
Presubmit-Ready: Sean Risser <srisser@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/OpenGL/compiler/OutputASM.cpp b/src/OpenGL/compiler/OutputASM.cpp
index 75f7167..54c6a04 100644
--- a/src/OpenGL/compiler/OutputASM.cpp
+++ b/src/OpenGL/compiler/OutputASM.cpp
@@ -3124,6 +3124,10 @@
 		if(var == -1)
 		{
 			var = allocate(varyings, varying);
+			if (var == -1)
+			{
+				return 0;
+			}
 			int registerCount = varying->totalRegisterCount();
 
 			if(pixelShader)
@@ -3300,6 +3304,10 @@
 				if(index == -1)
 				{
 					index = allocate(uniforms, uniform);
+					if (index == -1)
+					{
+						return 0;
+					}
 				}
 
 				// Verify if the current uniform is a member of an already declared block
@@ -3335,6 +3343,10 @@
 			if(symbol)
 			{
 				index = allocate(attributes, attribute);
+				if (index == -1)
+				{
+					return -1;
+				}
 				const TType &type = attribute->getType();
 				int registerCount = attribute->totalRegisterCount();
 				sw::VertexShader::AttribType attribType = sw::VertexShader::ATTRIBTYPE_FLOAT;
@@ -3453,6 +3465,10 @@
 		if(index == -1)
 		{
 			index = allocate(samplers, sampler, true);
+			if (index == -1)
+			{
+				return 0;
+			}
 
 			if(sampler->getQualifier() == EvqUniform)
 			{
@@ -3469,6 +3485,33 @@
 		return operand && IsSampler(operand->getBasicType()) && samplerRegister(operand) >= 0;
 	}
 
+	bool OutputASM::arrayExceedsLimits(TIntermTyped *operand)
+	{
+		const TVariable *maxUniformVectors = nullptr;
+		TString builtinName = "";
+		if (vertexShader)
+		{
+			builtinName = "gl_MaxVertexUniformVectors";
+		}
+		else if (pixelShader)
+		{
+			builtinName = "gl_MaxFragmentUniformVectors";
+		}
+		maxUniformVectors = static_cast<const TVariable *>(mContext.symbolTable.findBuiltIn(builtinName.c_str(), mContext.getShaderVersion()));
+		if (operand->getArraySize() > maxUniformVectors->getConstPointer()->getIConst())
+		{
+			std::stringstream extraInfoStream;
+			extraInfoStream << "Array size (" << operand->getArraySize() << ") "
+			                << "exceeds limit of " << builtinName
+			                << " (" << maxUniformVectors->getConstPointer()->getIConst() << ")";
+			std::string errorStr = extraInfoStream.str();
+			mContext.error(operand->getLine(), errorStr.c_str(),
+			               operand->getBasicString());
+			return true;
+		}
+		return false;
+	}
+
 	int OutputASM::lookup(VariableArray &list, TIntermTyped *variable)
 	{
 		for(unsigned int i = 0; i < list.size(); i++)
@@ -3549,6 +3592,10 @@
 
 		if(index == -1)
 		{
+			if (arrayExceedsLimits(variable))
+			{
+				return -1;
+			}
 			unsigned int registerCount = variable->blockRegisterCount(samplersOnly);
 
 			for(unsigned int i = 0; i < list.size(); i++)
diff --git a/src/OpenGL/compiler/OutputASM.h b/src/OpenGL/compiler/OutputASM.h
index 1b08de0..9ac2e8d 100644
--- a/src/OpenGL/compiler/OutputASM.h
+++ b/src/OpenGL/compiler/OutputASM.h
@@ -303,12 +303,14 @@
 		int samplerRegister(TIntermTyped *sampler);
 		int samplerRegister(TIntermSymbol *sampler);
 		bool isSamplerRegister(TIntermTyped *operand);
+		bool arrayExceedsLimits(TIntermTyped *operand);
 
 		typedef std::vector<TIntermTyped*> VariableArray;
 
 		int lookup(VariableArray &list, TIntermTyped *variable);
 		int lookup(VariableArray &list, TInterfaceBlock *block);
 		int blockMemberLookup(const TType &type, const TString &name, int registerIndex);
+		// Returns -1 if it fails to allocate variable.
 		int allocate(VariableArray &list, TIntermTyped *variable, bool samplersOnly = false);
 		void free(VariableArray &list, TIntermTyped *variable);
 
diff --git a/tests/GLESUnitTests/unittests.cpp b/tests/GLESUnitTests/unittests.cpp
index 0f6298b..f01b93b 100644
--- a/tests/GLESUnitTests/unittests.cpp
+++ b/tests/GLESUnitTests/unittests.cpp
@@ -381,6 +381,58 @@
 		checkCompiles("", s);
 	}
 
+	void checkCompileFails(GLuint glShader, std::string source)
+	{
+		GLchar buf[1024];
+		GLint compileStatus = 0;
+		const char *c_source[1] = { source.c_str() };
+
+		glShaderSource(glShader, 1, c_source, nullptr);
+		glCompileShader(glShader);
+
+		EXPECT_GLENUM_EQ(GL_NONE, glGetError());
+
+		glGetShaderiv(glShader, GL_COMPILE_STATUS, &compileStatus);
+		glGetShaderInfoLog(glShader, sizeof(buf), nullptr, buf);
+
+		EXPECT_EQ(compileStatus, GL_FALSE) << "Compile status: " << std::endl << buf;
+
+		glDeleteShader(glShader);
+	}
+
+	void checkCompileFails(std::string s)
+	{
+		std::string vs =
+			"#version 300 es\n"
+			"in vec4 position;\n"
+			"out float unfoldable;\n"
+			"$INSERT\n"
+			"void main()\n"
+			"{\n"
+			"    unfoldable = position.x;\n"
+			"    gl_Position = vec4(position.xy, 0.0, 1.0);\n"
+			"    gl_Position.x += F(unfoldable);\n"
+			"}\n";
+
+		std::string fs =
+			"#version 300 es\n"
+			"precision mediump float;\n"
+			"in float unfoldable;\n"
+			"out vec4 fragColor;\n"
+			"$INSERT\n"
+			"void main()\n"
+			"{\n"
+			"    fragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
+			"    fragColor.x += F(unfoldable);\n"
+			"}\n";
+
+		vs = replace(vs, "$INSERT", s);
+		fs = replace(fs, "$INSERT", s);
+
+		checkCompileFails(glCreateShader(GL_VERTEX_SHADER), vs);
+		checkCompileFails(glCreateShader(GL_FRAGMENT_SHADER), fs);
+	}
+
 	EGLDisplay getDisplay() const { return display; }
 	EGLConfig getConfig() const { return config; }
 	EGLSurface getSurface() const { return surface; }
@@ -1767,6 +1819,23 @@
 	);
 }
 
+// Test that the compiler doesn't compile arrays larger than
+// GL_MAX_{VERTEX/FRAGMENT}_UNIFORM_VECTOR.
+TEST_F(SwiftShaderTest, CompilerLimits_ArraySize)
+{
+	Initialize(3, false);
+
+	checkCompileFails(
+		"uniform float u_var[100000000];\n"
+		"float F(float f) { return u_var[2]; }\n");
+	checkCompileFails(
+		"struct structType { mediump sampler2D m0; mediump samplerCube m1; }; \n"
+		"uniform structType u_var[100000000];\n"
+		"float F(float f) { return texture(u_var[2].m1, vec3(0.0)), vec4(0.26, 1.72, 0.60, 0.12).x; }\n");
+
+	Uninitialize();
+}
+
 #ifndef EGL_ANGLE_iosurface_client_buffer
 #define EGL_ANGLE_iosurface_client_buffer 1
 #define EGL_IOSURFACE_ANGLE 0x3454