CVE-2026-106223
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifgpu/command_buffer/service/gles2_cmd_decoder.cc |
modified |
Files Changed
gpu/command_buffer/service/gles2_cmd_decoder.cc
Patch
From ce1d57a0da9c4882cd2836409794a1298297b440 Mon Sep 17 00:00:00 2001 From: Brandon Jones <[email protected]> Date: Thu, 03 Sep 2026 14:37:47 -0700 Subject: [PATCH] Flag failed ClearCompressedTextureLevel calls If a ClearCompressedTextureLevel call emits a GL error, ensure that the level is not flagged as cleared so that it can't be sampled afterwards. Fixed: 501633302 Change-Id: I330d821e298cded38be3eb2c28c6f17b8918d69d Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8350478 Reviewed-by: Kai Ninomiya <[email protected]> Commit-Queue: Kai Ninomiya <[email protected]> Auto-Submit: Brandon Jones <[email protected]> Cr-Commit-Position: refs/heads/main@{#1691944} --- diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index f0aa661..2bb685b 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -12460,6 +12460,8 @@ TRACE_EVENT1("gpu", "GLES2DecoderImpl::ClearCompressedTextureLevel", "bytes_required", bytes_required); + bool cleared = true; + api()->glBindBufferFn(GL_PIXEL_UNPACK_BUFFER, 0); { // Add extra scope to destroy zero and the object it owns right @@ -12471,8 +12473,18 @@ if (reset_base_level) { api()->glTexParameteriFn(texture->target(), GL_TEXTURE_BASE_LEVEL, 0); } + + LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("ClearCompressedTextureLevel"); api()->glCompressedTexSubImage2DFn(target, level, 0, 0, width, height, format, zero.size(), zero.data()); + // Some compressed formats are not supported by CompressedTexSubImage* + // in some circumstances. If the driver rejects the call, report the failure + // to clear so the caller does not mark it cleared (which would let a draw + // sample uninitialized GPU memory). + if (LOCAL_PEEK_GL_ERROR("ClearCompressedTextureLevel") != GL_NO_ERROR) { + cleared = false; + } + if (reset_base_level) { api()->glTexParameteriFn(texture->target(), GL_TEXTURE_BASE_LEVEL, texture->base_level()); @@ -12487,7 +12499,7 @@ if (bound_buffer) { api()->glBindBufferFn(GL_PIXEL_UNPACK_BUFFER, bound_buffer->service_id()); } - return true; + return cleared; } bool GLES2DecoderImpl::ClearCompressedTextureLevel3D(Texture* texture, @@ -12514,6 +12526,8 @@ TRACE_EVENT1("gpu", "GLES2DecoderImpl::ClearCompressedTextureLevel3D", "bytes_required", bytes_required); + bool cleared = true; + api()->glBindBufferFn(GL_PIXEL_UNPACK_BUFFER, 0); { // Add extra scope to destroy zero and the object it owns right @@ -12525,8 +12539,18 @@ if (reset_base_level) { api()->glTexParameteriFn(texture->target(), GL_TEXTURE_BASE_LEVEL, 0); } + + LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("ClearCompressedTextureLevel3D"); api()->glCompressedTexSubImage3DFn(target, level, 0, 0, 0, width, height, depth, format, zero.size(), zero.data()); + // Some compressed formats are not supported by CompressedTexSubImage* + // in some circumstances. If the driver rejects the call, report the failure + // to clear so the caller does not mark it cleared (which would let a draw + // sample uninitialized GPU memory). + if (LOCAL_PEEK_GL_ERROR("ClearCompressedTextureLevel3D") != GL_NO_ERROR) { + cleared = false; + } + if (reset_base_level) { api()->glTexParameteriFn(texture->target(), GL_TEXTURE_BASE_LEVEL, texture->base_level()); @@ -12541,7 +12565,7 @@ if (bound_buffer) { api()->glBindBufferFn(GL_PIXEL_UNPACK_BUFFER, bound_buffer->service_id()); } - return true; + return cleared; } bool GLES2DecoderImpl::IsCompressedTextureFormat(unsigned format) {
Original Bug Report
Uninitialized GPU memory disclosure via ETC1 textures
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 Chrome Security team.
Overview: The validating command decoder fails to verify if native drivers successfully clear ETC1/ATC compressed textures. Because the GL specification forbids glCompressedTexSubImage2D for these formats, native drivers reject the clear operation, leaving VRAM uninitialized. A malicious WebGL page could potentially sample this uninitialized memory, leading to cross-origin or cross-process information disclosure.
Affected files:
gpu/command_buffer/service/gles2_cmd_decoder.ccgpu/command_buffer/service/texture_manager.ccgpu/command_buffer/service/feature_info.cc
Estimated timestamp from git blame: 2024-04-10
Description
There is a potential uninitialized memory disclosure vulnerability in the GLES2 validating command decoder (predominantly used on Android or when ANGLE is not active). The issue stems from a failure to check for OpenGL errors during the lazy-clearing of specific compressed texture formats, namely GL_ETC1_RGB8_OES and GL_ATC_*_AMD.
When a WebGL application allocates an immutable texture using texStorage2D, the command buffer allocates VRAM but correctly marks it as uninitialized. Before this texture can be sampled in a draw call, the decoder must zero-initialize it to prevent leaking sensitive cross-origin or cross-process VRAM.
The clearing logic in GLES2DecoderImpl::ClearCompressedTextureLevel (gpu/command_buffer/service/gles2_cmd_decoder.cc) attempts to zero-fill the texture by calling the native driver’s glCompressedTexSubImage2D with a buffer of zeros. However, the Khronos specifications for OES_compressed_ETC1_RGB8_texture and AMD_compressed_ATC_texture explicitly state that glCompressedTexSubImage2D is not supported for these formats.
Consequently, compliant native drivers reject the sub-image update with a GL_INVALID_OPERATION error and leave the VRAM unmodified. Because ClearCompressedTextureLevel does not check for GL errors (e.g., via glGetError()) after making the call, it unconditionally returns true. The TextureManager then erroneously marks the texture level as fully cleared, allowing the renderer to sample uninitialized VRAM in subsequent draw calls.
Potential Exploitation Steps
Note: These steps are suggested based on code analysis; our tooling agent does not run code, so a working proof-of-concept has not been executed.
An attacker could potentially exploit this via the following sequence:
- Request a WebGL 2.0 context to ensure
texStorage2Dretains the compressed internal format (avoiding fallback behaviors in WebGL 1.0). - Allocate an immutable texture using
gl.texStorage2Dwith theCOMPRESSED_RGB8_ETC1_WEBGLformat. - Create a standard RGBA8 Framebuffer Object (FBO) and attach it as the rendering destination.
- Create a shader program with a fragment shader that samples from the ETC1 texture and writes the output to the
gl_FragColor. - Bind the uninitialized ETC1 texture to the sampler and issue a draw call (e.g.,
gl.drawArrays). - The command buffer attempts to lazy-clear the ETC1 texture using
glCompressedTexSubImage2D, which fails silently on the native driver. - The draw call proceeds, sampling uninitialized VRAM (which may contain decrypted media frames, pixels from other tabs, or OS UI elements) into the RGBA8 FBO.
- The attacker calls
gl.readPixelson the FBO to exfiltrate the leaked memory.
Suggested Fix
There are two primary ways to address this issue:
- Check for GL Errors: Modify
GLES2DecoderImpl::ClearCompressedTextureLevelto verify the success of the clear operation usingLOCAL_PEEK_GL_ERRORafter callingapi()->glCompressedTexSubImage2DFn. If an error occurred, the function should returnfalseso the texture remains marked as uncleared. - Prevent Allocation: If a platform’s native driver cannot clear ETC1/ATC textures because it strictly adheres to the extension specification prohibiting sub-image updates, Chromium should prevent these formats from being allocated via
TexStorage2D(or explicitly implement a software fallback workaround for initialization).
Evaluated with Chrome root at commit: 096fc8fdbfacf2546485756d03f160a3d04fcc9b
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.