CVE-2026-10974
Overview
Files Changed
src/compiler/translator/ParseContext.cppsrc/tests/gl_tests/GLSLValidationTest.cpp
Patch
From d9508fb37d4725a6d6a00a83e392a19403a17f85 Mon Sep 17 00:00:00 2001 From: Shahbaz Youssefi <[email protected]> Date: Mon, 25 May 2026 16:46:48 -0400 Subject: [PATCH] Translator: Disallow equality check on structs-with-samplers The spec doesn't say it, but glslang and drivers don't allow equality check on structs with samplers. This was already checked for ESSL 100. Spec clarification requested at https://gitlab.khronos.org/opengl/API/-/issues/297 Bug: chromium:513135862 Change-Id: I0c6d2215bc3fd9cd7f3f28e59237ebf3b86f12d0 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7875104 Commit-Queue: Shahbaz Youssefi <[email protected]> Reviewed-by: Geoff Lang <[email protected]> --- diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp index 438fde8..b05aec1 100644 --- a/src/compiler/translator/ParseContext.cpp +++ b/src/compiler/translator/ParseContext.cpp @@ -8813,8 +8813,10 @@ // Samplers as l-values are disallowed also in ESSL 3.00, see section 4.1.7, // we interpret the spec so that this extends to structs containing samplers, // similarly to ESSL 1.00 spec. - if ((mShaderVersion < 300 || op == EOpAssign || op == EOpInitialize) && - left->getType().isStructureContainingSamplers()) + // + // ESSL 3.00 doesn't disallow comparison between structs with samplers, but glslang, the + // reference implementation does, as do many drivers. + if (left->getType().isStructureContainingSamplers()) { error(loc, "undefined operation for structs containing samplers", GetOperatorString(op)); diff --git a/src/tests/gl_tests/GLSLValidationTest.cpp b/src/tests/gl_tests/GLSLValidationTest.cpp index da82bd8..70ccd19 100644 --- a/src/tests/gl_tests/GLSLValidationTest.cpp +++ b/src/tests/gl_tests/GLSLValidationTest.cpp @@ -334,6 +334,25 @@ "'==' : undefined operation for structs containing samplers"); } +// The ESSL 3.00 spec says that equality is supported for all types, but glslang does not accept +// equality between structs with samplers. glslang is the reference compiler, so ANGLE follows +// suit with the same validation. +TEST_P(GLSLValidationTest, CompareStructsContainingSamplersESSL300) +{ + constexpr char kFS[] = R"(#version 300 es +precision mediump float; +struct S { sampler2D s; }; +uniform S a; +uniform S b; +out vec4 c; +void main() { + c = vec4(a == b ? 1.0 : 0.0); +})"; + + validateError(GL_FRAGMENT_SHADER, kFS, + "'==' : undefined operation for structs containing samplers"); +} + // https://crbug.com/499176133 TEST_P(GLSLValidationTest, LongIdentifierAtLimit_1024) {
Regression Test / PoC
diff --git a/src/tests/gl_tests/GLSLValidationTest.cpp b/src/tests/gl_tests/GLSLValidationTest.cpp
index da82bd8..70ccd19 100644
--- a/src/tests/gl_tests/GLSLValidationTest.cpp
+++ b/src/tests/gl_tests/GLSLValidationTest.cpp
@@ -334,6 +334,25 @@
"'==' : undefined operation for structs containing samplers");
}
+// The ESSL 3.00 spec says that equality is supported for all types, but glslang does not accept
+// equality between structs with samplers. glslang is the reference compiler, so ANGLE follows
+// suit with the same validation.
+TEST_P(GLSLValidationTest, CompareStructsContainingSamplersESSL300)
+{
+ constexpr char kFS[] = R"(#version 300 es
+precision mediump float;
+struct S { sampler2D s; };
+uniform S a;
+uniform S b;
+out vec4 c;
+void main() {
+ c = vec4(a == b ? 1.0 : 0.0);
+})";
+
+ validateError(GL_FRAGMENT_SHADER, kFS,
+ "'==' : undefined operation for structs containing samplers");
+}
+
// https://crbug.com/499176133
TEST_P(GLSLValidationTest, LongIdentifierAtLimit_1024)
{
Original Bug Report
ANGLE: Potential validation bypass for equality ops on sampler-only structs in ESSL 3.00+
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 without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: ANGLE’s ESSL translator incorrectly allows equality comparisons on structures containing samplers when using ESSL 3.00 or higher. This leads to the generation of malformed SPIR-V modules after AST transformations fail to account for the invalid state. These malformed modules are passed to the native Vulkan driver, potentially allowing for exploitation of driver-level vulnerabilities.
Affected files:
third_party/angle/src/compiler/translator/ParseContext.cppthird_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cppthird_party/angle/src/compiler/translator/spirv/OutputSPIRV.cpp
Estimated timestamp from git blame: 2015-03-23
Summary
A potential vulnerability exists in the ANGLE ESSL translator where front-end validation fails to prevent equality operators (== and !=) on structures containing samplers in ESSL 3.00+. This oversight leads to a corrupted Abstract Syntax Tree (AST) after subsequent transformations, resulting in the emission of malformed SPIR-V modules to the underlying Vulkan driver.
Root Cause Analysis
1. Front-end Validation Bypass
In third_party/angle/src/compiler/translator/ParseContext.cpp, the function binaryOpCommonCheck handles validation for binary operators. For ESSL 3.00+, the check intended to prevent operations on structs containing samplers is bypassed for equality operators:
// ParseContext.cpp:8798
if ((mShaderVersion < 300 || op == EOpAssign || op == EOpInitialize) &&
left->getType().isStructureContainingSamplers())
{
error(loc, "undefined operation for structs containing samplers", ...);
return false;
}
For #version 300 es and op == EOpEqual, this condition evaluates to false, allowing the comparison to proceed into the AST.
2. AST Transformation Failure
The RewriteStructSamplers pass (RewriteStructSamplers.cpp) extracts samplers from uniforms. If a uniform struct contains only samplers, the pass removes the uniform declaration entirely. However, because RewriteStructSamplersTraverser::visitBinary only handles indexing operators, it fails to rewrite or remove the EOpEqual node. This leaves the AST with orphaned TIntermSymbol nodes referencing deleted uniform declarations.
3. Malformed SPIR-V Generation
During SPIR-V emission in OutputSPIRV.cpp, the generator encounters these orphaned symbols. In release builds, internal UNREACHABLE() guards are no-ops. The generator proceeds to emit irregular SPIR-V:
- It creates an
OpVariableinStorageClassUniformusing the original struct type (which may still contain sampled-image members). - It applies a
BuiltIndecoration with an invalid sentinel value (BuiltInMax). - It emits
OpIEqualinstructions operating directly on sampled-image types, which violates the SPIR-V specification.
Security Impact
By providing a specifically crafted GLSL shader, a malicious web page can force ANGLE to emit malformed SPIR-V and pass it to the native Vulkan driver’s vkCreateShaderModule. On platforms where the GPU process is unsandboxed (notably Android), a vulnerability in the native driver triggered by this malformed input could lead to code execution in the context of the GPU process.
Potential Reproduction Steps
- Use a Chromium build with the ANGLE Vulkan backend.
- Compile and link a fragment shader containing an equality comparison on a struct with only samplers:
#version 300 es precision mediump float; struct S { sampler2D s; }; uniform S a; uniform S b; out vec4 color; void main() { color = vec4(a == b ? 1.0 : 0.0); } - Observe (in a debugger or via driver logs) the emission of invalid SPIR-V to
vkCreateShaderModule.
Suggested Fix
Update TParseContext::binaryOpCommonCheck in ParseContext.cpp to correctly enforce the restriction on structs containing samplers for all versions and comparison operators:
if (left->getType().isStructureContainingSamplers())
{
error(loc, "undefined operation for structs containing samplers", GetOperatorString(op));
return false;
}
Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e
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.