CVE-2026-13030
Overview
Files Changed
gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
Patch
From 92b7a3ad4869e940d79019996d0751f739e1a842 Mon Sep 17 00:00:00 2001 From: Brandon Jones <[email protected]> Date: Fri, 12 Jun 2026 10:00:56 -0700 Subject: [PATCH] Clear PIXEL_PACK_BUFFER in CopyTextureCHROMIUM One Android path in CopyTextureCHROMIUM uses a glReadPixels call to a client buffer, but doesn't clear the PIXEL_PACK_BUFFER before doing the read. If the page sets the PIXEL_PACK_BUFFER in a WebGL 2 context prior to this method being called it could interfere with the data being read, leading the client buffer to potentially contain uninitialized memory. This change ensures the PIXEL_PACK_BUFFER is always set to 0 before performing the client read. Fixed: 522840723 Change-Id: I95c3ad176a2759b490b0b36555c186489fe9153d Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7927868 Commit-Queue: Brandon Jones <[email protected]> Reviewed-by: Kenneth Russell <[email protected]> Cr-Commit-Position: refs/heads/main@{#1646051} --- diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc index 1c76f7eb..fda1e42 100644 --- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc +++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc @@ -716,6 +716,7 @@ // GLCopyTextureCHROMIUMES3Test.FormatCombinations in gl_tests. This is seen // on Nexus 5 but not Nexus 4. Read pixels to client memory, then upload to // pixel unpack buffer with glBufferData. + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); auto pixels = base::HeapArray<uint8_t>::Uninit(pixel_num * 4); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); auto data = base::HeapArray<float>::Uninit(pixel_num * 3);
Original Bug Report
Potential GPU Information Leak via uninitialized fallback buffer in CopyTextureCHROMIUM on Android
Flapjack, 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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: The Android software fallback path for CopyTextureCHROMIUM allocates an uninitialized heap array for reading pixels. By keeping a Pixel Buffer Object (PBO) bound to GL_PIXEL_PACK_BUFFER, an attacker can reliably cause the glReadPixels call to fail, leaving the array uninitialized. This stale GPU process heap memory is then uploaded to a texture, leading to a cross-origin information leak.
Affected files:
gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
Estimated timestamp from git blame: 2017-03-30
Overview
A vulnerability exists in the Android software fallback path of CopyTextureCHROMIUM that could allow a malicious webpage to leak uninitialized memory from the GPU process heap. By manipulating WebGL state, an attacker can force a readback failure, causing stale heap data to be uploaded to an attacker-controlled texture.
Root Cause
The issue lies in gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc, specifically within PrepareUnpackBuffer. When copying to a destination internal format that requires conversion, such as GL_RGB9_E5, the implementation falls back to CopyTextureMethod::DRAW_AND_READBACK.
On Android, when format == GL_RGB and type == GL_FLOAT, PrepareUnpackBuffer allocates uninitialized memory using base::HeapArray<uint8_t>::Uninit and attempts to populate it using glReadPixels:
if (format == GL_RGB && type == GL_FLOAT) {
#if BUILDFLAG(IS_ANDROID)
auto pixels = base::HeapArray<uint8_t>::Uninit(pixel_num * 4);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
// ...
The vulnerability occurs because the code fails to unbind the GL_PIXEL_PACK_BUFFER target before calling glReadPixels (unlike the non-Android fallback path, which explicitly binds a different pack buffer).
If an attacker binds a WebGL Pixel Buffer Object (PBO) to gl.PIXEL_PACK_BUFFER immediately before issuing a texture copy command, the OpenGL driver interprets the pixels.data() host pointer as a large integer offset into the bound PBO. Since this offset typically exceeds the bounds of the PBO, the driver safely rejects the operation with a GL_INVALID_OPERATION error. Consequently, the glReadPixels call writes no data, and the pixels array retains uninitialized GPU heap memory.
The uninitialized memory is then converted to floating-point format and uploaded to a local GL_PIXEL_UNPACK_BUFFER, which is subsequently uploaded to the final destination texture via glTexSubImage2D.
Potential Attack Steps
Note: These are suggested steps; our tooling has not executed a proof of concept.
- Initialize a WebGL 2.0 context, which provides access to
GL_RGB9_E5textures and PBOs. - Create a texture and configure it with the
gl.RGB9_E5internal format. - Create a WebGLBuffer and bind it to
gl.PIXEL_PACK_BUFFER. - Allocate a very small size for this buffer (e.g., 1 byte) using
gl.bufferDatato ensure any host-pointer offset will trigger an out-of-bounds error. - Prepare an
HTMLVideoElementto serve as the source for the upload. - Call
gl.texSubImage2Dtargeting thegl.RGB9_E5texture, passing the video element. This triggers theDRAW_AND_READBACKpath in the GPU process. - The GPU process allocates uninitialized heap memory. Due to the bound pack buffer,
glReadPixelsfails, and the uninitialized memory is written into theGL_RGB9_E5texture. - Because
GL_RGB9_E5cannot be read directly viagl.readPixels, compile a WebGL shader that samples from theGL_RGB9_E5texture and renders it into a standard color-renderable texture (e.g.,gl.RGBA8). - Call
gl.readPixelson the standard texture to read the leaked GPU memory back to JavaScript, bypassing Site Isolation.
Proposed Fix
Explicitly unbind the GL_PIXEL_PACK_BUFFER target before invoking glReadPixels in the Android fallback path to prevent the driver from interpreting the host pointer as a PBO offset.
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
Additionally, consider using zero-initialized memory (base::HeapArray<uint8_t>::WithSize) instead of Uninit when reading back pixel data to provide a robust defense-in-depth against similar uninitialized memory leaks.
Evaluated with Chrome root at commit: 2155cb00003ec35716a76ed3246eae995f87b7ff
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
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.