Low chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized Use in WebXR
DescriptionUninitialized Use in WebXR
ComponentWebXR
Bug ClassUninitialized Memory
Tracker518337516
Fix commitcf40e3311960 (chromium/src) +11/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Files Changed

  • third_party/blink/renderer/modules/xr/xr_webgl_cubemap_swap_chain.cc
  • third_party/blink/renderer/modules/xr/xr_webgl_texture_array_swap_chain.cc
From cf40e33119608fc222ae4be24a7d56972558a7ff Mon Sep 17 00:00:00 2001
From: Brandon Jones <[email protected]>
Date: Tue, 02 Jun 2026 14:05:54 -0700
Subject: [PATCH] Disable GL_RASTERIZER_DISCARD when clearing XR swapchains

Ensures that GL_RASTERIZER_DISCARD is disabled when clearing WebXR
swapchains produced by the system. Prevents a scenario where
uninitialized content may be exposed to the page.

Fixed: 518337516
Change-Id: Ib1e910ee5a9630a66ea202d5868b91349842a3c3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7897301
Commit-Queue: Brandon Jones <[email protected]>
Auto-Submit: Brandon Jones <[email protected]>
Commit-Queue: Alexander Cooper <[email protected]>
Reviewed-by: Alexander Cooper <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1640452}
---

diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_cubemap_swap_chain.cc b/third_party/blink/renderer/modules/xr/xr_webgl_cubemap_swap_chain.cc
index 5fe44a29..be9dcdf 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_cubemap_swap_chain.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_cubemap_swap_chain.cc
@@ -253,6 +253,11 @@
   gl->Disable(GL_BLEND);
   gl->Disable(GL_DITHER);
   gl->Disable(GL_SCISSOR_TEST);
+
+  if (webgl2()) {
+    gl->Disable(GL_RASTERIZER_DISCARD);
+  }
+
   gl->ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
   gl->DepthMask(GL_FALSE);
 
@@ -315,6 +320,7 @@
       static_cast<DrawingBuffer::Client*>(context());
   client->DrawingBufferClientRestoreTextureCubeMapBinding();
   client->DrawingBufferClientRestoreScissorTest();
+  client->DrawingBufferClientRestoreRasterizerDiscard();
   client->DrawingBufferClientRestoreMaskAndClearValues();
   client->DrawingBufferClientRestoreFramebufferBinding();
 
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_texture_array_swap_chain.cc b/third_party/blink/renderer/modules/xr/xr_webgl_texture_array_swap_chain.cc
index 5e2a0daa..a3206d11 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_texture_array_swap_chain.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_texture_array_swap_chain.cc
@@ -134,6 +134,10 @@
   gl->Disable(GL_DITHER);
   gl->Disable(GL_SCISSOR_TEST);
 
+  if (webgl2()) {
+    gl->Disable(GL_RASTERIZER_DISCARD);
+  }
+
   gl->ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
   gl->DepthMask(GL_FALSE);
   gl->BindVertexArrayOES(vao_);
@@ -173,6 +177,7 @@
       static_cast<DrawingBuffer::Client*>(context());
   client->DrawingBufferClientRestoreTexture2DArrayBinding();
   client->DrawingBufferClientRestoreScissorTest();
+  client->DrawingBufferClientRestoreRasterizerDiscard();
   client->DrawingBufferClientRestoreMaskAndClearValues();
   client->DrawingBufferClientRestoreFramebufferBinding();
 
Loading diff…

Original Bug Report

reported by [email protected]

Potential uninitialized GPU memory exposure in WebXR Texture Array and Cubemap swap chains

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

Overview: A potential state-management defect in Blink’s WebXR implementation allows WebGL2 contexts with GL_RASTERIZER_DISCARD active to bypass internal copy-draw steps on frame completion. Because the underlying swap chains for texture arrays and cubemaps force clear_on_access to false, this bypass leaves target textures uncleared. As a result, uninitialized GPU memory from raw platform allocations is submitted to the compositor and displayed on the user’s headset.

Affected files:

  • third_party/blink/renderer/modules/xr/xr_webgl_texture_array_swap_chain.cc
  • third_party/blink/renderer/modules/xr/xr_webgl_cubemap_swap_chain.cc

Estimated timestamp from git blame: 2025-02-01

Root Cause

In third_party/blink/renderer/modules/xr/xr_webgl_texture_array_swap_chain.cc and third_party/blink/renderer/modules/xr/xr_webgl_cubemap_swap_chain.cc, the OnFrameEnd() methods perform an internal full-quad copy from the WebGL context’s source texture to a wrapped (device-supplied) target swap chain texture.

While these functions disable several WebGL states (such as depth, stencil, culling, blending, dithering, and scissor tests) to ensure a clean drawing state, they fail to disable GL_RASTERIZER_DISCARD if it was enabled by the webpage’s WebGL2 context. Under OpenGL ES 3.0, when GL_RASTERIZER_DISCARD is enabled, fragment generation is skipped entirely, causing the copy draw calls (DrawArraysInstancedANGLE or DrawElements) to become no-ops.

This behavior creates a sibling asymmetry with the base class XRWebGLSwapChain::ClearCurrentTexture in third_party/blink/renderer/modules/xr/xr_webgl_swap_chain.cc, which correctly disables and restores GL_RASTERIZER_DISCARD:

// third_party/blink/renderer/modules/xr/xr_webgl_swap_chain.cc
gl->Disable(GL_SCISSOR_TEST);
if (webgl2_) {
  gl->Disable(GL_RASTERIZER_DISCARD);
}
... 
client->DrawingBufferClientRestoreRasterizerDiscard();

Why Stale GPU Memory is Exposed

To optimize drawing performance, the underlying swap chains for texture-arrays and cubemaps have clear_on_access forced to false (under the assumption that the copy program will unconditionally overwrite the entire target buffer). For example, in xr_webgl_binding.cc:

color_desc.clear_on_access =
    clear_on_access &&
    texture_type.AsEnum() != V8XRTextureType::Enum::kTextureArray;

Because of this, the target textures are never cleared when queried. On Android devices, the imported AHardwareBuffer backing memory is allocated raw from the OS gralloc pool without zero-initialization, and the lazy-clear tracking is marked cleared on import. When the copy-draw is bypassed due to GL_RASTERIZER_DISCARD, the uncleared, uninitialized hardware buffer is submitted to the compositor and rendered on the headset.

Potential Steps to Trigger

Note: Our automated analysis tools do not run code, so these are potential steps derived from code inspection:

  1. The user grants an immersive VR session with the WebXR layers feature.
  2. The webpage creates a WebGL2 context ({xrCompatible: true}) and an XRWebGLBinding instance.
  3. The page calls binding.createProjectionLayer({textureType: 'texture-array'}) or binding.createCubeLayer(...).
  4. Within the frame callback, the page requests the sub-image, then enables rasterizer discard: gl.enable(gl.RASTERIZER_DISCARD).
  5. Upon frame completion, OnFrameEnd() is triggered. The draw call is discarded, leaving the uninitialized hardware buffer intact, which is then submitted and displayed on the headset.

Potential Impact

An attacker could potentially cause uninitialized VRAM content (such as stale allocations from other applications or browser tabs) to be rendered on the user’s headset display. Because there is no script-accessible readback path for these target textures, this is a visual-only information disclosure to the user and does not lead to direct data exfiltration by the webpage or memory corruption.

Suggested Fix

In both XRWebGLTextureArraySwapChain::OnFrameEnd() and XRWebGLCubemapSwapChain::OnFrameEnd():

  1. Check if webgl2() is true and explicitly disable GL_RASTERIZER_DISCARD prior to executing the copy draw call:
    if (webgl2()) {
      gl->Disable(GL_RASTERIZER_DISCARD);
    }
    
  2. At the end of the functions, restore the original GL_RASTERIZER_DISCARD state using the DrawingBuffer::Client interface:
    if (webgl2()) {
      client->DrawingBufferClientRestoreRasterizerDiscard();
    }
    

Evaluated with Chrome root at commit: 208ca3371d87589335b108c431b95a36d768dc47


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.

View on issue tracker