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

Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized Use in GPU
DescriptionUninitialized Use in GPU
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.
Tracker511772034
Fix commit7c0c88c63a95 (chromium/src) +3/-0
CWECWE-457 (Use of uninitialized)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-05-10 (per vendor advisory)
Fix landed2026-05-12 (public git)
Disclosed2026-06-30
Patch gap49 days — fix public in source before the advisory

Background

Luma emulation texture copy
a GPU-service fallback in gles2_cmd_copy_tex_image.cc that emulates luminance/alpha texture formats by drawing into a destination texture, used when the driver lacks native support.
`GL_RASTERIZER_DISCARD`
an OpenGL ES 3.0 / WebGL 2 pipeline state that, when enabled, discards all primitives before rasterization, so draw calls produce no fragments and write nothing.
`IsWebGL2OrES3OrHigherContext`
a FeatureInfo predicate indicating the context is at least WebGL 2 / ES 3.0, i.e. the level at which GL_RASTERIZER_DISCARD exists.
Uninitialized texture use
reading a texture whose backing store was never written, yielding whatever residual GPU memory occupied it.

Root Cause Analysis

The luma-emulation copy routine backs up and neutralizes a set of pipeline states (depth mask, blend, dither, window rectangles, etc.) before performing the emulation draw that writes the destination texture, but it did not account for GL_RASTERIZER_DISCARD. On a WebGL 2 / ES 3.0 context where the client had left GL_RASTERIZER_DISCARD enabled, the emulation draw would be entirely discarded before rasterization, so the copy silently produced no output and the destination texture was left with uninitialized (residual) contents. The invariant that the emulation path must actually populate the destination was violated by inherited client rasterizer-discard state.

The fix adds, guarded by IsWebGL2OrES3OrHigherContext, a glDisable(GL_RASTERIZER_DISCARD) alongside the other state neutralizations so the emulation draw is guaranteed to rasterize and write the texture.

Key insight
The emulation copy neutralized many pipeline states but forgot GL_RASTERIZER_DISCARD, letting a client-enabled discard silently skip the copy and leave the texture uninitialized; explicitly disabling it (on contexts where it exists) restores the guarantee that the copy runs.

Attack Path

  1. Use a WebGL 2 / ES 3.0 context A page (or worker) obtains a WebGL 2 context so that GL_RASTERIZER_DISCARD is available.
  2. Enable rasterizer discard The client enables GL_RASTERIZER_DISCARD via enable(RASTERIZER_DISCARD) and leaves it set.
  3. Trigger the luma emulation copy The page performs a texture operation that routes into the luma-emulation copy path; pre-patch the emulation draw is discarded before rasterization, so the destination texture is never written.
  4. Read back uninitialized memory The page samples or reads back the destination texture and obtains residual GPU memory contents rather than the intended copied data, an uninitialized-memory disclosure. (inferred)

Impact Assessment

An attacker running WebGL 2 content can cause the luma-emulation texture copy to be skipped, leaving a destination texture populated with uninitialized GPU memory that may then be read back, an information-disclosure primitive in the GPU process. The advisory classifies this as uninitialized use in GPU with no exploited-in-the-wild status stated; any escalation beyond reading stale GPU memory is not demonstrated by the patch and is (inferred). The precondition is a WebGL2/ES3 context with GL_RASTERIZER_DISCARD enabled at the time the emulation copy runs.

Files Changed

  • gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc

Audit Directions

  • Incomplete state backup/reset
    Review GPU-service helper routines that snapshot and neutralize GL state before an internal draw/copy to confirm they cover all states that can suppress output (rasterizer discard, scissor, color/depth masks, stencil, sample masks).
  • Client-inherited pipeline state
    Audit internal driver-emulation and blit paths for reliance on default pipeline state that a client could have changed, especially ES3/WebGL2-only states absent on older contexts.
  • Silent no-op draws
    Look for emulation or clear/copy operations whose result is assumed but not verified, where a discarded or scissored draw would leave a resource uninitialized.
From 7c0c88c63a9513aecc436d60226ec0e12194d60f Mon Sep 17 00:00:00 2001
From: Ken Russell <[email protected]>
Date: Mon, 11 May 2026 22:10:17 -0700
Subject: [PATCH] Disable rasterizer discard during luma emulation texture copy.

The copy could otherwise be accidentally skipped.

Fixed: 511772034
Change-Id: I301da32524fb8949b516fb8f5d19765bec202673
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7836292
Reviewed-by: Brandon Jones <[email protected]>
Commit-Queue: Kenneth Russell <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1629062}
---

diff --git a/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc b/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc
index a08bb3ac4..0d8da6e 100644
--- a/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc
+++ b/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc
@@ -232,6 +232,9 @@
   glDepthMask(GL_FALSE);
   glDisable(GL_BLEND);
   glDisable(GL_DITHER);
+  if (decoder->GetFeatureInfo()->IsWebGL2OrES3OrHigherContext()) {
+    glDisable(GL_RASTERIZER_DISCARD);
+  }
   if (decoder->GetFeatureInfo()->feature_flags().ext_window_rectangles) {
     glWindowRectanglesEXT(GL_EXCLUSIVE_EXT, 0, nullptr);
   }
Loading diff…

Original Bug Report

reported by [email protected]

Potential GPU VRAM leak via GL_RASTERIZER_DISCARD during LUMA emulation blit

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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A malicious WebGL2 page on Android can potentially leak uninitialized GPU memory (VRAM) containing cross-origin pixel data. This occurs because the internal blitter used for LUMA texture emulation fails to disable GL_RASTERIZER_DISCARD, allowing an attacker to discard the internal initialization draw call. The resulting uninitialized memory is then copied to an attacker-accessible texture and incorrectly marked as cleared by Chromium’s safety mechanisms.

Affected files:

  • gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc
  • gpu/command_buffer/service/gles2_cmd_decoder.cc

Estimated timestamp from git blame: 2017-11-03

Summary

A vulnerability exists in the Chromium GPU process on Android when using the validating command decoder with native OpenGL ES 3 drivers. The CopyTexImageResourceManager uses an internal draw call to emulate legacy LUMINANCE, ALPHA, and LUMINANCE_ALPHA textures during copyTexImage2D and copyTexSubImage2D operations. However, the emulation logic fails to ensure that the GL_RASTERIZER_DISCARD state is disabled. A malicious WebGL application can enable this state to cause the internal blit to be silently discarded, resulting in uninitialized driver memory being copied into a texture and marked as cleared, bypassing Chromium’s lazy-clearing protections.

Technical Details

When a WebGL application requests a copy to a LUMA format texture on hardware that requires emulation, the command is routed to CopyTexImageResourceManager::DoCopyTexSubImageToLUMACompatibilityTexture (gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc).

The emulation process involves the following steps:

  1. Allocation: A temporary scratch texture (scratch_textures_[1]) is allocated using glTexImage2D(..., nullptr) (line 217). Passing nullptr causes the GPU driver to allocate uninitialized VRAM.
  2. State Configuration: The method configures the GL state for an internal blit, explicitly disabling tests like GL_SCISSOR_TEST and GL_DEPTH_TEST (lines 227-234). Crucially, it fails to disable GL_RASTERIZER_DISCARD.
  3. Discarded Draw: The method issues glDrawArrays(GL_TRIANGLES, 0, 6) (line 242) to render swizzled data into the scratch texture. If the attacker previously called gl.enable(gl.RASTERIZER_DISCARD), the OpenGL driver will silently discard all primitives. The draw call does nothing, and the scratch texture remains completely uninitialized.
  4. VRAM Copy: The method calls glCopyTexSubImage2D (line 250) to copy from the scratch framebuffer to the destination texture. Because glCopyTexSubImage2D is a pixel transfer operation, it is unaffected by GL_RASTERIZER_DISCARD. It successfully copies the uninitialized VRAM into the attacker’s texture.

Execution returns to GLES2DecoderImpl::DoCopyTexSubImage2D (gpu/command_buffer/service/gles2_cmd_decoder.cc). Because discarding primitives is a valid OpenGL operation, LOCAL_PEEK_GL_ERROR returns GL_NO_ERROR. Finding no errors, Chromium incorrectly assumes the texture was successfully initialized and updates its internal security tracking by calling texture_manager()->SetLevelCleared(texture_ref, target, level, true); (line 13724).

Impact

An attacker can sample the resulting texture to exfiltrate stale GPU memory. This memory may contain sensitive cross-origin data, such as pixels from other web pages (via canvas or video elements) or compositor tiles from other GPU-process clients. This represents a High severity (S1) Site Isolation bypass.

Suggested Reproduction Steps

Note: These steps are based on static analysis and have not been executed in a live environment.

  1. From a web page, obtain a WebGL2 context.
  2. Create and explicitly clear a source framebuffer (to bypass Chromium’s ClearUnclearedAttachments logic).
  3. Enable discard: gl.enable(gl.RASTERIZER_DISCARD).
  4. Perform a copy operation to a LUMA format requiring emulation: gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, 0, 0, width, height, 0).
  5. Disable discard: gl.disable(gl.RASTERIZER_DISCARD).
  6. Sample the destination texture in a fragment shader or use gl.readPixels to retrieve the uninitialized memory data.

Suggested Fix

In gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc, update DoCopyTexSubImageToLUMACompatibilityTexture to explicitly disable GL_RASTERIZER_DISCARD alongside the other state changes before the draw call:

  glDisable(GL_SCISSOR_TEST);
  glDisable(GL_DEPTH_TEST);
  glDisable(GL_STENCIL_TEST);
  glDisable(GL_CULL_FACE);
  glDisable(GL_BLEND);
  glDisable(GL_DITHER);
+ if (decoder->GetFeatureInfo()->IsWebGL2OrES3OrHigherContext()) {
+   glDisable(GL_RASTERIZER_DISCARD);
+ }

Because decoder->RestoreGlobalState() is called at the end of the function (line 261), the GL_RASTERIZER_DISCARD state should be automatically restored to the user’s expected state.

Evaluated with Chrome root at commit: eca8648a4e1cdfdda68c495a6003059fed641955


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.

View on issue tracker