CVE-2026-10913
Overview
Files Changed
src/libANGLE/renderer/d3d/d3d11/Image11.cpp
Patch
From 0bcb689c7059e6c02d78905bd29c863eeeff60ef Mon Sep 17 00:00:00 2001 From: Geoff Lang <[email protected]> Date: Fri, 01 May 2026 12:15:55 -0400 Subject: [PATCH] D3D11: Disassociate storage before recovering image data. If staging texture creation fails due to OOM or context loss, Image11::mAssociatedStorage is left pointing to a potentially deleted storage. Instead, call disassociateStorage early to clean up the references. No test since this requires injecting a driver-level error and forcing a context loss. Fixed: chromium:497450927 Change-Id: I631c43289ea955ff713b19a0fba6e6296a63f5a7 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7807740 Commit-Queue: Shahbaz Youssefi <[email protected]> Reviewed-by: Shahbaz Youssefi <[email protected]> --- diff --git a/src/libANGLE/renderer/d3d/d3d11/Image11.cpp b/src/libANGLE/renderer/d3d/d3d11/Image11.cpp index c0e8361..843ece4 100644 --- a/src/libANGLE/renderer/d3d/d3d11/Image11.cpp +++ b/src/libANGLE/renderer/d3d/d3d11/Image11.cpp @@ -193,14 +193,17 @@ { if (mRecoverFromStorage) { - ANGLE_TRY(createStagingTexture(context)); + TextureStorage11 *storage = mAssociatedStorage; + gl::ImageIndex imageIndex = mAssociatedImageIndex; + storage->verifyAssociatedImageValid(imageIndex, this); + disassociateStorage(); - mAssociatedStorage->verifyAssociatedImageValid(mAssociatedImageIndex, this); + ANGLE_TRY(createStagingTexture(context)); // CopySubResource from the Storage to the Staging texture gl::Box region(0, 0, 0, mWidth, mHeight, mDepth); - ANGLE_TRY(mAssociatedStorage->copySubresourceLevel( - context, mStagingTexture, mStagingSubresource, mAssociatedImageIndex, region)); + ANGLE_TRY(storage->copySubresourceLevel(context, mStagingTexture, mStagingSubresource, + imageIndex, region)); mRecoveredFromStorageCount += 1; // Reset all the recovery parameters, even if the texture storage association is broken.
Original Bug Report
Potential UAF in ANGLE D3D11 Image11 via unhandled error in recoverFromAssociatedStorage
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A potential Use-After-Free (UAF) vulnerability exists in ANGLE’s D3D11 backend due to improper error handling during texture storage release. If an allocation error occurs while recovering image data, an early return skips disassociating the image before the storage is unconditionally deleted. When the image is later destroyed, it makes a virtual function call using the dangling pointer, which can lead to arbitrary code execution in the GPU process.
Affected files:
third_party/angle/src/libANGLE/renderer/d3d/d3d11/Image11.cppthird_party/angle/src/libANGLE/renderer/d3d/TextureD3D.cppthird_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cppthird_party/angle/src/libANGLE/renderer/d3d/d3d11/Image11.hthird_party/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.h
Estimated timestamp from git blame: 2018-07-25
Vulnerability Details
A potential Use-After-Free vulnerability has been identified in the ANGLE D3D11 renderer involving the bidirectional association between Image11 and TextureStorage11.
When a texture is redefined, TextureD3D::releaseTexStorage is called. This function first gives the storage a chance to gracefully recover data into local images by calling mTexStorage->onDestroy(context), and then unconditionally frees the storage using SafeDelete(mTexStorage) (TextureD3D.cpp:862).
Inside TextureStorage11_2D::onDestroy, it iterates through its associated Image11 objects and calls ANGLE_TRY(mAssociatedImages[i]->recoverFromAssociatedStorage(context)) (TextureStorage11.cpp:1102).
The flaw occurs in Image11::recoverFromAssociatedStorage (Image11.cpp:192). If this function encounters an error (for example, E_OUTOFMEMORY when attempting to allocate a staging texture via createStagingTexture), the ANGLE_TRY macro causes an immediate early return.
Crucially, this early return skips the disassociateStorage() call on line 207. The Image11 object is left in a corrupted state: its mRecoverFromStorage flag remains true, and its mAssociatedStorage member holds a raw, dangling pointer to the TextureStorage11 object. Because TextureD3D::releaseTexStorage unconditionally calls SafeDelete, the storage object is freed.
Later, when the Image11 object is destroyed (e.g., during context teardown), its destructor calls disassociateStorage(). Because mRecoverFromStorage is still true, it attempts to execute mAssociatedStorage->disassociateImage(...) (Image11.cpp:220). Since disassociateImage is a pure virtual function, this results in a virtual method call on freed memory.
Exploitability:
ANGLE uses native C++ raw pointers (T*), meaning these objects are not protected by Chromium’s MiraclePtr (BackupRefPtr) mitigation. An attacker who can exhaust GPU memory to trigger the early return can subsequently use a secondary WebGL context to spray the heap, reclaim the freed TextureStorage11 memory, and control the vtable pointer to achieve arbitrary Remote Code Execution (RCE) in the sandboxed GPU process.
Potential Attacker Steps
Note: These are suggested steps based on source code analysis; our tooling has not yet run a live proof-of-concept.
- Initialize: Create two WebGL contexts: Context A (victim) and Context B (spray/memory pressure).
- Prime State: In Context A, create a texture and draw with it to allocate a
TextureStorage11_2Dand link it to anImage11(settingmRecoverFromStorage = true). - Induce Failure: Use Context B to allocate massive amounts of memory, pushing the GPU into an Out-Of-Memory state.
- Trigger UAF: In Context A, redefine the texture dimensions via
gl.texImage2D. This callsreleaseTexStorage. The OOM condition causesrecoverFromAssociatedStorageto abort early viaANGLE_TRY. TheTextureStorage11_2Dis freed, leaving a dangling pointer inImage11. - Reclaim Memory: Use Context B to spray the heap with objects matching the size of
TextureStorage11_2D, writing a controlled fake vtable pointer at the beginning of the reclaimed memory block. - Execute: Delete the texture or destroy Context A. The
Image11destructor invokesdisassociateStorage(), which makes a virtual call via the attacker-controlled vtable, hijacking control flow.
Suggested Fix
Ensure that the association between Image11 and TextureStorage11 is reliably broken even if data recovery fails.
One approach is to modify Image11::recoverFromAssociatedStorage to use an RAII scope guard (or a try/catch-like pattern using standard boolean checks instead of ANGLE_TRY for the allocations) so that disassociateStorage() is guaranteed to execute regardless of whether the staging texture allocation succeeds or fails.
Alternatively, TextureStorage11::onDestroy or the TextureStorage11 destructor could explicitly iterate through mAssociatedImages and manually clear their mAssociatedStorage pointers before the object is deleted.
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.