CVE-2026-7942
Overview
Background
- ANGLE `TextureGL`
- ANGLE’s OpenGL-backend texture implementation, which on some paths must supply a zero-filled staging buffer when initializing texture image data.
- `getZeroFilledBuffer`
- a context helper that returns a zero-initialized
angle::MemoryBufferof a requested byte size, used here to back un-specified texture data. - `sourceArea.width` / `sourceArea.height` / `pixelBytes`
- the dimensions and bytes-per-pixel whose product is the number of bytes needed for the zero-filled buffer.
- `angle::CheckedNumeric` / `ANGLE_CHECK_GL_MATH`
- ANGLE’s overflow-checked arithmetic wrapper and the macro that raises a GL error (rather than proceeding) when a checked computation is invalid.
Root Cause Analysis
In the texture-initialization path (enclosing function name not shown in the @@ -744 hunk header), the byte size for the zero-filled buffer was computed as the raw expression sourceArea.width * sourceArea.height * pixelBytes. These are integer operands, so a sufficiently large width/height/pixelBytes combination could overflow the multiplication and yield a truncated, too-small size that was then passed to getZeroFilledBuffer. The commit message states this is a speculative fix for a potential overflow, and the added TODO (Validate if this CheckedNumeric is required) confirms the authors had not confirmed a concrete reachable overflow.
The fix computes the size with angle::base::CheckMul(angle::base::CheckMul(sourceArea.width, sourceArea.height), pixelBytes) into a CheckedNumeric<size_t> and gates it with ANGLE_CHECK_GL_MATH(contextGL, checkedBufferSize.IsValid()), so an overflowing product now raises a GL math error before any allocation instead of silently wrapping; checkedBufferSize.ValueOrDie() then supplies the validated size. This converts a possibly-truncated size into a checked value, preventing an undersized zero buffer from being created.
width * height * pixelBytes size multiplication in unchecked integer arithmetic, which could overflow to a small value; the fix routes the computation through CheckedNumeric/CheckMul and fails via ANGLE_CHECK_GL_MATH when the product is not representable.Attack Path
- Reach the init path
A client (e.g. via WebGL in a renderer) issues a texture operation that takes ANGLE’s GL backend path requiring a zero-filled staging buffer sized from
sourceAreaandpixelBytes. - Supply overflowing dimensions
Width/height and
pixelBytesare chosen so their product exceeds the integer type’s range, causingsourceArea.width * sourceArea.height * pixelBytesto wrap to a smaller value. - Undersized buffer requested
Pre-patch, the truncated size was passed to
getZeroFilledBuffer, yielding a buffer smaller than the texture actually needs. - Potential downstream undersized-buffer use (inferred) A subsequent copy/upload using the real (larger) dimensions against the too-small zero buffer could read/write out of bounds; the diff does not demonstrate this sink, and the commit itself calls the fix speculative, so this step is (inferred).
Impact Assessment
Files Changed
src/libANGLE/renderer/gl/TextureGL.cpp
Audit Directions
- Unchecked size multiplicationsSearch ANGLE (and other GPU code) for
width * height * bytesPerPixel-style products feeding allocation or buffer-size arguments and confirm each usesCheckedNumeric/CheckMulwith a validity check. - Allocation size vs. later useAudit paths where a size is computed once for allocation but a separate (unwrapped) computation drives the subsequent copy, since divergence between the two is where overflow bites.
- Speculative/TODO-guarded checksRevisit fixes marked speculative (as here) to determine whether the overflow is actually reachable and whether the same pattern recurs in sibling
Tex[Sub]Image/CopyTexImageinit paths.
Patch
From 6edf0098f7375d243fb8cf9574285aab221a3253 Mon Sep 17 00:00:00 2001 From: Stephen Nusko <[email protected]> Date: Tue, 24 Mar 2026 02:15:52 -0700 Subject: [PATCH] Use CheckedNumeric for size calculation in TextureGL. Wrap the multiplication of width, height, and pixelBytes with angle::CheckedNumeric to prevent potential integer overflow when calculating the size for the zero-filled buffer. This is speculative fix for the potential bug linked below. See (sorry internal only): go/code-terracotta-reviewer-explainer for details. Bug:b/495363705 Change-Id: I3651b86f06d6acf9cf048c06c4acaeb6ba8ce2ac Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7696452 Commit-Queue: Geoff Lang <[email protected]> Auto-Submit: Stephen Nusko <[email protected]> Reviewed-by: Geoff Lang <[email protected]> Reviewed-by: Shahbaz Youssefi <[email protected]> --- diff --git a/src/libANGLE/renderer/gl/TextureGL.cpp b/src/libANGLE/renderer/gl/TextureGL.cpp index 05299c9..c91c6ac 100644 --- a/src/libANGLE/renderer/gl/TextureGL.cpp +++ b/src/libANGLE/renderer/gl/TextureGL.cpp @@ -744,10 +744,14 @@ const gl::InternalFormat &initFormatInfo = gl::GetInternalFormatInfo(initTexImageFormat.format, initTexImageFormat.type); GLuint pixelBytes = initFormatInfo.pixelBytes; + // TODO(b/495363705): Validate if this CheckedNumeric is required, remove either this TODO + // or the CheckedNumeric based on the result. + angle::CheckedNumeric<size_t> checkedBufferSize = angle::base::CheckMul( + angle::base::CheckMul(sourceArea.width, sourceArea.height), pixelBytes); + ANGLE_CHECK_GL_MATH(contextGL, checkedBufferSize.IsValid()); angle::MemoryBuffer *zero; - ANGLE_CHECK_GL_ALLOC( - contextGL, - context->getZeroFilledBuffer(sourceArea.width * sourceArea.height * pixelBytes, &zero)); + ANGLE_CHECK_GL_ALLOC(contextGL, + context->getZeroFilledBuffer(checkedBufferSize.ValueOrDie(), &zero)); gl::PixelUnpackState unpack; unpack.alignment = 1;
Original Bug Report
Potential Information Leak in ANGLE GL backend via integer overflow in robust resource initialization
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: An integer overflow in ANGLE’s GL backend during robust resource initialization results in a 0-byte scratch buffer allocation. This causes texImage2D to be called with a null pointer, silently bypassing initialization and leaving up to 4GB of VRAM uninitialized, leading to a potential cross-origin information leak.
Affected files:
third_party/angle/src/libANGLE/renderer/d3d/d3d11/Image11.cppthird_party/angle/src/libANGLE/renderer/gl/TextureGL.cpp
Estimated timestamp from git blame: 2018-11-15
Summary
A potential Information Leak vulnerability exists in ANGLE’s GL backend when handling glCopyTexImage2D calls in WebGL. When copying a small framebuffer into a large, uninitialized texture, WebGL mandates robust resource initialization to zero-out the unwritten areas. However, a 32-bit integer overflow during the calculation of the zero-fill buffer size can cause the initialization to be silently bypassed. This allows a malicious WebGL page to allocate and read up to 4GB of uninitialized Video RAM (VRAM), which may contain sensitive cross-origin graphics data, desktop framebuffer contents, or rendered text.
Technical Details
In third_party/angle/src/libANGLE/renderer/gl/TextureGL.cpp, the TextureGL::copyImage method handles copying from a framebuffer to a texture. If the copy area extends outside the bounds of the source framebuffer, ANGLE must initialize the texture to zero to comply with WebGL’s security requirements.
// TextureGL.cpp:746
GLuint pixelBytes = initFormatInfo.pixelBytes;
angle::MemoryBuffer *zero;
ANGLE_CHECK_GL_ALLOC(
contextGL,
context->getZeroFilledBuffer(sourceArea.width * sourceArea.height * pixelBytes, &zero));
The variables sourceArea.width and sourceArea.height are 32-bit integers, and pixelBytes is a 32-bit GLuint. The multiplication is performed using 32-bit arithmetic.
If an attacker requests a texture size of 16384 x 16384 (the MAX_TEXTURE_SIZE on many modern GPUs) with a 16-byte-per-pixel format like GL_RGBA32UI, the calculation is 16384 * 16384 * 16. This precisely equals 4,294,967,296 (2^32). Due to 32-bit integer overflow, the result wraps around to exactly 0.
Context::getZeroFilledBuffer(0) then successfully returns an angle::MemoryBuffer with a capacity of 0 and a data() pointer of nullptr.
// TextureGL.cpp:757
ANGLE_GL_TRY_ALWAYS_CHECK(
context, functions->texImage2D(..., sourceArea.width,
sourceArea.height, 0, ...
zero->data()));
ANGLE subsequently calls the native GL driver’s texImage2D with zero->data() (which is nullptr). According to the OpenGL specification, passing nullptr to texImage2D (when no pixel unpack buffer is bound) allocates the VRAM but does not initialize it.
Consequently, the underlying GPU driver allocates 4GB of VRAM without zeroing it out. The subsequent partial copy (glCopyTexSubImage2D) only overwrites the small valid region, leaving the vast majority of the 4GB texture filled with uninitialized VRAM, completely bypassing the WebGL sandbox protections.
Note: A similar overflow calculation exists in the D3D11 backend (Image11.cpp), but it is protected by a if (loadFunction.requiresConversion) check. Formats with pixelBytes == 16 natively map to DXGI formats and do not require conversion, rendering the D3D11 path unreachable for this specific overflow.
Suggested Steps to Reproduce
Note: Our setup cannot run code. These are potential steps an attacker might use to trigger the vulnerability.
- Initialize a WebGL 2.0 context.
- Verify
gl.getParameter(gl.MAX_TEXTURE_SIZE)is at least16384. - Create a
WebGLRenderbufferwith thegl.RGBA32UIformat, sized1x1. - Create a
WebGLFramebuffer, attach the renderbuffer, and bind it togl.READ_FRAMEBUFFER. - Create a new, uninitialized
WebGLTextureand bind it togl.TEXTURE_2D. - Call
gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA32UI, 0, 0, 16384, 16384, 0). The 32-bit size calculation overflows, and the texture is backed by uninitialized VRAM. - Use
gl.readPixels()or draw the texture to a canvas to harvest the uninitialized VRAM contents, achieving an Information Leak.
Evaluated with Chrome root at commit: 9760e6c70cd33a320713361f17c6dcca85648c0f
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. Please feel free to reach out to me if you have concerns or feedback.