Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds read in ANGLE
DescriptionOut of bounds read in ANGLE
ComponentANGLE
Sub-componentTranslator
Bug ClassOOB
Tracker553124799
Fix commitcac0efa53743 (angle/angle) +82/-20
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Changed Functions

FunctionChangeNotes
for
src/tests/gl_tests/GLSLTest.cpp
modified

Files Changed

  • src/compiler/translator/ParseContext.cpp
  • src/compiler/translator/tree_util/IntermNode_util.h
  • src/compiler/translator/tree_ops/ClampIndirectIndices.cpp
  • src/compiler/translator/tree_util/IntermNode_util.cpp
  • src/tests/angle_end2end_tests_expectations.txt
  • src/tests/gl_tests/GLSLTest.cpp
From cac0efa537430c13c9f9ce391cd07545c3c7e153 Mon Sep 17 00:00:00 2001
From: Shahbaz Youssefi <[email protected]>
Date: Wed, 02 Sep 2026 14:09:48 -0400
Subject: [PATCH] Translator: Fix gl_FragData index clamp vs comma

If gl_FragData is hidden inside a comma expression, it was undetected in
the ClampIndirectIndices AST transformation and its index was not
clamped.  Not an issue with IR.

Bug: chromium:553124799
Change-Id: I343fc31a2aaf8e93aed598b92778e79cc911316a
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/8342869
Reviewed-by: Yuxin Hu <[email protected]>
Commit-Queue: Shahbaz Youssefi <[email protected]>
---
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index e4acc6e..9da86d5 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -533,23 +533,6 @@
     components *= type.getArraySizeProduct();
     return components;
 }
-
-TIntermTyped *RemoveCommaLeftHandSize(TIntermTyped *node)
-{
-    TIntermTyped *current = node;
-    while (true)
-    {
-        TIntermBinary *asBinary = current->getAsBinaryNode();
-        if (asBinary == nullptr || asBinary->getOp() != EOpComma)
-        {
-            break;
-        }
-
-        current = asBinary->getRight();
-    }
-
-    return current;
-}
 }  // namespace
 
 // This tracks each binding point's current default offset for inheritance of subsequent
diff --git a/src/compiler/translator/tree_ops/ClampIndirectIndices.cpp b/src/compiler/translator/tree_ops/ClampIndirectIndices.cpp
index 3872a33..00f8851 100644
--- a/src/compiler/translator/tree_ops/ClampIndirectIndices.cpp
+++ b/src/compiler/translator/tree_ops/ClampIndirectIndices.cpp
@@ -88,14 +88,16 @@
 
         if (leftType.isArray())
         {
+            const bool isFragData =
+                RemoveCommaLeftHandSize(node->getLeft())->getType().getQualifier() == EvqFragData;
+
             int arraySize = static_cast<int>(leftType.getOutermostArraySize());
-            if (leftType.getQualifier() == EvqFragData &&
-                mExtDrawBuffers == ExtDrawBuffers::Disabled)
+            if (isFragData && mExtDrawBuffers == ExtDrawBuffers::Disabled)
             {
                 // When EXT_draw_buffers is disabled, only element 0 of gl_FragData may be accessed.
                 arraySize = 1;
             }
-            else if (leftType.getQualifier() == EvqFragData && mIsSecondaryFragDataUsed)
+            else if (isFragData && mIsSecondaryFragDataUsed)
             {
                 // When gl_SecondaryFragDataEXT is used, only indices up to MaxDualSourceDrawBuffers
                 // of gl_FragData may be accessed.
diff --git a/src/compiler/translator/tree_util/IntermNode_util.cpp b/src/compiler/translator/tree_util/IntermNode_util.cpp
index e8ea3c4..2d8fd35 100644
--- a/src/compiler/translator/tree_util/IntermNode_util.cpp
+++ b/src/compiler/translator/tree_util/IntermNode_util.cpp
@@ -571,6 +571,23 @@
     return TIntermAggregate::CreateConstructor(castDestType, {scalar});
 }
 
+TIntermTyped *RemoveCommaLeftHandSize(TIntermTyped *node)
+{
+    TIntermTyped *current = node;
+    while (true)
+    {
+        TIntermBinary *asBinary = current->getAsBinaryNode();
+        if (asBinary == nullptr || asBinary->getOp() != EOpComma)
+        {
+            break;
+        }
+
+        current = asBinary->getRight();
+    }
+
+    return current;
+}
+
 void MoveDeclarationsBeforeFunctions(TIntermBlock *root)
 {
     TIntermSequence *original = root->getSequence();
diff --git a/src/compiler/translator/tree_util/IntermNode_util.h b/src/compiler/translator/tree_util/IntermNode_util.h
index 88d9bcc..b57787c 100644
--- a/src/compiler/translator/tree_util/IntermNode_util.h
+++ b/src/compiler/translator/tree_util/IntermNode_util.h
@@ -172,6 +172,9 @@
 // Cast a scalar to the basic type of type. No-ops if scalar is already the right type.
 TIntermNode *CastScalar(const TType &type, TIntermTyped *scalar);
 
+// Given |(a, b, c, d)|, returns |d|.
+TIntermTyped *RemoveCommaLeftHandSize(TIntermTyped *node);
+
 // Called on root, moves declarations before function definitions.  Done so that transformations
 // that need to do something with globals and append code for them in main() don't have to deal with
 // declarations after main.
diff --git a/src/tests/angle_end2end_tests_expectations.txt b/src/tests/angle_end2end_tests_expectations.txt
index d24b4cf..1d8f85c 100644
--- a/src/tests/angle_end2end_tests_expectations.txt
+++ b/src/tests/angle_end2end_tests_expectations.txt
@@ -525,6 +525,7 @@
 534815900 MAC METAL : TextureCubeTestES3.RedefinedCubemapLevelsOnlyFaceZeroCompatible/* = SKIP
 536936861 MAC METAL : ClipDistance*.ThreeClipDistancesRedeclaredAndPassedToFunction/* = SKIP
 537814733 MAC METAL : WebGLGLSLTest.FragDataIndexClampWithoutDrawBuffers/* = SKIP
+537814733 MAC METAL : WebGLGLSLTest.FragDataInCommaIndexClampWithoutDrawBuffers/* = SKIP
 
 // The workaround is not intended to be enabled in this configuration so
 // skip it as the failure is likely a driver bug.
@@ -1328,6 +1329,7 @@
 535213600 PIXEL10 GLES : PbufferTestES3.BindTexImageAndCopyTextureInvertedSrc/* = SKIP
 535213600 PIXEL10 GLES : PbufferTestES3.BindTexImageAndCopyTextureSrc/* = SKIP
 537235694 PIXEL10 GLES : WebGLGLSLTest.FragDataIndexClampWithoutDrawBuffers/* = SKIP
+537235694 PIXEL10 GLES : WebGLGLSLTest.FragDataInCommaIndexClampWithoutDrawBuffers/* = SKIP
 546627192 PIXEL10 GLES : GLSLTest_ES3.VaryingAnonymousStructAssignment/* = SKIP
 546627192 PIXEL10 GLES : GLSLTest_ES3.VaryingAnonymousStructTernary/* = SKIP
 
@@ -2464,6 +2466,7 @@
 42267100 WGPU : GLSLTest.FragData/* = SKIP
 42267100 WGPU : GLSLTest.FragData_AlphaToCoverage/* = SKIP
 42267100 WGPU : WebGLGLSLTest.FragDataIndexClampWithoutDrawBuffers/* = SKIP
+42267100 WGPU : WebGLGLSLTest.FragDataInCommaIndexClampWithoutDrawBuffers/* = SKIP
 42267100 WGPU : GLSLTest.SeparateStructDeclaratorStructInStruct/* = SKIP
 42267100 WGPU : GLSLTest.ConstructorinSequenceOperator/* = SKIP
 42267100 WGPU : GLSLTest.VectorAndMatrixScalarizationDoesNotAffectRendering/* = SKIP
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
index fbf7ec5..09a6d2e 100644
--- a/src/tests/gl_tests/GLSLTest.cpp
+++ b/src/tests/gl_tests/GLSLTest.cpp
@@ -24861,6 +24861,33 @@
 }
 
 // Test that indirect indices to gl_FragData get clamped to the right bounds when
+// GL_EXT_draw_buffers is not enabled, and gl_FragData is the right hand side of a comma expression.
+//
+// The same test for ES3 is not needed, because unlike gl_FragData in ESSL 100, it's not allowed to
+// index a fragment output variable with a non-constant index in ESSL 300+.
+TEST_P(WebGLGLSLTest, FragDataInCommaIndexClampWithoutDrawBuffers)
+{
+    constexpr char kFS[] = R"(precision mediump float;
+void main() {
+    // GL_EXT_draw_buffers is not enabled, which means only one output is valid.  Make sure all the
+    // following writes in the loop end up writing to gl_FragData[0].
+    gl_FragData[0] = vec4(0.1, 0.05, 0, 1);
+    int v = 0;
+    vec4 res = vec4(0);
+    for (int i = 0; i < 8; i++) {
+        res += (v += 1, gl_FragData)[i];
+    }
+    gl_FragData[0] = res;
+    gl_FragData[0].z = float(v == 8);
+})";
+
+    ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS);
+    drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+    EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(204, 102, 255, 255), 1);
+    ASSERT_GL_NO_ERROR();
+}
+
+// Test that indirect indices to gl_FragData get clamped to the right bounds when
 // gl_SecondaryFragDataEXT is used.
 //
 // The same test for ES3 is not needed, because unlike gl_FragData in ESSL 100, it's not allowed to
@@ -25027,6 +25054,33 @@
     ASSERT_GL_NO_ERROR();
 }
 
+// Test that indirect indices to gl_FragData get clamped to the right bounds when
+// gl_SecondaryFragDataEXT is used, and gl_FragData is the right hand side of a comma expression.
+TEST_P(WebGLGLSLTest, FragDataInCommaIndexClampWithSecondaryFragData)
+{
+    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_draw_buffers"));
+    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
+
+    constexpr char kFS[] = R"(#extension GL_EXT_draw_buffers : require
+#extension GL_EXT_blend_func_extended : require
+void main() {
+    gl_FragData[0] = vec4(0.1, 0.05, 0, 1);
+    int v = 0;
+    mediump vec4 res = vec4(0);
+    for (int i = 0; i < 8; i++) {
+        res += (v += 1, gl_FragData)[i];
+    }
+    gl_FragData[0] = res;
+    gl_FragData[0].z = float(v == 8);
+    gl_SecondaryFragDataEXT[0] = vec4(1.0, 0.0, 0.0, 1.0);
+})";
+
+    ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS);
+    drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+    EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(204, 102, 255, 255), 1);
+    ASSERT_GL_NO_ERROR();
+}
+
 // Test that long symbols work
 TEST_P(GLSLTest_ES3, LongIdentifiers)
 {
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/tests/angle_end2end_tests_expectations.txt b/src/tests/angle_end2end_tests_expectations.txt
index d24b4cf..1d8f85c 100644
--- a/src/tests/angle_end2end_tests_expectations.txt
+++ b/src/tests/angle_end2end_tests_expectations.txt
@@ -525,6 +525,7 @@
 534815900 MAC METAL : TextureCubeTestES3.RedefinedCubemapLevelsOnlyFaceZeroCompatible/* = SKIP
 536936861 MAC METAL : ClipDistance*.ThreeClipDistancesRedeclaredAndPassedToFunction/* = SKIP
 537814733 MAC METAL : WebGLGLSLTest.FragDataIndexClampWithoutDrawBuffers/* = SKIP
+537814733 MAC METAL : WebGLGLSLTest.FragDataInCommaIndexClampWithoutDrawBuffers/* = SKIP
 
 // The workaround is not intended to be enabled in this configuration so
 // skip it as the failure is likely a driver bug.
@@ -1328,6 +1329,7 @@
 535213600 PIXEL10 GLES : PbufferTestES3.BindTexImageAndCopyTextureInvertedSrc/* = SKIP
 535213600 PIXEL10 GLES : PbufferTestES3.BindTexImageAndCopyTextureSrc/* = SKIP
 537235694 PIXEL10 GLES : WebGLGLSLTest.FragDataIndexClampWithoutDrawBuffers/* = SKIP
+537235694 PIXEL10 GLES : WebGLGLSLTest.FragDataInCommaIndexClampWithoutDrawBuffers/* = SKIP
 546627192 PIXEL10 GLES : GLSLTest_ES3.VaryingAnonymousStructAssignment/* = SKIP
 546627192 PIXEL10 GLES : GLSLTest_ES3.VaryingAnonymousStructTernary/* = SKIP
 
@@ -2464,6 +2466,7 @@
 42267100 WGPU : GLSLTest.FragData/* = SKIP
 42267100 WGPU : GLSLTest.FragData_AlphaToCoverage/* = SKIP
 42267100 WGPU : WebGLGLSLTest.FragDataIndexClampWithoutDrawBuffers/* = SKIP
+42267100 WGPU : WebGLGLSLTest.FragDataInCommaIndexClampWithoutDrawBuffers/* = SKIP
 42267100 WGPU : GLSLTest.SeparateStructDeclaratorStructInStruct/* = SKIP
 42267100 WGPU : GLSLTest.ConstructorinSequenceOperator/* = SKIP
 42267100 WGPU : GLSLTest.VectorAndMatrixScalarizationDoesNotAffectRendering/* = SKIP
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
index fbf7ec5..09a6d2e 100644
--- a/src/tests/gl_tests/GLSLTest.cpp
+++ b/src/tests/gl_tests/GLSLTest.cpp
@@ -24861,6 +24861,33 @@
 }
 
 // Test that indirect indices to gl_FragData get clamped to the right bounds when
+// GL_EXT_draw_buffers is not enabled, and gl_FragData is the right hand side of a comma expression.
+//
+// The same test for ES3 is not needed, because unlike gl_FragData in ESSL 100, it's not allowed to
+// index a fragment output variable with a non-constant index in ESSL 300+.
+TEST_P(WebGLGLSLTest, FragDataInCommaIndexClampWithoutDrawBuffers)
+{
+    constexpr char kFS[] = R"(precision mediump float;
+void main() {
+    // GL_EXT_draw_buffers is not enabled, which means only one output is valid.  Make sure all the
+    // following writes in the loop end up writing to gl_FragData[0].
+    gl_FragData[0] = vec4(0.1, 0.05, 0, 1);
+    int v = 0;
+    vec4 res = vec4(0);
+    for (int i = 0; i < 8; i++) {
+        res += (v += 1, gl_FragData)[i];
+    }
+    gl_FragData[0] = res;
+    gl_FragData[0].z = float(v == 8);
+})";
+
+    ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS);
+    drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+    EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(204, 102, 255, 255), 1);
+    ASSERT_GL_NO_ERROR();
+}
+
+// Test that indirect indices to gl_FragData get clamped to the right bounds when
 // gl_SecondaryFragDataEXT is used.
 //
 // The same test for ES3 is not needed, because unlike gl_FragData in ESSL 100, it's not allowed to
@@ -25027,6 +25054,33 @@
     ASSERT_GL_NO_ERROR();
 }
 
+// Test that indirect indices to gl_FragData get clamped to the right bounds when
+// gl_SecondaryFragDataEXT is used, and gl_FragData is the right hand side of a comma expression.
+TEST_P(WebGLGLSLTest, FragDataInCommaIndexClampWithSecondaryFragData)
+{
+    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_draw_buffers"));
+    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
+
+    constexpr char kFS[] = R"(#extension GL_EXT_draw_buffers : require
+#extension GL_EXT_blend_func_extended : require
+void main() {
+    gl_FragData[0] = vec4(0.1, 0.05, 0, 1);
+    int v = 0;
+    mediump vec4 res = vec4(0);
+    for (int i = 0; i < 8; i++) {
+        res += (v += 1, gl_FragData)[i];
+    }
+    gl_FragData[0] = res;
+    gl_FragData[0].z = float(v == 8);
+    gl_SecondaryFragDataEXT[0] = vec4(1.0, 0.0, 0.0, 1.0);
+})";
+
+    ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS);
+    drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+    EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(204, 102, 255, 255), 1);
+    ASSERT_GL_NO_ERROR();
+}
+
 // Test that long symbols work
 TEST_P(GLSLTest_ES3, LongIdentifiers)
 {
Loading diff…

Original Bug Report

reported by [email protected]

GPU OOB read via comma-wrapped gl_FragData bypassing ClampIndirectIndices in ANGLE

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: In WebGL1 (ESSL 100), wrapping gl_FragData in a comma expression sets the resulting AST binary node’s qualifier to EvqTemporary, bypassing the EvqFragData qualifier checks in ClampIndirectIndices. When dual-source blending (EXT_blend_func_extended) is enabled with gl_SecondaryFragDataEXT, TranslatorGLSL shrinks the emitted webgl_FragData output declaration to 1 element, but dynamic indirect indices are clamped up to MaxDrawBuffers - 1 (typically 7). Consequently, the emitted desktop GLSL shader performs dynamic out-of-bounds reads into the fragment output storage at GPU runtime.

Affected files:

  • third_party/angle/src/compiler/translator/tree_ops/ClampIndirectIndices.cpp
  • third_party/angle/src/compiler/translator/glsl/TranslatorGLSL.cpp
  • third_party/angle/src/compiler/translator/ParseContext.cpp

Estimated timestamp from git blame: 2026-06-23

Summary

A potential out-of-bounds read vulnerability exists in ANGLE’s GLSL translator when handling sequence (comma) expressions wrapping gl_FragData in WebGL1 shaders that use dual-source blending (EXT_blend_func_extended). When gl_FragData is indexed indirectly through a comma expression, ClampIndirectIndices fails to recognize the base qualifier as EvqFragData, leading to an emitted clamp ceiling of 7 against a declared array size of 1.

Note: These findings are based on static source-code analysis; our tooling agent does not have runtime execution capability, so the steps and outcomes described below represent potential reproduction and impact behaviors.


Affected Files

  • third_party/angle/src/compiler/translator/tree_ops/ClampIndirectIndices.cpp
  • third_party/angle/src/compiler/translator/ParseContext.cpp
  • third_party/angle/src/compiler/translator/glsl/TranslatorGLSL.cpp

Technical Details & Root Cause

  1. Comma Qualifier Assignment: Under WebGL1 (SH_WEBGL_SPEC), comma operations on arrays are permitted by TParseContext::addComma (third_party/angle/src/compiler/translator/ParseContext.cpp:9552-9562). TIntermBinary::CreateComma (IntermNode.cpp:1614-1621) sets the binary node’s type to the right operand (vec4[8]) but assigns its qualifier via GetCommaQualifier() (IntermNode.cpp:1846-1857). When the left operand has side effects (e.g., t = 1.0), GetCommaQualifier returns EvqTemporary, and constant folding is skipped (IntermNode.cpp:2220-2227).

  2. Bypassing Parse-time Constant Checks: When indexing dynamically (e.g., via a loop index i), indexExpression->getAsConstantUnion() is nullptr (ParseContext.cpp:7334). Thus, the block updating mMaxFragDataArrayIndexUsed (ParseContext.cpp:7486) is skipped, leaving it at 0. The post-parse check if (secondaryFragDataUsed && mMaxFragDataArrayIndexUsed >= mResources.MaxDualSourceDrawBuffers) (ParseContext.cpp:10797) passes because 0 >= 1 is false.

  3. Defect in ClampIndirectIndices: ClampIndirectIndicesTraverser::visitBinary (ClampIndirectIndices.cpp:89-105) clamps indirect array indices based on leftType.getQualifier():

    if (leftType.isArray())
    {
        int arraySize = static_cast<int>(leftType.getOutermostArraySize());
        if (leftType.getQualifier() == EvqFragData &&
            mExtDrawBuffers == ExtDrawBuffers::Disabled)
        {
            arraySize = 1;
        }
        else if (leftType.getQualifier() == EvqFragData && mIsSecondaryFragDataUsed)
        {
            arraySize =
                std::min(arraySize, mCompiler->getBuiltInResources().MaxDualSourceDrawBuffers);
        }
        max = createClampValue(arraySize - 1, useFloatClamp);
    }
    

    Because leftType is retrieved directly from node->getLeft() without descending through comma expressions, leftType.getQualifier() is EvqTemporary rather than EvqFragData. The pass fails to shrink arraySize to MaxDualSourceDrawBuffers (1), resulting in max = 7.0 (MaxDrawBuffers - 1).

  4. Mismatched GLSL Emission: TranslatorGLSL::writeOutputVariables (TranslatorGLSL.cpp:221-227) sees both gl_FragData and gl_SecondaryFragDataEXT in mOutputVariables and emits:

    out vec4 webgl_FragData[1];
    out vec4 webgl_SecondaryFragData[1];
    

    The body emits the index expression verbatim (OutputGLSLBase.cpp:644-646, 694-697):

    acc += (t = 1.0, webgl_FragData)[int(clamp(float(i), 0.0, 7.0))];
    

    When executed, indices 1..7 dynamically read past the 1-element allocation of webgl_FragData on the GPU.


Potential Trigger Steps

  1. Run Chrome on desktop Linux or with --use-angle=gl using the passthrough command decoder on a system with OpenGL >= 3.3.
  2. In an unprivileged web page, create a WebGL1 context: const gl = canvas.getContext('webgl');.
  3. Request both extensions: gl.getExtension('WEBGL_draw_buffers'); and gl.getExtension('WEBGL_blend_func_extended');.
  4. Compile a fragment shader containing a comma-wrapped dynamic read:
    #extension GL_EXT_draw_buffers : require
    #extension GL_EXT_blend_func_extended : require
    precision mediump float;
    float t;
    void main() {
        vec4 acc = vec4(0.0);
        for (int i = 0; i < 8; ++i) {
            acc += (t = 1.0, gl_FragData)[i];
        }
        gl_FragData[0] = acc + vec4(t);
        gl_SecondaryFragDataEXT[0] = vec4(0.25);
    }
    
  5. Link the program and issue a draw call, then read back the accumulated result using gl.readPixels(...).

Impact

This flaw allows a WebGL shader to bypass ANGLE’s mandatory array access clamping mechanism, resulting in dynamic out-of-bounds reads into fragment output storage during shader execution on desktop OpenGL drivers. The read data (which may include uninitialized GPU registers or stale cross-origin GPU memory) is accumulated into the fragment output and observable by web content via gl.readPixels().


Suggested Fix

In third_party/angle/src/compiler/translator/tree_ops/ClampIndirectIndices.cpp, unwrap sequence (EOpComma) expressions when evaluating leftType and its qualifier for indirect indexing nodes, ensuring the check descends to the effectively indexed expression (similar to RemoveCommaLeftHandSize in ParseContext.cpp):

const TIntermTyped *effectivelyIndexed = node->getLeft();
while (effectivelyIndexed->getAsBinaryNode() &&
       effectivelyIndexed->getAsBinaryNode()->getOp() == EOpComma)
{
    effectivelyIndexed = effectivelyIndexed->getAsBinaryNode()->getRight();
}
const TType &leftType = effectivelyIndexed->getType();

Evaluated with Chrome root at commit: c6718ebbf2c677c6f63691e28c7801c563f6b19c


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker