Medium watch CVSS 5.3 chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
5.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized Use in GPU
DescriptionUninitialized Use in GPU
ComponentGPU
Sub-componentGPU
Bug ClassUninitialized Memory
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar
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.
Tracker496441232
Fix commita8b79551f4bc (chromium/src) +6/-3
CWECWE-457 (Use of uninitialized)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-03-26 (per vendor advisory)
Fix landed2026-03-30 (public git)
Disclosed2026-05-05
Patch gap36 days — fix public in source before the advisory

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_COMPLETE only when the attached texture forms a valid render target; if incomplete, a glClear against 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.

Key insight
The code asserted “cleared” unconditionally even when the clear could have been a no-op against an incomplete framebuffer; the fix only records the clear when glCheckFramebufferStatusEXT confirms the framebuffer was complete, so an un-zeroed texture is never flagged as initialized.

Attack Path

  1. 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.
  2. Clearing silently no-ops The clear path attaches the texture and issues glClear, but because the framebuffer is not GL_FRAMEBUFFER_COMPLETE the clear does nothing while, pre-patch, SetCleared() still marks it initialized.
  3. 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.
  4. 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

An attacker in a renderer could cause a shared image to be treated as initialized while its backing texture still contained uninitialized GPU memory, enabling disclosure of stale GPU memory contents to the renderer in the GPU process context. The precondition is reaching the clear path with a texture configuration that produces an incomplete framebuffer. The vendor advisory rates this medium (Uninitialized Use in GPU) and does not report exploitation in the wild; any sensitivity of the leaked bytes is (inferred).

Files Changed

  • gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc

Audit Directions

  • Set-cleared without success check
    Audit 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 succeed
    Look for glClear, glTexImage, and similar calls whose effect is assumed without checking framebuffer completeness or glGetError, especially where the result feeds an initialization flag.
  • Uninitialized-memory invariants in the GPU process
    Review shared-image backing factories and representations for paths that can expose a buffer before a guaranteed zero-initialization step.
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();
   }
 }
 
Loading diff…

Original Bug Report

reported by [email protected]

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.cc
  • gpu/ipc/service/shared_image_stub.cc
  • gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc
  • gpu/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():

  1. A temporary Framebuffer Object (FBO) is generated.
  2. The code attempts to attach the texture to the FBO: api->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture->target(), texture->service_id(), 0);
  3. It issues a glClear command.
  4. 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.

  1. Compromise Renderer: An attacker gains arbitrary code execution within the sandboxed renderer process.
  2. Send IPC Request: The attacker crafts a CreateSharedImage IPC request. They specify a multi-planar format (e.g., NV12) and manually set the prefers_external_sampler flag to true.
  3. Bypass OS Constraints: On non-Windows/Apple platforms (e.g., Linux/Ozone), SharedImageStub::CreateSharedImage does not block the prefers_external_sampler flag, allowing the request to proceed.
  4. Allocate Uninitialized Memory: The OzoneImageBackingFactory allocates a fresh native pixmap (e.g., a dma-buf) containing uninitialized memory that may hold stale data from other processes or origins.
  5. Trigger EnsureClear: The renderer sends a CreateAndTexStorage2DSharedImageINTERNAL command. The GPU process maps the mailbox and triggers PassthroughResources::SharedImageData::EnsureClear().
  6. Silent Failure: The FBO attachment fails silently due to the GL_TEXTURE_EXTERNAL_OES target, the clear acts as a no-op, and the uninitialized image is marked as cleared.
  7. Exfiltrate Data: The compromised renderer binds the texture, uses a GLSL shader with samplerExternalOES to sample the uninitialized memory into a readable renderbuffer, and extracts the sensitive pixels using glReadPixels.

Suggested Fix

The EnsureClear() function must verify that the clear operation actually succeeded before marking the representation as cleared.

  1. Introduce a check for framebuffer completeness (api->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) after attaching the texture, or explicitly check glGetError().
  2. If the FBO is incomplete, EnsureClear must not call representation_->SetCleared().
  3. 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.

View on issue tracker