CVE-2026-12023
Overview
Files Changed
gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
Patch
From fc3dd3d9c8c7c40a542c891eb2d51cbbeea0c7bb Mon Sep 17 00:00:00 2001
From: Ken Russell <kbr@chromium.org>
Date: Wed, 03 Jun 2026 14:20:59 -0700
Subject: [PATCH] Clear ANGLE's blob cache callbacks upon context loss.
Prevent later calls against a deleted command decoder.
A test wasn't practical because it would have required extensive and
intrusive scaffolding.
Fixed: 517018374
Change-Id: If774d9efae62558f93c61fef924f5765dac86331
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7899528
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Auto-Submit: Kenneth Russell <kbr@chromium.org>
Commit-Queue: Kenneth Russell <kbr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1641206}
---
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index 5b9a46f..82692c54 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -1706,10 +1706,15 @@
resources_->MarkContextLost();
}
- // SECURITY: crbug.com/500187083. Unconditionally clear the debug callback
- // if current context IsCurrent before it gets lost to prevent UAF.
+ // SECURITY: crbug.com/500187083 and crbug.com/517018374.
+ // Unconditionally clear per-context callbacks that hold a raw
+ // `this` pointer if the context IsCurrent before it gets lost, to
+ // prevent UAF when Destroy(have_context=false) skips them.
if (api()) {
api()->glDebugMessageCallbackKHRFn(nullptr, nullptr);
+ if (feature_info_ && feature_info_->feature_flags().angle_blob_cache) {
+ api()->glBlobCacheCallbacksANGLEFn(nullptr, nullptr, nullptr);
+ }
}
}
Original Bug Report
Potential GPU Process Use-After-Free via Stale glBlobCacheCallbacksANGLE in MarkContextLost
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: A potential Use-After-Free vulnerability exists in the GPU process because GLES2DecoderPassthroughImpl fails to clear registered ANGLE blob cache callbacks during context loss teardown. If the underlying GLContext is kept alive or resurrected, subsequent shader compilation or program linking can trigger the stale callbacks on the freed decoder. This could potentially allow an attacker to achieve arbitrary code execution within the GPU process.
Affected files:
gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
Estimated timestamp from git blame: 2024-10-24
Root Cause Analysis
In gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc, Initialize() registers ANGLE blob cache callbacks using a raw this pointer when the angle_blob_cache feature is enabled:
if (feature_info_->feature_flags().angle_blob_cache) {
api()->glBlobCacheCallbacksANGLEFn(PassthroughGLBlobCacheSetCallback,
PassthroughGLBlobCacheGetCallback, this);
}
The security patch for crbug.com/500187083 cleared the KHR_debug callback inside MarkContextLost() to prevent a potential Use-After-Free (UAF) of the decoder. However, the patch omitted clearing the sibling glBlobCacheCallbacksANGLE callback registered with the same raw this pointer.
When a context is lost, MarkContextLost() sets context_lost_ = true. During command buffer teardown, the stub computes have_context as false because the context has been lost (making MakeCurrent() return false early). Consequently, Destroy(have_context = false) is called on the decoder. This completely bypasses the cleanup of the blob-cache callback, as it is gated on have_context being true:
void GLES2DecoderPassthroughImpl::Destroy(bool have_context) {
if (have_context && feature_info_->feature_flags().angle_blob_cache) {
api()->glBlobCacheCallbacksANGLEFn(nullptr, nullptr, nullptr);
}
...
}
As a result, a stale callback pointing to the destroyed GLES2DecoderPassthroughImpl remains registered inside ANGLE’s gl::Context state.
Context Retention & Resurrection
On macOS, IOSurfaceBackingEGLState can hold a strong reference to the decoder’s gl::GLContext (and thus the underlying ANGLE gl::Context) beyond the destruction of the decoder itself. When the last reference to IOSurfaceBackingEGLState is dropped, its destructor attempts to make the context current again via ui::ScopedMakeCurrent:
IOSurfaceBackingEGLState::~IOSurfaceBackingEGLState() {
ui::ScopedMakeCurrent smc(context_.get(), surface_.get());
...
}
If restoration of the original context inside ~ScopedMakeCurrent fails (for instance, due to surface invalidation), the resurrected context containing the stale callback pointers remains thread-current.
Potential Steps to Trigger
Note: The following are suggested/potential steps to trigger the issue, as our analysis tooling does not currently have the capability to execute a live proof-of-concept.
- Establish a GLES2 passthrough command buffer from a compromised renderer.
- Force a context loss event so
MarkContextLost()runs and setscontext_lost_ = trueon the decoder. - The
CommandBufferStubdestructor runs, callingDestroy(have_context = false)which skips clearing the ANGLE blob cache callback. TheGLES2DecoderPassthroughImplinstance is deleted, but the ANGLE context and its registered callbacks are kept alive viaIOSurfaceBackingEGLState. - When
IOSurfaceBackingEGLStateis subsequently destroyed, its destructor attempts context restoration viaui::ScopedMakeCurrent. If this fails, the resurrected context with stale callbacks remains thread-current. - Trigger subsequent shader compilation or program linking operations on the thread. This causes ANGLE to query or write to its
BlobCache, executingPassthroughGLBlobCacheGetCallbackorPassthroughGLBlobCacheSetCallbackwith the staleuser_parampointer, resulting in a Use-After-Free.
Suggested Fix
Unconditionally clear the registered ANGLE blob cache callback in GLES2DecoderPassthroughImpl::MarkContextLost if have_context is true, matching the security fix applied for the KHR_debug callback:
// gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
void GLES2DecoderPassthroughImpl::MarkContextLost(error::ContextLostReason reason) {
...
bool have_context = context_ && context_->IsCurrent(nullptr);
if (have_context) {
...
if (api()) {
api()->glDebugMessageCallbackKHRFn(nullptr, nullptr);
if (feature_info_->feature_flags().angle_blob_cache) {
api()->glBlobCacheCallbacksANGLEFn(nullptr, nullptr, nullptr);
}
}
}
...
}
Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8
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.