High chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized Use in ANGLE
DescriptionUninitialized Use in ANGLE
ComponentANGLE
Bug ClassUninitialized Memory
Tracker513051340
Fix commit2642c51bbee9 (angle/angle) +29/-16
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Files Changed

  • src/libANGLE/renderer/d3d/d3d11/Blit11.cpp
From 2642c51bbee9e1851e8c245466cfae2be6fe00dc Mon Sep 17 00:00:00 2001
From: Geoff Lang <[email protected]>
Date: Fri, 15 May 2026 09:52:50 -0400
Subject: [PATCH] D3D11: Avoid partial init state in Blit11.

If a resource allocation fails, Blit11 would be left in a partially
initialized state. Allocate to new stack variables and assign them to
members at the end.

Fixed: chromium:513051340
Change-Id: I197715bad3db9c6eda9d8fd2cb7593450ba5e413
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7850285
Commit-Queue: Shahbaz Youssefi <[email protected]>
Auto-Submit: Geoff Lang <[email protected]>
Reviewed-by: Shahbaz Youssefi <[email protected]>
---

diff --git a/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp b/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp
index 98bf6b2..b0f12f9 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp
@@ -1774,8 +1774,9 @@
 
     Context11 *context11 = GetImplAs<Context11>(context);
 
-    ANGLE_TRY(mRenderer->allocateTexture(context11, textureDesc, format, &mResolvedDepth));
-    mResolvedDepth.setInternalName("Blit11::mResolvedDepth");
+    TextureHelper11 newResolvedDepth;
+    ANGLE_TRY(mRenderer->allocateTexture(context11, textureDesc, format, &newResolvedDepth));
+    newResolvedDepth.setInternalName("Blit11::mResolvedDepth");
 
     D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
     dsvDesc.Flags              = 0;
@@ -1783,14 +1784,20 @@
     dsvDesc.Texture2D.MipSlice = 0;
     dsvDesc.ViewDimension      = D3D11_DSV_DIMENSION_TEXTURE2D;
 
-    ANGLE_TRY(mRenderer->allocateResource(context11, dsvDesc, mResolvedDepth.get(),
-                                          &mResolvedDepthDSView));
-    mResolvedDepthDSView.setInternalName("Blit11::mResolvedDepthDSView");
+    d3d11::DepthStencilView newResolvedDepthDSView;
+    ANGLE_TRY(mRenderer->allocateResource(context11, dsvDesc, newResolvedDepth.get(),
+                                          &newResolvedDepthDSView));
+    newResolvedDepthDSView.setInternalName("Blit11::mResolvedDepthDSView");
 
     // Possibly D3D11 bug or undefined behaviour: Clear the DSV so that our first render
     // works as expected. Otherwise the results of the first use seem to be incorrect.
     ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
-    deviceContext->ClearDepthStencilView(mResolvedDepthDSView.get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
+    deviceContext->ClearDepthStencilView(newResolvedDepthDSView.get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
+
+    // Only assign after the allocations have fully succeeded to avoid a partially initialized
+    // state.
+    mResolvedDepth       = std::move(newResolvedDepth);
+    mResolvedDepthDSView = std::move(newResolvedDepthDSView);
 
     return angle::Result::Continue;
 }
@@ -1805,11 +1812,6 @@
         return angle::Result::Continue;
     }
 
-    if (mResolvedDepthStencil.valid())
-    {
-        releaseResolveDepthStencilResources();
-    }
-
     const auto &formatSet = d3d11::Format::Get(GL_RG32F, mRenderer->getRenderer11DeviceCaps());
 
     D3D11_TEXTURE2D_DESC textureDesc;
@@ -1827,13 +1829,24 @@
 
     Context11 *context11 = GetImplAs<Context11>(context);
 
+    TextureHelper11 newResolvedDepthStencil;
     ANGLE_TRY(
-        mRenderer->allocateTexture(context11, textureDesc, formatSet, &mResolvedDepthStencil));
-    mResolvedDepthStencil.setInternalName("Blit11::mResolvedDepthStencil");
+        mRenderer->allocateTexture(context11, textureDesc, formatSet, &newResolvedDepthStencil));
+    newResolvedDepthStencil.setInternalName("Blit11::mResolvedDepthStencil");
 
-    ANGLE_TRY(mRenderer->allocateResourceNoDesc(context11, mResolvedDepthStencil.get(),
-                                                &mResolvedDepthStencilRTView));
-    mResolvedDepthStencilRTView.setInternalName("Blit11::mResolvedDepthStencilRTView");
+    d3d11::RenderTargetView newResolvedDepthStencilRTView;
+    ANGLE_TRY(mRenderer->allocateResourceNoDesc(context11, newResolvedDepthStencil.get(),
+                                                &newResolvedDepthStencilRTView));
+    newResolvedDepthStencilRTView.setInternalName("Blit11::mResolvedDepthStencilRTView");
+
+    // Only assign after the allocations have fully succeeded to avoid a partially initialized
+    // state.
+    if (mResolvedDepthStencil.valid())
+    {
+        releaseResolveDepthStencilResources();
+    }
+    mResolvedDepthStencil       = std::move(newResolvedDepthStencil);
+    mResolvedDepthStencilRTView = std::move(newResolvedDepthStencilRTView);
 
     return angle::Result::Continue;
 }
Loading diff…

Original Bug Report

reported by [email protected]

Potential GPU memory disclosure in ANGLE D3D11 due to Blit11 cache desynchronization

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 flaw in ANGLE’s D3D11 backend allows for the desynchronization of cached depth resolve resources when a resource allocation fails. This can lead to a WebGL context receiving an uninitialized texture containing stale GPU memory instead of the results of a resolve operation.

Affected files:

  • third_party/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp
  • third_party/angle/src/libANGLE/renderer/d3d/d3d11/ResourceManager11.cpp
  • third_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp

Estimated timestamp from git blame: 2017-05-18

Background

In the ANGLE D3D11 renderer, Blit11 manages internal resources for blit and resolve operations. To optimize performance, it maintains a renderer-scoped cache for depth-only resolve operations, consisting of a texture (mResolvedDepth) and a DepthStencilView (mResolvedDepthDSView). Because Blit11 is owned by the shared Renderer11, these cached resources are shared across all WebGL contexts (and therefore across different origins) using the same D3D11 device.

The Vulnerability

The function Blit11::initResolveDepthOnly manages these cached resources in a fallible two-step process without sufficient state rollback. When the requested dimensions change, it reallocates the texture and then the view:

// third_party/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp

// Step 1: Reallocate texture. If successful, mResolvedDepth is updated.
ANGLE_TRY(mRenderer->allocateTexture(context11, textureDesc, format, &mResolvedDepth));

// Step 2: Reallocate DSV. If this fails, mResolvedDepthDSView is NOT updated.
ANGLE_TRY(mRenderer->allocateResource(context11, dsvDesc, mResolvedDepth.get(),
                                      &mResolvedDepthDSView));

// Step 3: Clear the texture. This is only reached if Step 2 succeeds.
deviceContext->ClearDepthStencilView(mResolvedDepthDSView.get(), D3D11_CLEAR_DEPTH, 1.0f, 0);

If Step 1 succeeds but Step 2 fails (e.g., due to E_OUTOFMEMORY), the function returns an error, but the Blit11 object is left in an inconsistent state: mResolvedDepth points to the newly allocated texture, while mResolvedDepthDSView still points to the view for the previous texture size.

Because production builds typically do not zero-initialize newly allocated textures (unless specific debug flags are set), the new texture contains uninitialized GPU memory.

Potential Exploitation Path

  1. Attacker Context A triggers a multisample depth resolve for size $X$. The cache is populated correctly.
  2. Attacker Context A triggers a resolve for size $Y$ while the GPU is under memory pressure. Step 1 succeeds (updating mResolvedDepth to size $Y$), but Step 2 fails. mResolvedDepthDSView still points to a view of size $X$.
  3. Attacker Context B (or the same attacker) triggers a resolve for size $Y$. initResolveDepthOnly sees a cache hit (since mResolvedDepth is size $Y$) and returns Continue early.
  4. Blit11::resolveDepth proceeds to use the stale mResolvedDepthDSView. The GPU resolve operation writes into the old texture (size $X$) because that is what the stale view targets.
  5. The function returns mResolvedDepth (size $Y$), which remains uninitialized and contains stale GPU memory from the heap.
  6. The attacker reads back this texture using standard WebGL commands (e.g., glReadPixels), potentially disclosing sensitive data from other origins or processes.

Suggested Fix

The initResolveDepthOnly function should ensure that dependent views are reset before attempting to reallocate the underlying texture, or implement a proper rollback mechanism. A similar hardening was previously applied to the sibling function initResolveDepthStencil, but appears to have been omitted here.

 if (mResolvedDepth.valid())
 {
     mResolvedDepthDSView.reset();
     mResolvedDepth.reset();
 }

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.

View on issue tracker