Medium chrome Integer Overflow 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInteger overflow in ANGLE
DescriptionInteger overflow in ANGLE
ComponentANGLE
Bug ClassInteger Overflow
Tracker495363705
Fix commit6edf0098f737 (angle/angle) +7/-3
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-05

Files Changed

  • src/libANGLE/renderer/gl/TextureGL.cpp
From 6edf0098f7375d243fb8cf9574285aab221a3253 Mon Sep 17 00:00:00 2001
From: Stephen Nusko <nuskos@chromium.org>
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 <geofflang@chromium.org>
Auto-Submit: Stephen Nusko <nuskos@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
---

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 rj...@google.com

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