CVE-2026-79275
Overview
Files Changed
src/libANGLE/Context.cppsrc/libANGLE/State.cppsrc/libANGLE/State.hsrc/tests/gl_tests/LinkAndRelinkTest.cpp
Patch
From e4be88f0823379ae1b20dcf20890df0a885a9ba0 Mon Sep 17 00:00:00 2001 From: Shahbaz Youssefi <[email protected]> Date: Wed, 22 Jul 2026 22:02:38 -0400 Subject: [PATCH] Unset active textures on program relink While this didn't manifest as a bug, for uniformity with binding a new program, unsetActiveTextures() is now called when relinking the program too. Both operations install a new executable, so it makes sense they do the same state clean up. Bug: chromium:536659904 Change-Id: I19c0bd129e03859d242cb15c7e11969385156f2b Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/8135183 Reviewed-by: Geoff Lang <[email protected]> Commit-Queue: Shahbaz Youssefi <[email protected]> --- diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp index 3b1e69a..45f5d83 100644 --- a/src/libANGLE/Context.cpp +++ b/src/libANGLE/Context.cpp @@ -9492,6 +9492,7 @@ { Program *program = mState.getProgram(); ASSERT(program->isLinked()); + mState.onCurrentExecutableRelink(); ANGLE_CONTEXT_TRY(mState.installProgramExecutable(this)); mStateCache.onProgramExecutableChange(this); break; @@ -9515,6 +9516,7 @@ mStateCache.onProgramExecutableChange(this); break; case angle::SubjectMessage::ProgramRelinked: + mState.onCurrentExecutableRelink(); ANGLE_CONTEXT_TRY(mState.installProgramPipelineExecutable(this)); mStateCache.onProgramExecutableChange(this); break; diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp index db7a548..04f20f8 100644 --- a/src/libANGLE/State.cpp +++ b/src/libANGLE/State.cpp @@ -4248,6 +4248,16 @@ mDirtyBits.set(state::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING); } +void State::onCurrentExecutableRelink() +{ + // Called when a program or PPO is already current but its executable is recreated. The state + // of the previous executable is cleaned up before the new executable is installed. + if (mExecutable) + { + unsetActiveTextures(mExecutable->getActiveSamplersMask()); + } +} + void State::initializeForCapture(const Context *context) { mPrivateState.initializeForCapture(context); diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h index 0989879..3880642 100644 --- a/src/libANGLE/State.h +++ b/src/libANGLE/State.h @@ -1253,6 +1253,7 @@ void onUniformBufferStateChange(size_t uniformBufferIndex, angle::SubjectMessage message); void onAtomicCounterBufferStateChange(size_t atomicCounterBufferIndex); void onShaderStorageBufferStateChange(size_t shaderStorageBufferIndex); + void onCurrentExecutableRelink(); bool isCurrentTransformFeedback(const TransformFeedback *tf) const { diff --git a/src/tests/gl_tests/LinkAndRelinkTest.cpp b/src/tests/gl_tests/LinkAndRelinkTest.cpp index 6b91b68..a894889 100644 --- a/src/tests/gl_tests/LinkAndRelinkTest.cpp +++ b/src/tests/gl_tests/LinkAndRelinkTest.cpp @@ -956,6 +956,106 @@ glDeleteProgramPipelines(1, &pipeline); } +// Test relinking a program with a shader that uses fewer textures. +TEST_P(LinkAndRelinkTestES3, RelinkRemovesOneTexture) +{ + // Create a program with two textures, then relink it with a shader that removes one of the + // textures. + constexpr char kFS1[] = R"(#version 300 es +precision mediump float; +uniform sampler2D t0; +uniform sampler2D t1; +out vec4 color; +void main() +{ + color = texture(t0, vec2(0)) + texture(t1, vec2(0)); +})"; + constexpr char kFS2[] = R"(#version 300 es +precision mediump float; +uniform sampler2D t0; +out vec4 color; +void main() +{ + color = texture(t0, vec2(0)); +})"; + + GLuint program = glCreateProgram(); + + GLuint vs = CompileShader(GL_VERTEX_SHADER, essl3_shaders::vs::Simple()); + GLuint fs1 = CompileShader(GL_FRAGMENT_SHADER, kFS1); + GLuint fs2 = CompileShader(GL_FRAGMENT_SHADER, kFS2); + + EXPECT_NE(0u, vs); + EXPECT_NE(0u, fs1); + EXPECT_NE(0u, fs2); + + glAttachShader(program, vs); + glAttachShader(program, fs1); + glLinkProgram(program); + glUseProgram(program); + glUniform1i(glGetUniformLocation(program, "t0"), 0); + glUniform1i(glGetUniformLocation(program, "t1"), 1); + ASSERT_GL_NO_ERROR(); + + constexpr GLColor kTexture0Color = GLColor(20, 30, 0, 100); + constexpr GLColor kTexture1Color = GLColor(70, 10, 55, 50); + + GLTexture texture0; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture0); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &kTexture0Color); + + GLTexture texture1; + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, texture1); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &kTexture1Color); + + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + + // Draw once, which sums the two textures. + drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); + ASSERT_GL_NO_ERROR(); + + // Mark texture1 dirty so it's tracked for getting synced. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + + // Attach the second fragment shader and relink the program + glDetachShader(program, fs1); + glAttachShader(program, fs2); + glLinkProgram(program); + glUseProgram(program); + glUniform1i(glGetUniformLocation(program, "t0"), 0); + + // Delete the texture and bind another one in its place. + texture1.reset(); + + GLTexture texture2; + glBindTexture(GL_TEXTURE_2D, texture2); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &kTexture1Color); + + // Draw again, which accumulates the color from the first texture. + drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); + ASSERT_GL_NO_ERROR(); + + // Verify results + constexpr GLColor kExpect( + kTexture0Color.R * 2 + kTexture1Color.R, kTexture0Color.G * 2 + kTexture1Color.G, + kTexture0Color.B * 2 + kTexture1Color.B, kTexture0Color.A * 2 + kTexture1Color.A); + EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpect, 9); + + glDeleteShader(vs); + glDeleteShader(fs1); + glDeleteShader(fs2); + glDeleteProgram(program); + ASSERT_GL_NO_ERROR(); +} + ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(LinkAndRelinkTest); ANGLE_INSTANTIATE_TEST_ES3(LinkAndRelinkTestES3);
Regression Test / PoC
diff --git a/src/tests/gl_tests/LinkAndRelinkTest.cpp b/src/tests/gl_tests/LinkAndRelinkTest.cpp
index 6b91b68..a894889 100644
--- a/src/tests/gl_tests/LinkAndRelinkTest.cpp
+++ b/src/tests/gl_tests/LinkAndRelinkTest.cpp
@@ -956,6 +956,106 @@
glDeleteProgramPipelines(1, &pipeline);
}
+// Test relinking a program with a shader that uses fewer textures.
+TEST_P(LinkAndRelinkTestES3, RelinkRemovesOneTexture)
+{
+ // Create a program with two textures, then relink it with a shader that removes one of the
+ // textures.
+ constexpr char kFS1[] = R"(#version 300 es
+precision mediump float;
+uniform sampler2D t0;
+uniform sampler2D t1;
+out vec4 color;
+void main()
+{
+ color = texture(t0, vec2(0)) + texture(t1, vec2(0));
+})";
+ constexpr char kFS2[] = R"(#version 300 es
+precision mediump float;
+uniform sampler2D t0;
+out vec4 color;
+void main()
+{
+ color = texture(t0, vec2(0));
+})";
+
+ GLuint program = glCreateProgram();
+
+ GLuint vs = CompileShader(GL_VERTEX_SHADER, essl3_shaders::vs::Simple());
+ GLuint fs1 = CompileShader(GL_FRAGMENT_SHADER, kFS1);
+ GLuint fs2 = CompileShader(GL_FRAGMENT_SHADER, kFS2);
+
+ EXPECT_NE(0u, vs);
+ EXPECT_NE(0u, fs1);
+ EXPECT_NE(0u, fs2);
+
+ glAttachShader(program, vs);
+ glAttachShader(program, fs1);
+ glLinkProgram(program);
+ glUseProgram(program);
+ glUniform1i(glGetUniformLocation(program, "t0"), 0);
+ glUniform1i(glGetUniformLocation(program, "t1"), 1);
+ ASSERT_GL_NO_ERROR();
+
+ constexpr GLColor kTexture0Color = GLColor(20, 30, 0, 100);
+ constexpr GLColor kTexture1Color = GLColor(70, 10, 55, 50);
+
+ GLTexture texture0;
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture0);
+ glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &kTexture0Color);
+
+ GLTexture texture1;
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, texture1);
+ glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &kTexture1Color);
+
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE);
+
+ // Draw once, which sums the two textures.
+ drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // Mark texture1 dirty so it's tracked for getting synced.
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+
+ // Attach the second fragment shader and relink the program
+ glDetachShader(program, fs1);
+ glAttachShader(program, fs2);
+ glLinkProgram(program);
+ glUseProgram(program);
+ glUniform1i(glGetUniformLocation(program, "t0"), 0);
+
+ // Delete the texture and bind another one in its place.
+ texture1.reset();
+
+ GLTexture texture2;
+ glBindTexture(GL_TEXTURE_2D, texture2);
+ glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &kTexture1Color);
+
+ // Draw again, which accumulates the color from the first texture.
+ drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // Verify results
+ constexpr GLColor kExpect(
+ kTexture0Color.R * 2 + kTexture1Color.R, kTexture0Color.G * 2 + kTexture1Color.G,
+ kTexture0Color.B * 2 + kTexture1Color.B, kTexture0Color.A * 2 + kTexture1Color.A);
+ EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpect, 9);
+
+ glDeleteShader(vs);
+ glDeleteShader(fs1);
+ glDeleteShader(fs2);
+ glDeleteProgram(program);
+ ASSERT_GL_NO_ERROR();
+}
+
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(LinkAndRelinkTest);
ANGLE_INSTANTIATE_TEST_ES3(LinkAndRelinkTestES3);
Original Bug Report
Potential GPU-process UAF via State::syncDirtyObjects desync and program relinking 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: An atomicity violation in ANGLE’s State::syncDirtyObjects can desynchronize dirty texture sub-masks if a handler returns an early Stop without losing context. Combined with missing active texture cache invalidation during program relinking, this potentially enables a Use-After-Free condition in the GPU process.
Affected files:
third_party/angle/src/libANGLE/State.hthird_party/angle/src/libANGLE/State.cppthird_party/angle/src/libANGLE/Context.cppthird_party/angle/src/libANGLE/renderer/vulkan/TextureVk.cpp
Estimated timestamp from git blame: 2023-09-06
1. Summary of the Issue (Meant for Human Triage)
An atomicity break exists within the ANGLE front-end state synchronization mechanism (State::syncDirtyObjects). During state synchronization, ANGLE sequentially executes dirty object handlers. If an early handler (e.g., DIRTY_OBJECT_ACTIVE_TEXTURES) successfully clears its fine-grained sub-mask, but a subsequent handler (e.g., DIRTY_OBJECT_DRAW_FRAMEBUFFER) hits a non-fatal backend validation error and returns an early angle::Result::Stop, the loop terminates. This abort prevents the clearing of the coarse mDirtyObjects bitset and later sub-masks like mDirtyTextures, leading to a state desynchronization where mDirtyTextures indicates a texture needs synchronization, but mDirtyActiveTextures does not.
An attacker can exploit this desynchronization in conjunction with a logic flaw in glLinkProgram. When a program is re-linked, State::installProgramExecutable replaces the current executable without calling unsetActiveTextures for the old executable’s samplers mask (unlike the standard State::setProgram swap path). By manipulating the active program so that it no longer samples a specific texture unit, the attacker can cause the active textures cache (mActiveTexturesCache) to hold a dangling raw pointer to a deleted Texture object. A subsequent draw call processes the stale mDirtyTextures bit and dereferences this dangling pointer, leading to a potential Use-After-Free (UAF) read and virtual function call in the GPU process.
This vulnerability is reachable by a compromised renderer via forged command-buffer IPCs. Because the GPU process is unsandboxed on Android, this memory corruption potentially allows for arbitrary code execution with the user’s full privileges on that platform.
2. Proof-of-Concept & Detailed Execution Flow
Root Cause Analysis
In third_party/angle/src/libANGLE/State.h, State::syncDirtyObjects synchronizes dirty objects in an enumerated order:
ANGLE_INLINE angle::Result State::syncDirtyObjects(const Context *context,
const state::DirtyObjects &bitset,
Command command)
{
mDirtyObjects |= mPrivateState.getDirtyObjects();
mPrivateState.clearDirtyObjects();
...
const state::DirtyObjects &dirtyObjects = mDirtyObjects & bitset;
for (size_t dirtyObject : dirtyObjects)
{
ANGLE_TRY(dirtyObjectHandler(dirtyObject, context, command)); // Line 1755: Early return on Stop
}
mDirtyObjects &= ~dirtyObjects; // Line 1758: Skipped on Stop
return angle::Result::Continue;
}
The handlers execute in DirtyObjectType enum order. DIRTY_OBJECT_ACTIVE_TEXTURES (handler 0) runs first, updating mActiveTexturesCache and explicitly calling mDirtyActiveTextures.reset() (State.cpp:3842). If a subsequent handler, such as DIRTY_OBJECT_DRAW_FRAMEBUFFER (handler 6), returns an early Stop, the loop exits. mDirtyObjects and mDirtyTextures retain their bits, while mDirtyActiveTextures remains empty.
Non-Context-Losing Validation Error
Certain backend errors are non-fatal and do not mark the context as lost. On the Vulkan backend, attaching a multisampled framebuffer with an unsupported color format triggers a non-fatal VK_ERROR_FORMAT_NOT_SUPPORTED (TextureVk.cpp:4346):
if (ANGLE_UNLIKELY(mState.hasBeenBoundToMSRTTFramebuffer() && !supportsMSRTTUsage))
{
ERR() << "Texture bound to EXT_multisampled_render_to_texture framebuffer, ...";
ANGLE_VK_TRY(contextVk, VK_ERROR_FORMAT_NOT_SUPPORTED);
}
This error is mapped to GL_INVALID_OPERATION by DefaultGLErrorCode (ContextVk.cpp:94). ErrorSet::handleError (Context.cpp:10292) only marks the context as lost for GL_OUT_OF_MEMORY. Thus, the context remains alive and can continue accepting commands despite the Stop returned to syncDirtyObjects.
The Cache Invalid Bypass via Relinking
If the attacker re-links the active program instead of changing it, Context::onSubjectStateChange (Context.cpp:9501) invokes State::installProgramExecutable. Unlike State::setProgram, this function does not clear the old executable’s texture mask:
angle::Result State::installProgramExecutable(const Context *context)
{
... // Replaces executable without calling unsetActiveTextures(mExecutable->getActiveSamplersMask())
InstallExecutable(context, mProgram->getSharedExecutable(), &mExecutable);
return onExecutableChange(context);
}
onExecutableChange (State.cpp:4083) iterates strictly over the new executable’s getActiveSamplersMask(). If a unit is removed in the new shader, mActiveTexturesCache keeps the old raw Texture*.
Potential Exploitation Sequence
(Note: Our tooling agent does not have the ability to run code; these are hypothesized execution steps based on the code analysis.)
- Setup Program:
glUseProgram(P1)whereP1actively samples from texture unitiof typeT. - Setup Texture:
glActiveTexture(GL_TEXTURE0 + i);glBindTexture(T, texA). Mutate parameters (e.g.,glTexParameteri) somDirtyActiveTextures[i] = 1andmDirtyTextures[i] = 1. - Setup Failing FBO: Bind a draw framebuffer designed to trigger the Vulkan
VK_ERROR_FORMAT_NOT_SUPPORTEDerror (e.g., usingglFramebufferTexture2DMultisampleEXTwith a format incompatible with multisampled rendering). - Trigger Desync: Call
glDrawArrays().syncActiveTextures(handler 0) runs, cachingtexAinmActiveTexturesCache[i]and resettingmDirtyActiveTextures.syncDrawFramebuffer(handler 6) encounters the format error and aborts the loop with a non-context-losingStop.mDirtyTextures[i]remains1.
- Relink to Bypass Cache Update: Bind a valid FBO. Modify
P1’s shader source to stop sampling uniti. IssueglLinkProgram(P1).installProgramExecutablereplaces the executable but fails to resetmActiveTexturesCache[i], which still points totexA. - Create Dangling Pointer: Bind a new texture
texBto uniti. InState::setSamplerTexture(State.cpp:2731),updateTextureBindingis skipped because!mExecutable->getActiveSamplersMask()[getActiveSampler()]. However,mSamplerTexturesis still updated (State.cpp:2738), dropping the strong reference totexA. DeletetexAviaglDeleteTextures().State::detachTexturefails to findtexAinmSamplerTextures, destroyingtexAwhile leavingmActiveTexturesCache[i]as a raw dangling pointer. - Trigger UAF: Issue a second
glDrawArrays().- Handler 0 skips because
mDirtyActiveTexturesis clear. - Handler 8 (
syncTextures) iterates over the stalemDirtyTextures[i]bit. - It fetches the dangling pointer
texture = mActiveTexturesCache[i]. - It executes the UAF read
texture->hasAnyDirtyBit()and potentiallytexture->syncState(), resulting in a virtual method call (mTexture->syncState) hijacked by the attacker’s heap data.
- Handler 0 skips because
Suggested Fix
Ensure that State::installProgramExecutable and State::installProgramPipelineExecutable correctly invoke unsetActiveTextures for the existing mExecutable->getActiveSamplersMask() prior to installing the new executable, mirroring the safety guarantees in State::setProgram. Additionally, the atomicity logic within State::syncDirtyObjects should be evaluated so early returns rollback or re-evaluate sub-masks (like mDirtyActiveTextures) if the entire state synchronization fails to complete.
3. Technical Verification Details (Automated Audit Logs - Reviewers may skip this section)
> “The primary §G3.9 GPU-STATE desync and non-OOM Stop path are valid. The rewriter correctly noted that glUseProgram(P2) calls unsetActiveTextures, which resets mActiveTexturesCache[i] and defeats the provided UAF chain. However, the exact same cache-not-reset primitive can be achieved by mutating and re-linking the currently active program (glLinkProgram(P1)) instead of swapping programs. When P1 is relinked, Context::onSubjectStateChange calls mState.installProgramExecutable(this). This replaces mExecutable and calls onExecutableChange(context), which iterates only over the new executable’s mask. Crucially, unlike State::setProgram, installProgramExecutable does not call unsetActiveTextures for the old mask. If the relinked program no longer samples unit i, updateTextureBinding is skipped, and mActiveTexturesCache[i] remains pointing to texA.
>
> From here, the rest of the chain holds: glBindTexture(texB) skips updateTextureBinding (because activeSamplersMask()[i] is false) but overwrites mSamplerTextures; glDeleteTextures(texA) fails to find texA in mSamplerTextures, leaving mActiveTexturesCache[i] dangling. The next glDrawArrays processes the orphaned mDirtyTextures[i] bit, leading to a UAF read and virtual call (texture->syncState()).
>
> Because mActiveTexturesCache holds raw Texture* (no MiraclePtr protection in ANGLE third_party), and this provides a GPU-process UAF reachable via forged command-buffer IPC from a compromised renderer (unsandboxed on Android), this is a High (S1) severity vulnerability.”
Ledger of Verified Traces:
State::syncDirtyObjectsAtomicity Break:third_party/angle/src/libANGLE/State.h:1739-1762. TheANGLE_TRYat line 1755 early-aborts the loop onangle::Result::Stop, bypassingmDirtyObjects &= ~dirtyObjectsat line 1758. Handler 0 clears its mask atState.cpp:3842(mDirtyActiveTextures.reset()) independently of subsequent handlers.- Vulkan Non-Fatal Backend Validation Error:
third_party/angle/src/libANGLE/renderer/vulkan/TextureVk.cpp:4342-4346.ANGLE_VK_TRY(contextVk, VK_ERROR_FORMAT_NOT_SUPPORTED)propagates up throughvk_utils.h:1775-1784’sANGLE_VK_TRYmacro.DefaultGLErrorCode(ContextVk.cpp:83-96) maps this toGL_INVALID_OPERATION.ErrorSet::handleError(Context.cpp:10286-10310) does not mark the context as lost because it checks strictly forGL_OUT_OF_MEMORYcombined withGL_LOSE_CONTEXT_ON_RESET_EXT. State::installProgramExecutableMissing Invalidation:third_party/angle/src/libANGLE/State.cpp:4029-4048. The executable is explicitly updated viaInstallExecutablewithout callingunsetActiveTexturesfor the prior mask. In contrast,State::setProgramdoes this safely atState.cpp:3085.State::onExecutableChangeState Update Flaw:third_party/angle/src/libANGLE/State.cpp:4078-4115. Iterates strictly over the incoming executable’s mask:for (size_t textureIndex : mExecutable->getActiveSamplersMask()). Bypasses deactivated units.State::setSamplerTextureBypass:third_party/angle/src/libANGLE/State.cpp:2729-2741. ChecksmExecutable->getActiveSamplersMask()[getActiveSampler()]. If false,updateTextureBindingis skipped. The arraymSamplerTextures[type][getActiveSampler()]is still overwritten with.set(context, texture), removing the previous strong reference.- Raw Pointer Danger:
third_party/angle/src/libANGLE/State.h:79declaresActiveTextureArray<Texture *> mTextures;. ANGLE represents a third-party dependency with raw C++ pointers, omitting MiraclePtr protection against standard memory corruption.
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.