CVE-2026-79048
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ClampIndirectIndicesTraversersrc/compiler/translator/tree_ops/ClampIndirectIndices.cpp |
modified |
Files Changed
src/compiler/translator/Compiler.cppsrc/compiler/translator/tree_ops/ClampIndirectIndices.cppsrc/compiler/translator/tree_ops/ClampIndirectIndices.hsrc/tests/angle_end2end_tests_expectations.txtsrc/tests/gl_tests/GLSLTest.cpp
Patch
From 8521722b7ebc715adb1cec75c722fa51942c73d2 Mon Sep 17 00:00:00 2001 From: Shahbaz Youssefi <[email protected]> Date: Mon, 20 Jul 2026 16:23:13 -0400 Subject: [PATCH] Translator: Fix gl_FragData clamp without EXT_draw_buffers For AST, this change clamps the indices to [0,0] if EXT_draw_buffers is not enabled. For IR, this was already correct because gl_FragData is sized early to one element during parse. Bug: chromium:536531630 Change-Id: I02d3ba21ab0f5fef1119967538f6c66fbeedd7d8 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/8121717 Reviewed-by: Geoff Lang <[email protected]> Commit-Queue: Shahbaz Youssefi <[email protected]> --- diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp index fadb64c..450d2a8 100644 --- a/src/compiler/translator/Compiler.cpp +++ b/src/compiler/translator/Compiler.cpp @@ -1047,7 +1047,7 @@ if (compileOptions.clampIndirectArrayBounds) { - if (!ClampIndirectIndices(this, root, &mSymbolTable)) + if (!ClampIndirectIndices(this, root, &mSymbolTable, mExtensionBehavior)) { return false; } diff --git a/src/compiler/translator/tree_ops/ClampIndirectIndices.cpp b/src/compiler/translator/tree_ops/ClampIndirectIndices.cpp index 2bcc698..3872a33 100644 --- a/src/compiler/translator/tree_ops/ClampIndirectIndices.cpp +++ b/src/compiler/translator/tree_ops/ClampIndirectIndices.cpp @@ -19,13 +19,28 @@ { namespace { +enum class ExtDrawBuffers +{ + Disabled, + Enabled, +}; + +bool ClampIndirectIndicesImpl(TCompiler *compiler, + TIntermNode *root, + TSymbolTable *symbolTable, + ExtDrawBuffers extDrawBuffers); + // Traverser that finds EOpIndexIndirect nodes and applies a clamp to their right-hand side // expression. class ClampIndirectIndicesTraverser : public TIntermTraverser { public: - ClampIndirectIndicesTraverser(TCompiler *compiler, TSymbolTable *symbolTable) - : TIntermTraverser(true, false, false, symbolTable), mCompiler(compiler) + ClampIndirectIndicesTraverser(TCompiler *compiler, + TSymbolTable *symbolTable, + ExtDrawBuffers extDrawBuffers) + : TIntermTraverser(true, false, false, symbolTable), + mCompiler(compiler), + mExtDrawBuffers(extDrawBuffers) { mIsSecondaryFragDataUsed = symbolTable->isSecondaryFragDataUsed(); } @@ -41,9 +56,11 @@ } // Apply the transformation to the left and right nodes - bool valid = ClampIndirectIndices(mCompiler, node->getLeft(), mSymbolTable); + bool valid = + ClampIndirectIndicesImpl(mCompiler, node->getLeft(), mSymbolTable, mExtDrawBuffers); ASSERT(valid); - valid = ClampIndirectIndices(mCompiler, node->getRight(), mSymbolTable); + valid = + ClampIndirectIndicesImpl(mCompiler, node->getRight(), mSymbolTable, mExtDrawBuffers); ASSERT(valid); // Generate clamp(right, 0, N), where N is the size of the array being indexed minus 1. If @@ -72,7 +89,13 @@ if (leftType.isArray()) { int arraySize = static_cast<int>(leftType.getOutermostArraySize()); - if (leftType.getQualifier() == EvqFragData && mIsSecondaryFragDataUsed) + if (leftType.getQualifier() == EvqFragData && + 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) { // When gl_SecondaryFragDataEXT is used, only indices up to MaxDualSourceDrawBuffers // of gl_FragData may be accessed. @@ -133,15 +156,32 @@ } TCompiler *mCompiler; + const ExtDrawBuffers mExtDrawBuffers; bool mIsSecondaryFragDataUsed = false; }; -} // anonymous namespace -bool ClampIndirectIndices(TCompiler *compiler, TIntermNode *root, TSymbolTable *symbolTable) +bool ClampIndirectIndicesImpl(TCompiler *compiler, + TIntermNode *root, + TSymbolTable *symbolTable, + ExtDrawBuffers extDrawBuffers) { - ClampIndirectIndicesTraverser traverser(compiler, symbolTable); + ClampIndirectIndicesTraverser traverser(compiler, symbolTable, extDrawBuffers); root->traverse(&traverser); return traverser.updateTree(compiler, root); } +} // anonymous namespace + +bool ClampIndirectIndices(TCompiler *compiler, + TIntermNode *root, + TSymbolTable *symbolTable, + const TExtensionBehavior &extensionBehavior) +{ + const ExtDrawBuffers extDrawBuffers = + IsExtensionEnabled(extensionBehavior, TExtension::EXT_draw_buffers) + ? ExtDrawBuffers::Enabled + : ExtDrawBuffers::Disabled; + return ClampIndirectIndicesImpl(compiler, root, symbolTable, extDrawBuffers); +} + } // namespace sh diff --git a/src/compiler/translator/tree_ops/ClampIndirectIndices.h b/src/compiler/translator/tree_ops/ClampIndirectIndices.h index eebbab0..64cb203 100644 --- a/src/compiler/translator/tree_ops/ClampIndirectIndices.h +++ b/src/compiler/translator/tree_ops/ClampIndirectIndices.h @@ -10,6 +10,7 @@ #define COMPILER_TRANSLATOR_TREEOPS_CLAMPINDIRECTINDICES_H_ #include "common/angleutils.h" +#include "compiler/translator/ExtensionBehavior.h" namespace sh { @@ -20,7 +21,8 @@ [[nodiscard]] bool ClampIndirectIndices(TCompiler *compiler, TIntermNode *root, - TSymbolTable *symbolTable); + TSymbolTable *symbolTable, + const TExtensionBehavior &extensionBehavior); } // namespace sh diff --git a/src/tests/angle_end2end_tests_expectations.txt b/src/tests/angle_end2end_tests_expectations.txt index e6d48b8..ceea19b 100644 --- a/src/tests/angle_end2end_tests_expectations.txt +++ b/src/tests/angle_end2end_tests_expectations.txt @@ -465,6 +465,7 @@ 524008572 MAC METAL : VertexAttributeTestES3.LargeAttribPointerOffsetNoCrash/* = SKIP 534815900 MAC METAL : TextureCubeTestES3.RedefinedCubemapLevelsOnlyFaceZeroCompatible/* = SKIP 536936861 MAC METAL : ClipDistance*.ThreeClipDistancesRedeclaredAndPassedToFunction/* = SKIP +537814733 MAC METAL : WebGLGLSLTest.FragDataIndexClampWithoutDrawBuffers/* = SKIP // The workaround is not intended to be enabled in this configuration so // skip it as the failure is likely a driver bug. @@ -1264,6 +1265,7 @@ 535213600 PIXEL10 GLES : PbufferTestES3.BindTexImageAndCopyTexSubTextureInvertedDst/* = SKIP 535213600 PIXEL10 GLES : PbufferTestES3.BindTexImageAndCopyTextureInvertedSrc/* = SKIP 535213600 PIXEL10 GLES : PbufferTestES3.BindTexImageAndCopyTextureSrc/* = SKIP +537235694 PIXEL10 GLES : WebGLGLSLTest.FragDataIndexClampWithoutDrawBuffers/* = SKIP 480069042 PIXEL10 VULKAN : ClearTextureEXTTestES31Unrenderable.Clear/ES3_1_Vulkan*__GL_RG16_SNORM_EXT_GL_RG_GL_SHORT_GL_TEXTURE_2D_ARRAY = SKIP 480069042 PIXEL10 VULKAN : ClearTextureEXTTestES31Unrenderable.Clear/ES3_1_Vulkan*__GL_RGB32UI_GL_RGB_INTEGER_GL_UNSIGNED_INT_GL_TEXTURE_2D_ARRAY = SKIP @@ -2395,6 +2397,7 @@ 42267100 WGPU : GLSLTest.FragCoordConsistency/* = SKIP 42267100 WGPU : GLSLTest.FragData/* = SKIP 42267100 WGPU : GLSLTest.FragData_AlphaToCoverage/* = SKIP +42267100 WGPU : WebGLGLSLTest.FragDataIndexClampWithoutDrawBuffers/* = 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 6cad6e2..e91b6a4 100644 --- a/src/tests/gl_tests/GLSLTest.cpp +++ b/src/tests/gl_tests/GLSLTest.cpp @@ -24588,6 +24588,29 @@ } // Test that indirect indices to gl_FragData get clamped to the right bounds when +// GL_EXT_draw_buffers is not enabled. +// +// 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, FragDataIndexClampWithoutDrawBuffers) +{ + 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(1, 0, 0, 1);
Regression Test / PoC
diff --git a/src/tests/angle_end2end_tests_expectations.txt b/src/tests/angle_end2end_tests_expectations.txt
index e6d48b8..ceea19b 100644
--- a/src/tests/angle_end2end_tests_expectations.txt
+++ b/src/tests/angle_end2end_tests_expectations.txt
@@ -465,6 +465,7 @@
524008572 MAC METAL : VertexAttributeTestES3.LargeAttribPointerOffsetNoCrash/* = SKIP
534815900 MAC METAL : TextureCubeTestES3.RedefinedCubemapLevelsOnlyFaceZeroCompatible/* = SKIP
536936861 MAC METAL : ClipDistance*.ThreeClipDistancesRedeclaredAndPassedToFunction/* = SKIP
+537814733 MAC METAL : WebGLGLSLTest.FragDataIndexClampWithoutDrawBuffers/* = SKIP
// The workaround is not intended to be enabled in this configuration so
// skip it as the failure is likely a driver bug.
@@ -1264,6 +1265,7 @@
535213600 PIXEL10 GLES : PbufferTestES3.BindTexImageAndCopyTexSubTextureInvertedDst/* = SKIP
535213600 PIXEL10 GLES : PbufferTestES3.BindTexImageAndCopyTextureInvertedSrc/* = SKIP
535213600 PIXEL10 GLES : PbufferTestES3.BindTexImageAndCopyTextureSrc/* = SKIP
+537235694 PIXEL10 GLES : WebGLGLSLTest.FragDataIndexClampWithoutDrawBuffers/* = SKIP
480069042 PIXEL10 VULKAN : ClearTextureEXTTestES31Unrenderable.Clear/ES3_1_Vulkan*__GL_RG16_SNORM_EXT_GL_RG_GL_SHORT_GL_TEXTURE_2D_ARRAY = SKIP
480069042 PIXEL10 VULKAN : ClearTextureEXTTestES31Unrenderable.Clear/ES3_1_Vulkan*__GL_RGB32UI_GL_RGB_INTEGER_GL_UNSIGNED_INT_GL_TEXTURE_2D_ARRAY = SKIP
@@ -2395,6 +2397,7 @@
42267100 WGPU : GLSLTest.FragCoordConsistency/* = SKIP
42267100 WGPU : GLSLTest.FragData/* = SKIP
42267100 WGPU : GLSLTest.FragData_AlphaToCoverage/* = SKIP
+42267100 WGPU : WebGLGLSLTest.FragDataIndexClampWithoutDrawBuffers/* = 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 6cad6e2..e91b6a4 100644
--- a/src/tests/gl_tests/GLSLTest.cpp
+++ b/src/tests/gl_tests/GLSLTest.cpp
@@ -24588,6 +24588,29 @@
}
// Test that indirect indices to gl_FragData get clamped to the right bounds when
+// GL_EXT_draw_buffers is not enabled.
+//
+// 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, FragDataIndexClampWithoutDrawBuffers)
+{
+ 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(1, 0, 0, 1);
+ for (int i = 0; i < 8; i++) {
+ gl_FragData[i] += vec4(-0.1, 0.05, 0.0, 0.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(51, 102, 0, 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
Original Bug Report
Potential OOB HLSL Array Write via gl_FragData Dynamic Index Mismatch in ANGLE D3D11
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: ANGLE’s HLSL translator generates out-of-bounds array accesses in emitted HLSL when a WebGL 1 shader dynamically indexes gl_FragData without enabling GL_EXT_draw_buffers. The translator declares a size-1 array but clamps dynamic indices to [0, 7], allowing out-of-bounds index writes to be passed to D3DCompile. This exposes the sandboxed GPU process to potential memory corruption within the shader compiler.
Affected files:
third_party/angle/src/compiler/translator/hlsl/OutputHLSL.cpp
Estimated timestamp from git blame: 2013-06-20
1. Summary of the Issue (Meant for Human Triage)
An issue in ANGLE’s D3D11 HLSL translator allows untrusted shaders (e.g., from WebGL 1 content) to cause out-of-bounds array writes in generated HLSL code, which is then passed directly to the D3DCompile API in the sandboxed GPU process.
When an ESSL 1.00 shader dynamically indexes gl_FragData without enabling the GL_EXT_draw_buffers extension, the OutputHLSL generator declares the backing static array gl_Color with a size of 1. However, the AST representation of gl_FragData maintains an array size equal to the backend’s MaxDrawBuffers (which is 8 on D3D11). Consequently, the ClampIndirectIndices pass clamps the indirect access index to the range [0, 7] instead of [0, 0].
When this HLSL is emitted, it results in code like gl_Color[clamped_index] = ... where clamped_index can be up to 7 while gl_Color has a size of only 1. This malformed HLSL is sent to D3DCompile, exposing the underlying text-based shader compiler to memory corruption within the sandboxed GPU process on Windows (High Severity / S1).
2. Proof-of-Concept & Detailed Execution Flow
Potential Trigger Path (WebGL 1): An attacker can trigger this vulnerability by serving a web page that creates a WebGL 1 context and compiles a specific fragment shader:
const gl = canvas.getContext('webgl');
const fs = gl.createShader(gl.FRAGMENT_SHADER);
// The shader uses a dynamic loop index to access gl_FragData without enabling GL_EXT_draw_buffers
gl.shaderSource(fs,
'precision mediump float;\n' +
'uniform sampler2D s; varying vec2 v;\n' +
'void main(){\n' +
' for(int i=0;i<8;i++)\n' +
' gl_FragData[i] = texture2D(s, v);\n' +
'}');
gl.compileShader(fs);
(Note: These are potential steps to trigger the vulnerability based on static code analysis; our tooling has not executed this code.)
Detailed Step-by-Step Execution Trace:
- Attacker Setup: The attacker serves a web page that creates a WebGL 1 context (
gl = canvas.getContext('webgl')) and supplies a fragment shader. - Shader Characteristics: The attacker’s shader dynamically indexes the built-in output array
gl_FragDatausing a loop counter variable (e.g.,gl_FragData[i]). - Extension Omission: Crucially, the attacker does not enable the multiple render targets extension in the shader source (i.e., they omit
#extension GL_EXT_draw_buffers : enable). - Context Initialization: When the context is initialized on the D3D11 backend (Windows),
libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp:1148setscaps->maxDrawBufferstoGetMaximumSimultaneousRenderTargets(), which is8on feature level 10+. - Context Extensions: Additionally,
:1305setsextensions->drawBuffersEXTtotruebecause the underlying hardware supports it, even though the shader hasn’t explicitly enabled it. - Compiler Initialization: In
libANGLE/Compiler.cpp:67,mResources.MaxDrawBuffersis initialized tocaps.maxDrawBuffers(8). The fallback to1at:247(which checks!extensions.drawBuffersEXT) is skipped because the hardware extension is enabled. Thus,resources.MaxDrawBuffersremains 8. - Type Assignment: When the ANGLE parser runs (
third_party/angle/src/compiler/translator/SymbolTable_autogen.cpp:23784), it initializes the AST type forgl_FragData. Because it is WebGL 1 (SH_WEBGL_SPEC), it sets the array size toresources.MaxDrawBuffers, which is8. - Constant Index Check: The attacker’s dynamic index
ipasses the WebGL 1 ESSL 100 constant-index validation (ParseContext::checkESSL100ConstantIndexatParseContext.cpp:4123) because it is a loop counter (permitted per Appendix A). The AST nodeEOpIndexIndirectis successfully created. - Built-in Desync Flaw: During the first use of
gl_FragData,TParseContext::declareBuiltInOnFirstUse(ParseContext.cpp:3469) is called. It checks if theEXT_draw_buffersextension is enabled in the shader. Since it isn’t, it creates a new type with size1(:3494) to declare the variable in the IR builder. However, it fails to mutate theTTypeon the originalTVariable. The ASTTIntermSymbolforgl_FragDataretains the original array size of 8. - AST Clamping Pass: After parsing, ANGLE runs the
ClampIndirectIndicespass over the AST to enforce bounds-checking on dynamic array accesses (Compiler.cpp:1041). - Clamp Bound Evaluation:
ClampIndirectIndicesTraverserexamines thegl_FragDatanode. It callsleftType.getOutermostArraySize()(tree_ops/ClampIndirectIndices.cpp:74), which reads the size from the unmodifiedTIntermSymboltype. This evaluates to8. - Clamp Insertion: The pass clamps the dynamic index to
arraySize - 1(:82). The AST foriis replaced withint(clamp(float(i), 0.0, 7.0)). - HLSL Header Generation: The clamped AST is handed to
OutputHLSLto generate the D3D11 HLSL string. In the header generation (OutputHLSL.cpp:696), it checksIsExtensionEnabled(..., TExtension::EXT_draw_buffers)for the shader. Since it’s false,numColorValuesevaluates to1. - OOB Declaration:
OutputHLSLemits the static backing array forgl_FragDatanamedgl_Color:static float4 gl_Color[1] = ...(OutputHLSL.cpp:698). - HLSL Body Generation:
OutputHLSLtraverses the AST body. It emitsgl_ColorforEvqFragData(OutputHLSL.cpp:1195) and emits the clamped index for the array access. - Malformed Array Access: The resulting HLSL statement emitted is
gl_Color[int(clamp(float(_i), 0.0, 7.0))] = ..., demonstrating an out-of-bounds access up to index 7 on the size-1 arraygl_Color. - HLSL Compilation: This malformed HLSL string is dispatched to
D3DCompilein the GPU process (libANGLE/renderer/d3d/HLSLCompiler.cpp). - Unrolling Prevention: To prevent static analysis from folding the loop and emitting an
X3504compile-time error,Renderer11.cpp:3108unconditionally passes the macroANGLE_ENABLE_LOOP_FLATTEN=1toD3DCompile, which expandsLOOPto[loop]. - Vulnerability Realized:
D3DCompileis forced to parse, optimize, and compile a dynamic out-of-bounds array write into a static size-1 array. Per the Knowledge Base, feeding such out-of-bounds arrays toD3DCompilefrom attacker GLSL is an established vector for triggering memory corruption within the text-based shader compiler itself (running inside the sandboxed GPU process).
Suggested Fix:
In third_party/angle/src/compiler/translator/ParseContext.cpp, declareBuiltInOnFirstUse should correctly propagate the shrunken array size to the TVariable or TIntermSymbol nodes when EXT_draw_buffers is missing. Alternatively, ClampIndirectIndices should correctly account for the runtime-declared size of gl_FragData based on extension presence, rather than unconditionally trusting the MaxDrawBuffers-sized AST type.
3. Technical Verification Details (Automated Audit Logs - Reviewers may skip this section)
Prior Critic Verdict Verbatim:
> The report identifies a bug where ANGLE’s translator generates HLSL with an out-of-bounds array access. In WebGL 1, when a shader dynamically indexes gl_FragData[i] without the GL_EXT_draw_buffers extension, OutputHLSL declares gl_Color with size 1. However, the AST node retains its array size of 8 (MaxDrawBuffers), causing the ClampIndirectIndices pass to only clamp the index to [0, 7]. This results in the emitted HLSL accessing up to index 7 on a size-1 array, which is then handed to D3DCompile.
>
> The AI rewriter incorrectly downgraded the severity from High to Medium (S2) by arguing that the runtime execution of the resulting DXBC is bounded to shader-local registers per the D3D11.3 spec, and therefore doesn’t corrupt host memory.
>
> However, this overlooks the explicit rule in the Knowledge Base under High Severity (S1): ‘ANGLE shader translator emits SPIR-V/HLSL with an OOB OpAccessChain / array index from attacker GLSL (built-in redeclared with a smaller size, ClampIndirectIndices pass not run for a backend, constant-fold past declared bound). The emitted IR reaches vkCreateShaderModule/D3DCompile in the GPU process. This is web-content-reachable.’
>
> Our finding is an exact match for this S1 rule (a built-in redeclared with a smaller size resulting in an OOB array index in the HLSL fed to D3DCompile). The vulnerability is that the text-based shader compiler (D3DCompile) is forced to parse and optimize malformed arrays, which is a known vector for triggering memory corruption within the compiler itself (running in the GPU process).
>
> Because D3D11 is Windows-only, the GPU process is sandboxed, so this avoids the Critical (S0) rating applied to unsandboxed Android GPU issues, and correctly caps at High (S1).
Mitigations / Validators Checked:
ANGLE_ENABLE_LOOP_FLATTEN: This is unconditionally passed as"1"toD3DCompileinsideRenderer11.cpp:3108which confirms thatLOOPexpands to[loop]and forces the compiler to parse dynamic indices without compile-time folding.D3DCompileOOB Memory Limits: While runtime bounds might be constrained by DXBC specifications, parsing and optimization of such OOB indices in the HLSL compiler frontend or optimizer represents a high-risk attack surface within the GPU process.- WebGL 2 Check (
ParseContext.cpp:7169): This check only prevents dynamicgl_FragData[i]in WebGL 2 contexts, leaving WebGL 1 contexts fully vulnerable to this out-of-bounds code generation. ClampIndirectIndicesEnforcement: Verified thatisWebGL() || isHardenedContext()enforces the execution of the clamp pass (ShaderD3D.cpp:289-293), leading directly to the7.0max bound.
Evaluated with Chrome root at commit: b96d2ec58f4f5f92b540a723966b199d6e9951b4
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.