Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in ANGLE
DescriptionUse after free in ANGLE
ComponentANGLE
Bug ClassUAF
Tracker497574371
Fix commitcad8fd6da0f0 (angle/angle) +1/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Files Changed

  • src/libANGLE/renderer/d3d/d3d11/Buffer11.h
From cad8fd6da0f03b667943e12939e6ea4669b09868 Mon Sep 17 00:00:00 2001
From: Geoff Lang <geofflang@chromium.org>
Date: Wed, 15 Apr 2026 14:40:20 -0400
Subject: [PATCH] D3D11: Use uint64_t for buffer storage LRU count

The 32-bit counter has a chance of wrapping around with intentional
churn of the cache. Increase this to 64 bits which should be an
infeasible number of allocations.

Fixed: chromium:497574371
Change-Id: If8e50445515fe1a0231ca4c57644614a49e811c1
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7765257
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
---

diff --git a/src/libANGLE/renderer/d3d/d3d11/Buffer11.h b/src/libANGLE/renderer/d3d/d3d11/Buffer11.h
index 620f96f..4055a42 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Buffer11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/Buffer11.h
@@ -152,7 +152,7 @@
         BufferCacheEntry() : storage(nullptr), lruCount(0) {}
 
         BufferStorage *storage;
-        unsigned int lruCount;
+        uint64_t lruCount;
     };
 
     struct StructuredBufferKey
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential Use-After-Free in ANGLE Buffer11 due to LRU counter wraparound

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: A 32-bit LRU counter in ANGLE’s D3D11 Buffer11 cache can wrap around after 2^32 operations, causing newly created buffer storage objects to be incorrectly identified as the least recently used. This leads to the new buffer being synchronously deleted and immediately accessed, resulting in a Use-After-Free (UAF) and a reliable GPU process crash.

Affected files:

  • third_party/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.cpp
  • third_party/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h

Estimated timestamp from git blame: 2019-12-20

Description

There is a potential Use-After-Free (UAF) vulnerability in the ANGLE D3D11 backend’s Buffer11 implementation. The functions getStructuredBufferRangeSRV and getConstantBufferRangeStorage in Buffer11.cpp maintain caches of storage objects using a Least Recently Used (LRU) policy. To track usage, they rely on 32-bit unsigned integer counters (mMaxStructuredBufferLruCount and mMaxConstantBufferLruCount).

When a cache’s memory budget is exceeded, an eviction loop uses std::min_element to find and delete the entry with the smallest lruCount.

Because the counter is 32-bit, it can wrap around to 0 after 2^32 operations. When an attacker intentionally wraps this counter and then allocates a new buffer that triggers an eviction, the newly allocated buffer is given a very small lruCount (e.g., 1). The std::min_element logic incorrectly identifies this newly created buffer as the “least recently used” because its wrapped lruCount is smaller than the lruCount of older entries (which might be near 0xFFFFFFFF).

An assertion intended to prevent self-eviction (ASSERT(iter->second.storage != newStorage)) is compiled out in production Release builds. Consequently, SafeDelete is called on the newly created object, freeing it. The code then synchronously continues to use the dangling pointer (e.g., calling resizeStructuredBuffer or updateBufferStorage), resulting in a UAF.

Potential Steps to Trigger

Note: These are suggested steps based on static analysis, as our tooling cannot execute dynamic Proof-of-Concepts.

  1. Setup: An attacker hosts a malicious WebGL 2 page.
  2. Counter Wraparound: The JavaScript executes a tight loop of WebGL draw calls that bind structured or constant buffers. This is repeated exactly 2^32 times to silently overflow the 32-bit unsigned lruCount back to 0.
  3. Eviction Trigger: The attacker then binds a new, previously unseen buffer range, requesting a size large enough to exceed the cache’s maximum allowed memory budget (2 * getSize()).
  4. Flawed Eviction: Buffer11 allocates the new StructuredBufferStorage object and assigns it a wrapped, small lruCount (e.g., 1). The cache eviction loop triggers and std::min_element selects this newly created object for deletion.
  5. Synchronous UAF: SafeDelete frees the object. Immediately after, the code calls structuredBufferStorage->resizeStructuredBuffer(...) on the dangling stack pointer.

Impact

Because the freed memory is accessed synchronously within the exact same C++ function block, an attacker cannot pause execution to interleave JavaScript and spray the GPU process heap. Therefore, this UAF cannot be trivially exploited for Remote Code Execution (RCE).

Instead, accessing the freed memory immediately triggers a crash. If PartitionAlloc’s BackupRefPtr (MiraclePtr) or quarantine is enabled, the poisoned memory will safely cause an access violation. Otherwise, it will cause a double-free or null pointer dereference when the inner smart pointers (rx::Resource11) attempt to release their inner COM objects. This results in a reliable Denial of Service (DoS) of the GPU process.

Suggested Fix

  1. Increase Counter Width: Change the types of mMaxStructuredBufferLruCount and mMaxConstantBufferLruCount in Buffer11.h from unsigned int to uint64_t. A 64-bit counter will realistically never overflow during a single browser session.
  2. Robust Eviction Check: Replace the ASSERT with a hard if check to ensure the newly created newStorage is never considered for eviction, or use a proper LRU data structure (like base::LRUCache) instead of manual counter tracking.

Evaluated with Chrome root at commit: a9cbf6e8b275fe4147435aa905f3b7f5a656f5f0


Results from so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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