Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free GPU
DescriptionUse after free GPU
ComponentChromium
Bug ClassUAF
Tracker517018374
Fix commitfc3dd3d9c8c7 (chromium/src) +7/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-11

Files Changed

  • gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
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);
+      }
     }
   }
 
Loading diff…

Original Bug Report

reported by vm...@google.com

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.

  1. Establish a GLES2 passthrough command buffer from a compromised renderer.
  2. Force a context loss event so MarkContextLost() runs and sets context_lost_ = true on the decoder.
  3. The CommandBufferStub destructor runs, calling Destroy(have_context = false) which skips clearing the ANGLE blob cache callback. The GLES2DecoderPassthroughImpl instance is deleted, but the ANGLE context and its registered callbacks are kept alive via IOSurfaceBackingEGLState.
  4. When IOSurfaceBackingEGLState is subsequently destroyed, its destructor attempts context restoration via ui::ScopedMakeCurrent. If this fails, the resurrected context with stale callbacks remains thread-current.
  5. Trigger subsequent shader compilation or program linking operations on the thread. This causes ANGLE to query or write to its BlobCache, executing PassthroughGLBlobCacheGetCallback or PassthroughGLBlobCacheSetCallback with the stale user_param pointer, 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.

View on issue tracker