Medium watch CVSS 4.3 chrome Integer Overflow 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInteger overflow in ANGLE
DescriptionInteger overflow in ANGLE
ComponentANGLE
Bug ClassInteger Overflow
Action tierwatch — ub-class only — no demonstrated corruption path
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker495363705
Fix commit6edf0098f737 (angle/angle) +7/-3
CWECWE-472
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-03-23 (per vendor advisory)
Fix landed2026-03-26 (public git)
Disclosed2026-05-05
Patch gap40 days — fix public in source before the advisory

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::MemoryBuffer of 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.

Key insight
The core mistake was performing the 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

  1. 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 sourceArea and pixelBytes.
  2. Supply overflowing dimensions Width/height and pixelBytes are chosen so their product exceeds the integer type’s range, causing sourceArea.width * sourceArea.height * pixelBytes to wrap to a smaller value.
  3. Undersized buffer requested Pre-patch, the truncated size was passed to getZeroFilledBuffer, yielding a buffer smaller than the texture actually needs.
  4. 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

Within the GPU/ANGLE context (reachable from a renderer via GL/WebGL commands), a crafted texture size could overflow the buffer-size multiplication and produce an undersized zero-filled buffer; the advisory rates this medium (“Integer overflow in ANGLE”) with no exploited-in-the-wild claim. The patch demonstrates only the unchecked multiplication and its checked replacement — the commit explicitly describes it as a speculative fix — so any concrete out-of-bounds access or memory corruption is (inferred), not established by the diff. Preconditions: the attacker can drive the GL backend with controlled texture dimensions.

Files Changed

  • src/libANGLE/renderer/gl/TextureGL.cpp

Audit Directions

  • Unchecked size multiplications
    Search ANGLE (and other GPU code) for width * height * bytesPerPixel-style products feeding allocation or buffer-size arguments and confirm each uses CheckedNumeric/CheckMul with a validity check.
  • Allocation size vs. later use
    Audit 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 checks
    Revisit fixes marked speculative (as here) to determine whether the overflow is actually reachable and whether the same pattern recurs in sibling Tex[Sub]Image/CopyTexImage init paths.
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;
Loading diff…

Original Bug Report

reported by [email protected]

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.cpp
  • third_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.

  1. Initialize a WebGL 2.0 context.
  2. Verify gl.getParameter(gl.MAX_TEXTURE_SIZE) is at least 16384.
  3. Create a WebGLRenderbuffer with the gl.RGBA32UI format, sized 1x1.
  4. Create a WebGLFramebuffer, attach the renderbuffer, and bind it to gl.READ_FRAMEBUFFER.
  5. Create a new, uninitialized WebGLTexture and bind it to gl.TEXTURE_2D.
  6. 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.
  7. 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.

View on issue tracker