High chrome OOB 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactBuffer overflow in WebGL
DescriptionBuffer overflow in WebGL
ComponentWebGL
Bug ClassOOB
Tracker529991907
Fix commit318948133839 (angle/angle) +137/-3
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Files Changed

  • include/GLSLANG/ShaderLang.h
  • include/platform/autogen/FeaturesGL_autogen.h
  • include/platform/gl_features.json
  • src/compiler/translator/ParseContext.cpp
From 318948133839dcf445f6b39854538f776e3302b3 Mon Sep 17 00:00:00 2001
From: Ken Russell <[email protected]>
Date: Fri, 17 Jul 2026 15:24:36 -0700
Subject: [PATCH] Reland "GL: Limit declared vertex output varying components"

This reverts commit 3cf1a6c74785246ed0c29f58816632e05d7d6616.

Add a workaround which, when enabled, tracks the number of output
components from the vertex shader and fails the compile if there's an
attempt to exceed a specific limit (1024).

Relative to the original commit, apply a much higher compile time
limit which is unlikely to be reached by any test or real-world
application, and apply it to all context types, not just WebGL's.

Apply this workaround on Imagination's PowerVR OpenGL ES drivers to
work around a bug in the driver's shader compiler.

Co-authored with Gemini.

Test: angle_end2end_tests \
  --gtest_filter=GLSLValidationTest_ES3_LimitOutputVaryings.\
  TooManyDeclaredVertexOutputComponents
TAG=agy
CONV=8ed05bff-eb25-4de9-92b8-d097e4c0abae

Bug: chromium:529991907
Change-Id: I79d0bd117d6e0d6412011d361183484f088243e6
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/8114814
Commit-Queue: Kenneth Russell <[email protected]>
Reviewed-by: Shahbaz Youssefi <[email protected]>
---

diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h
index a725c38..dde82d2 100644
--- a/include/GLSLANG/ShaderLang.h
+++ b/include/GLSLANG/ShaderLang.h
@@ -26,7 +26,7 @@
 
 // Version number for shader translation API.
 // It is incremented every time the API changes.
-#define ANGLE_SH_VERSION 417
+#define ANGLE_SH_VERSION 418
 
 enum ShShaderSpec
 {
@@ -179,8 +179,8 @@
     // If requested, validates the AST after every transformation.  Useful for debugging.
     uint64_t validateAST : 1;
 
-    // placeholder bit for removed validateLoopIndexing option.
-    uint64_t unused3 : 1;
+    // Limit the number of output varyings allowed in vertex shaders to work around driver bugs.
+    uint64_t limitOutputVaryingsTo256 : 1;
 
     // Emits #line directives in HLSL.
     uint64_t lineDirectives : 1;
diff --git a/include/platform/autogen/FeaturesGL_autogen.h b/include/platform/autogen/FeaturesGL_autogen.h
index 9f60d6b..2b4a23e 100644
--- a/include/platform/autogen/FeaturesGL_autogen.h
+++ b/include/platform/autogen/FeaturesGL_autogen.h
@@ -722,6 +722,12 @@
         &members,
     };
 
+    FeatureInfo limitOutputVaryingsTo256AtCompileTime = {
+        "limitOutputVaryingsTo256AtCompileTime",
+        FeatureCategory::OpenGLWorkarounds,
+        &members,
+    };
+
 };
 
 inline FeaturesGL::FeaturesGL()  = default;
diff --git a/include/platform/gl_features.json b/include/platform/gl_features.json
index 127cccf..789e621 100644
--- a/include/platform/gl_features.json
+++ b/include/platform/gl_features.json
@@ -942,6 +942,14 @@
                 "Split full-image level 0 PBO uploads via TexSubImage2D into two calls to work around driver bugs."
             ],
             "issue": "http://crbug.com/496807874"
+        },
+        {
+            "name": "limit_output_varyings_to_256_at_compile_time",
+            "category": "Workarounds",
+            "description": [
+                "Limit the number of declared varying components at compile time to work around a PowerVR driver bug."
+            ],
+            "issue": "http://crbug.com/529991907"
         }
     ]
 }
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index ddcf9bc..7745cbe 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -498,6 +498,31 @@
     structDeclaration->appendDeclarator(structDeclarator);
     return structDeclaration;
 }
+
+unsigned int GetTypeComponentCount(const TType &type)
+{
+    unsigned int components = 0;
+    if (type.getBasicType() == EbtInterfaceBlock)
+    {
+        for (const TField *field : type.getInterfaceBlock()->fields())
+        {
+            components += GetTypeComponentCount(*field->type());
+        }
+    }
+    else if (type.getStruct())
+    {
+        for (const TField *field : type.getStruct()->fields())
+        {
+            components += GetTypeComponentCount(*field->type());
+        }
+    }
+    else
+    {
+        components = static_cast<unsigned int>(type.getNominalSize()) * type.getSecondarySize();
+    }
+    components *= type.getArraySizeProduct();
+    return components;
+}
 }  // namespace
 
 // This tracks each binding point's current default offset for inheritance of subsequent
@@ -577,6 +602,7 @@
       mNumViews(-1),
       mMaxUniformBlocks(GetMaxUniformBlocksForShaderType(mShaderType, options, resources)),
       mNumUniformBlocks(0),
+      mNumOutputVaryingComponents(0),
       mDeclaringFunction(false),
       mDeclaringMain(false),
       mMainFunction(nullptr),
@@ -2343,6 +2369,7 @@
         error(line, "redefinition", identifier);
         return false;
     }
+    addAndCheckOutputVaryings(**variable, line);
 
     if (!checkIsNonVoid(line, identifier, type->getBasicType()))
     {
@@ -7035,6 +7062,7 @@
                 error(field->line(), "redefinition of an interface block member name",
                       field->name());
             }
+            addAndCheckOutputVaryings(*fieldVariable, field->line());
 
             // Don't declare variables for fields of nameless interface blocks in the IR, just
             // remember to implicitly index the instance variable when referenced.
@@ -7064,6 +7092,7 @@
         {
             error(instanceLine, "redefinition of an interface block instance name", instanceName);
         }
+        addAndCheckOutputVaryings(*instanceVariable, instanceLine);
     }
 
     TIntermSymbol *blockSymbol = new TIntermSymbol(instanceVariable);
@@ -10768,6 +10797,44 @@
     return numErrors() == 0;
 }
 
+void TParseContext::addAndCheckOutputVaryings(const TVariable &variable, const TSourceLoc &line)
+{
+    if (mShaderType != GL_VERTEX_SHADER)
+    {
+        return;
+    }
+
+    if (!mCompileOptions.limitOutputVaryingsTo256)
+    {
+        return;
+    }
+
+    if (variable.symbolType() == SymbolType::BuiltIn)
+    {
+        return;
+    }
+
+    if (!IsVaryingOut(variable.getType().getQualifier()))
+    {
+        return;
+    }
+
+    angle::CheckedNumeric<unsigned int> checkedNum = mNumOutputVaryingComponents;
+    checkedNum += GetTypeComponentCount(variable.getType());
+    mNumOutputVaryingComponents =
+        checkedNum.ValueOrDefault(std::numeric_limits<unsigned int>::max());
+
+    // The cap to 256 vec4s = 1024 components seems somewhat arbitrary, but this is intended as a
+    // workaround for a specific driver bug, and this limit being much
+    // higher than the device limits (mResources.MaxVertexOutputVectors *
+    // 4), it avoids regressing both tests and applications.
+    if (mNumOutputVaryingComponents > 1024)
+    {
+        error(line, "Too many declared shader output varying components for this device",
+              variable.name());
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/tests/gl_tests/GLSLValidationTest.cpp b/src/tests/gl_tests/GLSLValidationTest.cpp
index 014ab69..28a44aa 100644
--- a/src/tests/gl_tests/GLSLValidationTest.cpp
+++ b/src/tests/gl_tests/GLSLValidationTest.cpp
@@ -9469,6 +9469,39 @@
     validateError(GL_FRAGMENT_SHADER, kFS, "version");
 }
 
+class GLSLValidationTest_ES3_LimitOutputVaryings : public GLSLValidationTest_ES3
+{};
+
+// Regression test for crbug.com/529991907.
+// Verify that compiling a shader with up to 1024 output varying components
+// succeeds, and exceeding 1024 components is rejected at compile time.
+TEST_P(GLSLValidationTest_ES3_LimitOutputVaryings, TooManyDeclaredVertexOutputComponents)
+{
+    ANGLE_SKIP_TEST_IF(
+        !getEGLWindow()->isFeatureEnabled(Feature::LimitOutputVaryingsTo256AtCompileTime));
+
+    constexpr int kMaxVectors = 1024 / 4;
+
+    std::stringstream vsValid;
+    vsValid << "#version 300 es\n";
+    for (int i = 0; i < kMaxVectors; ++i)
+    {
+        vsValid << "out highp vec4 v" << i << ";\n";
+    }
+    vsValid << "void main() { gl_Position = vec4(0.0); }\n";
+    validateSuccess(GL_VERTEX_SHADER, vsValid.str().c_str());
+
+    std::stringstream vsInvalid;
+    vsInvalid << "#version 300 es\n";
+    for (int i = 0; i < kMaxVectors + 1; ++i)
+    {
+        vsInvalid << "out highp vec4 v" << i << ";\n";
+    }
+    vsInvalid << "void main() { gl_Position = vec4(0.0); }\n";
+    validateError(GL_VERTEX_SHADER, vsInvalid.str().c_str(),
+                  "Too many declared shader output varying components for this device");
+}
+
 }  // namespace
 
 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(GLSLValidationTest);
@@ -9491,6 +9524,11 @@
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WebGL2GLSLValidationTest);
 ANGLE_INSTANTIATE_TEST_ES3(WebGL2GLSLValidationTest);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLValidationTest_ES3_LimitOutputVaryings);
+ANGLE_INSTANTIATE_TEST(GLSLValidationTest_ES3_LimitOutputVaryings,
+                       ES3_OPENGL().enable(Feature::LimitOutputVaryingsTo256AtCompileTime),
+                       ES3_OPENGLES().enable(Feature::LimitOutputVaryingsTo256AtCompileTime));
+
 ANGLE_INSTANTIATE_TEST_ES2_AND(WebGLGLSLValidationExtensionDisableTest,
                                ES2_OPENGL().enable(Feature::AllowExtensionDisableAfterNonPpTokens));
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.