CVE-2026-11268
Overview
Files Changed
src/libANGLE/renderer/d3d/d3d11/Renderer11.cppsrc/libANGLE/renderer/d3d/d3d11/Renderer11.hsrc/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
Patch
From 16c75333ba21d707e254ad7add7bbb2234d24438 Mon Sep 17 00:00:00 2001
From: Geoff Lang <geofflang@chromium.org>
Date: Fri, 17 Apr 2026 16:58:41 -0400
Subject: [PATCH] D3D11: Remove mScratchMemory buffer.
Bug: chromium:500528706
Change-Id: Ie61d194e5b753454cc3d19ab27850e5cb845eeed
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7770826
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
---
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
index a24fbc5..5c8c2dd 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -356,8 +356,6 @@
return executable->getExecutable()->getNumViews() * instanceCount;
}
-const uint32_t ScratchMemoryBufferLifetime = 1000;
-
void PopulateFormatDeviceCaps(ID3D11Device *device,
DXGI_FORMAT format,
UINT *outSupport,
@@ -418,8 +416,7 @@
mCreateDebugDevice(false),
mStateCache(),
mStateManager(this),
- mDebug(nullptr),
- mScratchMemoryBuffer(ScratchMemoryBufferLifetime)
+ mDebug(nullptr)
{
mLineLoopIB = nullptr;
mTriangleFanIB = nullptr;
@@ -2289,8 +2286,6 @@
void Renderer11::release()
{
- mScratchMemoryBuffer.clear();
-
mAnnotatorContext.release();
gl::UninitializeDebugAnnotations();
@@ -4175,14 +4170,6 @@
return new Framebuffer11(state, this);
}
-angle::Result Renderer11::getScratchMemoryBuffer(Context11 *context11,
- size_t requestedSize,
- angle::MemoryBuffer **bufferOut)
-{
- ANGLE_CHECK_GL_ALLOC(context11, mScratchMemoryBuffer.get(requestedSize, bufferOut));
- return angle::Result::Continue;
-}
-
gl::Version Renderer11::getMaxSupportedESVersion() const
{
return d3d11_gl::GetMaximumClientVersion(mRenderer11DeviceCaps);
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
index af61629..484b005 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
@@ -413,10 +413,6 @@
// Necessary hack for default framebuffers in D3D.
FramebufferImpl *createDefaultFramebuffer(const gl::FramebufferState &state) override;
- angle::Result getScratchMemoryBuffer(Context11 *context11,
- size_t requestedSize,
- angle::MemoryBuffer **bufferOut);
-
gl::Version getMaxSupportedESVersion() const override;
gl::Version getMaxConformantESVersion() const override;
@@ -613,8 +609,6 @@
std::vector<GLuint> mScratchIndexDataBuffer;
- angle::ScratchBuffer mScratchMemoryBuffer;
-
DebugAnnotatorContext11 mAnnotatorContext;
mutable Optional<bool> mSupportsShareHandles;
diff --git a/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp b/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
index 2088634..05e52ee 100644
--- a/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
@@ -930,8 +930,9 @@
bufferRowPitch = checkedBufferRowPitch.ValueOrDie<UINT>();
bufferDepthPitch = checkedBufferDepthPitch.ValueOrDie<UINT>();
- ANGLE_TRY(mRenderer->getScratchMemoryBuffer(
- context11, checkedNeededSize.ValueOrDie<size_t>(), &conversionBuffer));
+ ANGLE_CHECK_GL_ALLOC(
+ context11,
+ context->getScratchBuffer(checkedNeededSize.ValueOrDie<size_t>(), &conversionBuffer));
loadFunctionInfo.loadFunction(mRenderer->getDisplay()->getImageLoadContext(), width, height,
depth, pixelData + srcSkipBytes, srcRowPitch, srcDepthPitch,
conversionBuffer->data(), bufferRowPitch, bufferDepthPitch);
Original Bug Report
Potential Information leak in ANGLE D3D11 Image11::copyFromFramebuffer
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 security team.
Overview: An attacker can potentially leak cross-origin WebGL texture data or uninitialized GPU heap memory on Windows via the ANGLE D3D11 backend. By calling glCopyTexSubImage2D with out-of-bounds coordinates, a shared scratch buffer is left unmodified during a read operation but is still copied into an attacker-controlled texture. This allows the attacker to read stale data originating from other contexts.
Affected files:
third_party/angle/src/libANGLE/renderer/d3d/d3d11/Image11.cppthird_party/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
Estimated timestamp from git blame: 2022-10-19
Description
A potential information leak exists in the ANGLE D3D11 backend during WebGL texture copy operations.
When glCopyTexSubImage2D is called and format conversion is required, Image11::copyFromFramebuffer (in third_party/angle/src/libANGLE/renderer/d3d/d3d11/Image11.cpp) retrieves a shared scratch buffer (Renderer11::mScratchMemoryBuffer) to temporarily hold the intermediate pixel data. It then calls Renderer11::readFromAttachment to populate this buffer from the source framebuffer.
If an attacker provides source coordinates (x, y) that are entirely out-of-bounds of the source framebuffer, readFromAttachment mathematically clamps the read region to zero dimensions. Because the read dimensions are zero, the function immediately returns angle::Result::Continue without modifying the output scratch buffer.
Image11::copyFromFramebuffer receives the Continue result and blindly proceeds to execute the pixel conversion loadFunction, assuming the read populated the buffer. The conversion function reads width * height pixels from the unmodified scratch buffer and writes them into the attacker’s destination texture.
Since the scratch buffer is shared across all WebGL contexts on the D3D11 device and uses malloc without zero-initialization upon resizing, this flaw copies either uninitialized GPU heap memory or stale cross-origin texture data (from recent conversions) into the attacker’s texture.
Potential Trigger Steps
Note: These are suggested steps to trigger the vulnerability based on static code analysis. Our tooling cannot currently execute code to verify the PoC.
- The attacker creates a WebGL context on a system using the D3D11 backend (default on Windows).
- A victim origin performs WebGL operations that populate the shared
Renderer11::mScratchMemoryBufferwith sensitive texture data. - The attacker creates a destination texture with a format that forces software conversion (e.g.,
GL_RGBA8). - The attacker creates a source framebuffer and attaches a texture with a format that cannot be directly copied via hardware (e.g.,
GL_LUMINANCE). This ensures the fallback conversion path inImage11::copyFromFramebufferis triggered. - The attacker calls
gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, x, y, width, height)withxandycoordinates that are completely outside the source framebuffer’s actual dimensions. - The attacker reads back the destination texture using
gl.readPixelsor by drawing it to an HTML canvas, extracting the leaked cross-origin data or uninitialized heap memory.
Suggested Fix
There are a few ways to resolve this issue to conform to OpenGL semantics (which require out-of-bounds reads to return zeroed pixels) and prevent memory leaks:
- Validate and Zero-fill Read Dimensions: In
Image11::copyFromFramebuffer, determine the actual valid region that was read from the framebuffer. If the requested area is out-of-bounds, the scratch buffer (or the out-of-bounds regions of the destination texture) should be explicitly zero-filled before or after theloadFunctionexecutes. - Explicitly handle Zero-Dimension Reads: If
readFromAttachmentcalculates asafeAreaof 0,Image11::copyFromFramebuffershould bypass theloadFunctionread entirely and directly zero-out the target region in the mapped destination texture. - Zero-initialize Scratch Memory: As a defense-in-depth measure,
ScratchBuffer::getshould zero-initialize newly allocated heap memory to prevent leaking uninitialized PartitionAlloc/system memory, though this alone would not prevent leaking stale data from previous operations.
Evaluated with Chrome root at commit: 137d451a126685dd5010e6609db9f6d4a78d8234
Results 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.