Program::getUniformInternal: return only one array element
When getUniformInternal detected a mismatch between the glGetUniform
type and the uniform type, it entered a code path where it wrongly wrote
the whole array instead of a single element, causing a buffer overflow.
Adds a regression test.
BUG=595836
Change-Id: Id7372faece276d28363a30bf3183497d97357c76
Reviewed-on: https://chromium-review.googlesource.com/333771
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index bd16fd9..b4aa8dc 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -2508,7 +2508,7 @@
return;
}
- int components = VariableComponentCount(uniform.type) * uniform.elementCount();
+ int components = VariableComponentCount(uniform.type);
switch (componentType)
{
diff --git a/src/tests/gl_tests/UniformTest.cpp b/src/tests/gl_tests/UniformTest.cpp
index 9d1bd8c..a6d19d5 100644
--- a/src/tests/gl_tests/UniformTest.cpp
+++ b/src/tests/gl_tests/UniformTest.cpp
@@ -6,6 +6,7 @@
#include "test_utils/ANGLETest.h"
+#include <array>
#include <cmath>
using namespace angle;
@@ -500,6 +501,117 @@
glDeleteProgram(program);
}
+template <typename T>
+void CheckOneElement(void (*getUniformv) (GLuint, int, T *),
+ GLuint program,
+ const std::string& name,
+ int components,
+ T canary)
+{
+ // The buffer getting the results has three chunks
+ // - A chunk to see underflows
+ // - A chunk that will hold the result
+ // - A chunk to see overflows for when components = kChunkSize
+ static const size_t kChunkSize = 4;
+ std::array<T, 3 * kChunkSize> buffer;
+ buffer.fill(canary);
+
+ GLint location = glGetUniformLocation(program, name.c_str());
+ ASSERT_NE(location, -1);
+
+ getUniformv(program, location, &buffer[kChunkSize]);
+ for (size_t i = 0; i < kChunkSize; i++)
+ {
+ ASSERT_EQ(canary, buffer[i]);
+ }
+ for (size_t i = kChunkSize + components; i < buffer.size(); i++)
+ {
+ ASSERT_EQ(canary, buffer[i]);
+ }
+}
+
+// Check that getting an element array doesn't return the whole array.
+TEST_P(UniformTestES3, ReturnsOnlyOneArrayElement)
+{
+ static const size_t kArraySize = 4;
+ struct UniformArrayInfo
+ {
+ UniformArrayInfo(std::string type, std::string name, int components)
+ : type(type), name(name), components(components){}
+ std::string type;
+ std::string name;
+ int components;
+ };
+
+ // Check for various number of components and types
+ std::vector<UniformArrayInfo> uniformArrays;
+ uniformArrays.emplace_back("bool", "uBool", 1);
+ uniformArrays.emplace_back("vec2", "uFloat", 2);
+ uniformArrays.emplace_back("ivec3", "uInt", 3);
+ uniformArrays.emplace_back("uvec4", "uUint", 4);
+
+ std::ostringstream uniformStream;
+ std::ostringstream additionStream;
+ for (const auto &array : uniformArrays)
+ {
+ uniformStream << "uniform " << array.type << " " << array.name << "[" << std::to_string(kArraySize) << "];\n";
+
+ // We need to make use of the uniforms or they get compiled out.
+ for (int i = 0; i < 4; i++)
+ {
+ if (array.components == 1)
+ {
+ additionStream << " + float(" << array.name << "[" << i << "])";
+ }
+ else
+ {
+ for (int component = 0; component < array.components; component++)
+ {
+ additionStream << " + float(" << array.name << "[" << i << "][" << component
+ << "])";
+ }
+ }
+ }
+ }
+
+ const std::string &vertexShader =
+ "#version 300 es\n" +
+ uniformStream.str() +
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(1.0" + additionStream.str() + ");\n"
+ "}";
+
+ const std::string &fragmentShader =
+ "#version 300 es\n"
+ "precision mediump float;\n"
+ "out vec4 color;\n"
+ "void main ()\n"
+ "{\n"
+ " color = vec4(1, 0, 0, 1);\n"
+ "}";
+
+ GLuint program = CompileProgram(vertexShader, fragmentShader);
+ ASSERT_NE(0u, program);
+
+ for (const auto &uniformArray : uniformArrays)
+ {
+ for (size_t index = 0; index < kArraySize; index++)
+ {
+ std::string strIndex = "[" + std::to_string(index) + "]";
+ // Check all the different glGetUniformv functions
+ CheckOneElement<float>(glGetUniformfv, program, uniformArray.name + strIndex,
+ uniformArray.components, 42.4242f);
+ CheckOneElement<int>(glGetUniformiv, program, uniformArray.name + strIndex,
+ uniformArray.components, 0x7BADBED5);
+ CheckOneElement<unsigned int>(glGetUniformuiv, program, uniformArray.name + strIndex,
+ uniformArray.components, 0xDEADBEEF);
+ }
+ }
+
+ glDeleteProgram(program);
+}
+
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(UniformTest,
ES2_D3D9(),