CVE-2026-17968
Overview
Files Changed
third_party/blink/renderer/modules/xr/xr_webgl_cubemap_swap_chain.ccthird_party/blink/renderer/modules/xr/xr_webgl_texture_array_swap_chain.cc
Patch
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();
Original Bug Report
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.ccthird_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:
- The user grants an immersive VR session with the WebXR
layersfeature. - The webpage creates a WebGL2 context (
{xrCompatible: true}) and anXRWebGLBindinginstance. - The page calls
binding.createProjectionLayer({textureType: 'texture-array'})orbinding.createCubeLayer(...). - Within the frame callback, the page requests the sub-image, then enables rasterizer discard:
gl.enable(gl.RASTERIZER_DISCARD). - 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():
- Check if
webgl2()is true and explicitly disableGL_RASTERIZER_DISCARDprior to executing the copy draw call:if (webgl2()) { gl->Disable(GL_RASTERIZER_DISCARD); } - At the end of the functions, restore the original
GL_RASTERIZER_DISCARDstate using theDrawingBuffer::Clientinterface: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.