CVE-2026-9982
Overview
Files Changed
src/compiler/translator/ParseContext.cppsrc/tests/gl_tests/GLSLValidationTest.cpp
Patch
From 77daa7a3be822e5446122a9500cf61c2b9eaf38b Mon Sep 17 00:00:00 2001
From: Shahbaz Youssefi <syoussefi@chromium.org>
Date: Fri, 15 May 2026 09:27:48 -0400
Subject: [PATCH] Translator: Disallow structs-with-sampelrs in constructors
Bug: chromium:513001247
Change-Id: I7bf0ed6d7a3a12588ca523348531679dcbc6ad89
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7852696
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
---
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index ef8d290..a256765 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -1272,6 +1272,12 @@
error(line, reason.c_str(), "constructor");
return false;
}
+ else if (argTyped->getType().isStructureContainingSamplers())
+ {
+ std::string reason("cannot convert a variable with struct type containing samplers");
+ error(line, reason.c_str(), "constructor");
+ return false;
+ }
else if (argTyped->getMemoryQualifier().writeonly)
{
error(line, "cannot convert a variable with writeonly", "constructor");
diff --git a/src/tests/gl_tests/GLSLValidationTest.cpp b/src/tests/gl_tests/GLSLValidationTest.cpp
index 3446dcb..c63e089 100644
--- a/src/tests/gl_tests/GLSLValidationTest.cpp
+++ b/src/tests/gl_tests/GLSLValidationTest.cpp
@@ -1128,6 +1128,25 @@
"'constructor' : cannot convert a variable with type sampler2D");
}
+// Test that a struct-with-sampler can't be used in a constructor
+TEST_P(GLSLValidationTest_ES3, ConstructorWithStructWithSampler)
+{
+ constexpr char kFS[] = R"(#version 300 es
+ precision mediump float;
+ struct S {
+ sampler2D inStruct;
+ };
+ uniform S s;
+ out vec4 color;
+ void main()
+ {
+ color = texture(S[2](s, s)[0].inStruct, vec2(0));
+ })";
+
+ validateError(GL_FRAGMENT_SHADER, kFS,
+ "'constructor' : cannot convert a variable with struct type containing samplers");
+}
+
// Test that void can't be used in constructor argument list
TEST_P(GLSLValidationTest, VoidInConstructorArguments)
{
Regression Test / PoC
diff --git a/src/tests/gl_tests/GLSLValidationTest.cpp b/src/tests/gl_tests/GLSLValidationTest.cpp
index 3446dcb..c63e089 100644
--- a/src/tests/gl_tests/GLSLValidationTest.cpp
+++ b/src/tests/gl_tests/GLSLValidationTest.cpp
@@ -1128,6 +1128,25 @@
"'constructor' : cannot convert a variable with type sampler2D");
}
+// Test that a struct-with-sampler can't be used in a constructor
+TEST_P(GLSLValidationTest_ES3, ConstructorWithStructWithSampler)
+{
+ constexpr char kFS[] = R"(#version 300 es
+ precision mediump float;
+ struct S {
+ sampler2D inStruct;
+ };
+ uniform S s;
+ out vec4 color;
+ void main()
+ {
+ color = texture(S[2](s, s)[0].inStruct, vec2(0));
+ })";
+
+ validateError(GL_FRAGMENT_SHADER, kFS,
+ "'constructor' : cannot convert a variable with struct type containing samplers");
+}
+
// Test that void can't be used in constructor argument list
TEST_P(GLSLValidationTest, VoidInConstructorArguments)
{
Original Bug Report
Memory corruption in ANGLE via sampler-bearing struct array constructors
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: A logic error in ANGLE’s SPIR-V translator allows sampler-bearing structures to be used in array constructors, bypassing intended security checks. This results in the generation of malformed SPIR-V instructions, which can lead to type confusion and potential memory corruption within the GPU process.
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.cppthird_party/angle/src/compiler/translator/Types.cppthird_party/angle/src/compiler/translator/spirv/TranslatorSPIRV.cpp
Estimated timestamp from git blame: Unknown (Google3 checkout)
Summary
A potential vulnerability has been identified in ANGLE’s SPIR-V translator that allows for the creation of malformed SPIR-V. The issue stems from a failure to detect opaque types (like samplers) nested within structures during constructor validation, combined with a failure to update type information during AST transformations. This can lead to spec-invalid SPIR-V being passed to the underlying Vulkan driver, potentially resulting in memory corruption in the GPU process.
Root Cause Analysis
1. Incomplete Opaque Type Check in checkConstructorArguments
In third_party/angle/src/compiler/translator/ParseContext.cpp, the function checkConstructorArguments validates constructor parameters. It checks if an argument is an opaque type using IsOpaqueType(argTyped->getBasicType()). However, for a structure containing a sampler, the basic type is EbtStruct, which IsOpaqueType returns false for. This allows constructors for structures containing samplers (which are generally prohibited) to be successfully parsed into the AST, especially when used within an array constructor.
2. Stale Type Information in RewriteStructSamplers
The RewriteStructSamplers transformation (in third_party/angle/src/compiler/translator/tree_ops/RewriteStructSamplers.cpp) extracts samplers from uniforms and creates a “stripped” version of the structure. While it updates symbols and field selections, it does not override visitAggregate. Since EOpConstruct (constructor) nodes are a type of TIntermAggregate, they are not visited, and their mType field retains the original, sampler-bearing structure type. This creates an inconsistency where a constructor expecting a sampler-bearing struct is passed a stripped struct as an argument.
Technical Impact
During SPIR-V emission in OutputSPIRV.cpp, this AST inconsistency results in malformed instructions:
- Mismatched
OpCompositeConstruct: The instruction is emitted with a result type ID for the original sampler-bearing struct array, but with constituents that are the stripped structs. This violates SPIR-V type matching rules. - Invalid
OpCompositeExtract: Operations like struct comparisons (==) on these arrays will attempt to extract fields based on the original struct layout. This leads to emittingOpCompositeExtractwith incorrect indices, causing type confusion in the Vulkan driver (e.g., treating a float as a sampler descriptor).
Since SPIR-V validation is typically disabled in release builds, these malformed blobs are passed directly to vkCreateShaderModule. Driver behavior on such invalid input is undefined and has historically led to memory corruption within the shader compiler.
Potential Attack Vector
- A compromised renderer process provides a crafted GLSL fragment shader to the WebGL2 API.
- The shader defines a struct with a sampler and uses it in an array constructor:
struct S { sampler2D s; float f; }; uniform S a; ... S[1](a) == S[1](a) .... - ANGLE translates this into malformed SPIR-V and passes it to the GPU process.
- The Vulkan driver in the GPU process crashes or suffers memory corruption while compiling the malformed shader.
On platforms like Android, where the GPU process is unsandboxed, this could potentially allow for a significant elevation of privilege.
Suggested Fix
- Improve Validation: In
TParseContext::checkConstructorArguments, replace theIsOpaqueTypecheck with a recursive check (e.g.,ContainsOpaque<IsOpaqueFunc>(argTyped->getType())) to ensure structures containing opaque types cannot be used as constructor arguments. - Update Transformations: In
RewriteStructSamplers.cpp, overridevisitAggregateto ensure thatEOpConstructnodes have theirmTypeupdated to the stripped structure type when their constituents are modified.
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.