CVE-2026-7955
Overview
Background
- Shared image
- a GPU-backed image buffer shared across command-buffer clients; before first use its backing texture must be “cleared” (zero-initialized) so no stale GPU memory is exposed.
- "Cleared" tracking / `SetCleared`
- a per-image flag asserting the backing memory has been initialized; once set, the system skips re-clearing and permits reads, so it must only be set when the clear actually happened.
- Framebuffer completeness / `glCheckFramebufferStatusEXT`
- an OpenGL query that returns
GL_FRAMEBUFFER_COMPLETEonly when the attached texture forms a valid render target; if incomplete, aglClearagainst that framebuffer is a no-op and leaves the texture uninitialized. - Passthrough decoder
- the ANGLE-backed GLES2 command decoder (
gles2_cmd_decoder_passthrough.cc) that services renderer GL commands in the GPU process.
Root Cause Analysis
The clear routine (PassthroughResources::SharedImageData::EnsureClear, per the commit subject; the @@ -473 header does not name the enclosing function) generated a framebuffer, bound the texture, disabled scissor, and issued glClear(GL_COLOR_BUFFER_BIT), then unconditionally called representation_->SetCleared(). The flaw is that glClear only initializes the texture if the framebuffer is complete; if glCheckFramebufferStatusEXT would have returned anything other than GL_FRAMEBUFFER_COMPLETE, the clear silently did nothing yet the code still marked the image cleared. That violates the invariant “the cleared flag implies the memory was actually zeroed,” so a texture could be flagged initialized while still holding whatever uninitialized/stale GPU memory it was allocated over.
The fix moves SetCleared() inside an if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) guard so the image is marked cleared only when the clear is known to have succeeded, otherwise it stays uncleared and will be re-cleared on a later path. This is a tightening: SetCleared() now runs conditionally, after the completeness check, before the framebuffer is torn down.
glCheckFramebufferStatusEXT confirms the framebuffer was complete, so an un-zeroed texture is never flagged as initialized.Attack Path
- Create a shared image A renderer (via the GPU command buffer) allocates a shared image whose backing texture, under certain formats/dimensions, yields an incomplete framebuffer when attached for clearing.
- Clearing silently no-ops
The clear path attaches the texture and issues
glClear, but because the framebuffer is notGL_FRAMEBUFFER_COMPLETEthe clear does nothing while, pre-patch,SetCleared()still marks it initialized. - Read the "cleared" image Subsequent use of the image skips re-clearing and samples/reads its contents, which are uninitialized GPU memory rather than zeros.
- Information disclosure The renderer observes residual GPU memory contents (inferred; the diff demonstrates the mis-set cleared flag and uninitialized state, not a specific read-back primitive or cross-process leak).
Impact Assessment
Files Changed
gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
Audit Directions
- Set-cleared without success checkAudit every call site that marks a GPU/shared-image buffer as cleared or initialized and confirm it is gated on the clear/draw operation actually having succeeded (framebuffer complete, no GL error).
- GL operations assumed to succeedLook for
glClear,glTexImage, and similar calls whose effect is assumed without checking framebuffer completeness orglGetError, especially where the result feeds an initialization flag. - Uninitialized-memory invariants in the GPU processReview shared-image backing factories and representations for paths that can expose a buffer before a guaranteed zero-initialization step.
Patch
From a8b79551f4bc5001d859115cede7eadcdd481257 Mon Sep 17 00:00:00 2001 From: Yuki Shiino <[email protected]> Date: Mon, 30 Mar 2026 08:04:44 -0700 Subject: [PATCH] [gpu] Mark SetCleared iff succeeded in PassthroughResources::SharedImageData::EnsureClear Bug: 496441232 Change-Id: I605e9f15400bd174979107f0729a359127e8328c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7706449 Reviewed-by: Saifuddin Hitawala <[email protected]> Commit-Queue: Saifuddin Hitawala <[email protected]> Auto-Submit: Yuki Shiino <[email protected]> Cr-Commit-Position: refs/heads/main@{#1607104} --- diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc index 9514ae65..0947f6c 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc @@ -473,13 +473,16 @@ api->glDisableFn(GL_SCISSOR_TEST); api->glClearFn(GL_COLOR_BUFFER_BIT); + if (api->glCheckFramebufferStatusEXTFn(GL_FRAMEBUFFER) == + GL_FRAMEBUFFER_COMPLETE) { + // Mark the shared image as cleared. + representation_->SetCleared(); + } + // Delete the generated framebuffer. api->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture->target(), 0, 0); api->glDeleteFramebuffersEXTFn(1, &fbo); - - // Mark the shared image as cleared. - representation_->SetCleared(); } }
Original Bug Report
GPU memory leak via unchecked GL errors in SharedImageData::EnsureClear
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A compromised renderer can potentially read uninitialized GPU memory by creating a SharedImage with prefers_external_sampler=true. The GPU process’s EnsureClear function fails to clear GL_TEXTURE_EXTERNAL_OES textures on certain platforms due to silent GL attachment failures, but still marks them as cleared. The renderer can then sample the uncleared memory, leading to cross-origin or cross-process information disclosure.
Affected files:
gpu/command_buffer/service/gles2_cmd_decoder_passthrough.ccgpu/ipc/service/shared_image_stub.ccgpu/command_buffer/service/shared_image/ozone_image_backing_factory.ccgpu/command_buffer/service/shared_image/ozone_image_gl_textures_holder.cc
Estimated timestamp from git blame: 2022-07-21
Vulnerability Details
There is a potential high-severity information disclosure vulnerability in the GPU process. Specifically, the function PassthroughResources::SharedImageData::EnsureClear() in gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc fails to handle OpenGL errors when attempting to clear uninitialized SharedImage backing memory.
When a SharedImage is created with the prefers_external_sampler flag set to true, its underlying OpenGL texture is initialized with the GL_TEXTURE_EXTERNAL_OES target (see OzoneImageGLTexturesHolder). Later, to prevent uninitialized memory from being exposed to the renderer, the command buffer invokes EnsureClear().
Inside EnsureClear():
- A temporary Framebuffer Object (FBO) is generated.
- The code attempts to attach the texture to the FBO:
api->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture->target(), texture->service_id(), 0); - It issues a
glClearcommand. - It unconditionally executes
representation_->SetCleared(), marking the memory as safe for renderer consumption.
However, on certain platforms (such as Linux and ChromeOS using ANGLE-on-Vulkan), attaching a GL_TEXTURE_EXTERNAL_OES texture to an FBO is unsupported unless the GL_EXT_YUV_target extension is fully supported and enabled. In environments where it is not, the glFramebufferTexture2DEXTFn call silently fails and raises a GL_INVALID_OPERATION error. The FBO remains incomplete, causing the subsequent glClear to act as a silent no-op. Because EnsureClear() performs no error checking (e.g., glCheckFramebufferStatusEXT or glGetError), the uninitialized buffer is incorrectly marked as cleared.
Suggested Steps to Trigger
Note: These are potential steps suggested by our setup, which does not currently have the ability to run code to verify a working proof of concept.
- Compromise Renderer: An attacker gains arbitrary code execution within the sandboxed renderer process.
- Send IPC Request: The attacker crafts a
CreateSharedImageIPC request. They specify a multi-planar format (e.g.,NV12) and manually set theprefers_external_samplerflag totrue. - Bypass OS Constraints: On non-Windows/Apple platforms (e.g., Linux/Ozone),
SharedImageStub::CreateSharedImagedoes not block theprefers_external_samplerflag, allowing the request to proceed. - Allocate Uninitialized Memory: The
OzoneImageBackingFactoryallocates a fresh native pixmap (e.g., a dma-buf) containing uninitialized memory that may hold stale data from other processes or origins. - Trigger EnsureClear: The renderer sends a
CreateAndTexStorage2DSharedImageINTERNALcommand. The GPU process maps the mailbox and triggersPassthroughResources::SharedImageData::EnsureClear(). - Silent Failure: The FBO attachment fails silently due to the
GL_TEXTURE_EXTERNAL_OEStarget, the clear acts as a no-op, and the uninitialized image is marked ascleared. - Exfiltrate Data: The compromised renderer binds the texture, uses a GLSL shader with
samplerExternalOESto sample the uninitialized memory into a readable renderbuffer, and extracts the sensitive pixels usingglReadPixels.
Suggested Fix
The EnsureClear() function must verify that the clear operation actually succeeded before marking the representation as cleared.
- Introduce a check for framebuffer completeness (
api->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) after attaching the texture, or explicitly checkglGetError(). - If the FBO is incomplete,
EnsureClearmust not callrepresentation_->SetCleared(). - Instead, the implementation should either fallback to an alternative clear mechanism (e.g., clearing the native pixmap via CPU mapping or Skia before it is bound to GL), or explicitly fail the command and terminate the context to prevent uninitialized memory access.
Evaluated with Chrome root at commit: 0eb4855bda702feaaa8b899336664f97e3df88b8
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.