WebGL: Validate shader entry point names do not contain invalid characters.

Section 6.20 of the WebGL spec states that all shader-related entry points
that accept strings must validate that the string does not contain
characters that are not valid in ESSL.

TEST=conformance/misc/invalid-passed-params
BUG=2016

Change-Id: I220d9dd79c4b0e8e0195277093268b9e1e66f9d9
Reviewed-on: https://chromium-review.googlesource.com/519445
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index 4a922f0..8ec453d 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -781,6 +781,31 @@
     }
 }
 
+// Return true if a character belongs to the ASCII subset as defined in GLSL ES 1.0 spec section
+// 3.1.
+bool IsValidESSLCharacter(unsigned char c)
+{
+    // Printing characters are valid except " $ ` @ \ ' DEL.
+    if (c >= 32 && c <= 126 && c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' &&
+        c != '\'')
+    {
+        return true;
+    }
+
+    // Horizontal tab, line feed, vertical tab, form feed, carriage return are also valid.
+    if (c >= 9 && c <= 13)
+    {
+        return true;
+    }
+
+    return false;
+}
+
+bool IsValidESSLString(const char *str, size_t len)
+{
+    return len == 0 || std::find_if_not(str, str + len, IsValidESSLCharacter) == str + len;
+}
+
 }  // anonymous namespace
 
 bool ValidateES2TexImageParameters(Context *context,
@@ -2658,6 +2683,14 @@
         return false;
     }
 
+    // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
+    // shader-related entry points
+    if (context->getExtensions().webglCompatibility && !IsValidESSLString(name, strlen(name)))
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Uniform name contains invalid characters"));
+        return false;
+    }
+
     if (strncmp(name, "gl_", 3) == 0)
     {
         context->handleError(
@@ -4002,6 +4035,14 @@
         return false;
     }
 
+    // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
+    // shader-related entry points
+    if (context->getExtensions().webglCompatibility && !IsValidESSLString(name, strlen(name)))
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Attribute name contains invalid characters"));
+        return false;
+    }
+
     return GetValidProgram(context, program) != nullptr;
 }
 
@@ -4732,6 +4773,14 @@
 
 bool ValidateGetAttribLocation(ValidationContext *context, GLuint program, const GLchar *name)
 {
+    // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
+    // shader-related entry points
+    if (context->getExtensions().webglCompatibility && !IsValidESSLString(name, strlen(name)))
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Attribute name contains invalid characters"));
+        return false;
+    }
+
     Program *programObject = GetValidProgram(context, program);
 
     if (!programObject)
@@ -4882,6 +4931,14 @@
         return false;
     }
 
+    // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
+    // shader-related entry points
+    if (context->getExtensions().webglCompatibility && !IsValidESSLString(name, strlen(name)))
+    {
+        context->handleError(Error(GL_INVALID_VALUE, "Uniform name contains invalid characters"));
+        return false;
+    }
+
     Program *programObject = GetValidProgram(context, program);
 
     if (!programObject)
@@ -5097,6 +5154,22 @@
         return false;
     }
 
+    // The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for
+    // shader-related entry points
+    if (context->getExtensions().webglCompatibility)
+    {
+        for (GLsizei i = 0; i < count; i++)
+        {
+            size_t len = length ? static_cast<size_t>(length[i]) : strlen(string[i]);
+            if (!IsValidESSLString(string[i], len))
+            {
+                context->handleError(
+                    Error(GL_INVALID_VALUE, "Shader source contains invalid characters"));
+                return false;
+            }
+        }
+    }
+
     Shader *shaderObject = GetValidShader(context, shader);
     if (!shaderObject)
     {
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
index d0e87d1..cf720ba 100644
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
@@ -837,6 +837,78 @@
     }
 }
 
+// Test that binding/querying uniforms and attributes with invalid names generates errors
+TEST_P(WebGLCompatibilityTest, InvalidAttributeAndUniformNames)
+{
+    const std::string validAttribName =
+        "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+    const std::string validUniformName =
+        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_1234567890";
+    const char invalidSet[] = {'"', '$', '`', '@', '\\', '\''};
+
+    std::string vert = "attribute float ";
+    vert += validAttribName;
+    vert +=
+        ";\n"
+        "void main()\n"
+        "{\n"
+        "    gl_Position = vec4(1.0);\n"
+        "}\n";
+
+    std::string frag =
+        "precision highp float;\n"
+        "uniform vec4 ";
+    frag += validUniformName;
+    frag +=
+        ";\n"
+        "void main()\n"
+        "{\n"
+        "    gl_FragColor = vec4(1.0);\n"
+        "}\n";
+
+    ANGLE_GL_PROGRAM(program, vert, frag);
+    EXPECT_GL_NO_ERROR();
+
+    for (char invalidChar : invalidSet)
+    {
+        std::string invalidName = validAttribName + invalidChar;
+        glGetAttribLocation(program, invalidName.c_str());
+        EXPECT_GL_ERROR(GL_INVALID_VALUE)
+            << "glGetAttribLocation unexpectedly succeeded for name \"" << invalidName << "\".";
+
+        glBindAttribLocation(program, 0, invalidName.c_str());
+        EXPECT_GL_ERROR(GL_INVALID_VALUE)
+            << "glBindAttribLocation unexpectedly succeeded for name \"" << invalidName << "\".";
+    }
+
+    for (char invalidChar : invalidSet)
+    {
+        std::string invalidName = validUniformName + invalidChar;
+        glGetUniformLocation(program, invalidName.c_str());
+        EXPECT_GL_ERROR(GL_INVALID_VALUE)
+            << "glGetUniformLocation unexpectedly succeeded for name \"" << invalidName << "\".";
+    }
+
+    for (char invalidChar : invalidSet)
+    {
+        std::string invalidAttribName = validAttribName + invalidChar;
+        const char *invalidVert[] = {
+            "attribute float ",
+            invalidAttribName.c_str(),
+            ";\n",
+            "void main()\n",
+            "{\n",
+            "    gl_Position = vec4(1.0);\n",
+            "}\n",
+        };
+
+        GLuint shader = glCreateShader(GL_VERTEX_SHADER);
+        glShaderSource(shader, static_cast<GLsizei>(ArraySize(invalidVert)), invalidVert, nullptr);
+        EXPECT_GL_ERROR(GL_INVALID_VALUE);
+        glDeleteShader(shader);
+    }
+}
+
 // Test the checks for OOB reads in the vertex buffers, instanced version
 TEST_P(WebGL2CompatibilityTest, DrawArraysBufferOutOfBoundsInstanced)
 {