CVE-2026-17869
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ScopedUnpackParametersResetRestorethird_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc |
modified | |
ScopedDisableRasterizerDiscardthird_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc |
modified | |
ScopedUnpackParametersResetRestorethird_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h |
modified | |
ifthird_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h |
modified |
Files Changed
third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.ccthird_party/blink/renderer/modules/webgl/webgl_rendering_context_base.hthird_party/blink/renderer/modules/xr/xr_cube_map.cc
Patch
From 50fe292d369dfc5b3017ff421896e2f3d0ea4ee9 Mon Sep 17 00:00:00 2001
From: Alexander Cooper <alcooper@chromium.org>
Date: Tue, 09 Jun 2026 11:38:42 -0700
Subject: [PATCH] Reset WebGL unpack parameters in XRCubeMap.
Ensure that unpack parameters are temporarily reset to their default
values before uploading CPU-backed vectors to the WebGL context
in XRCubeMap::updateWebGLEnvironmentCube. This prevents the GLES2
client from using stale pixel-store unpack parameters (like row length)
which could lead to incorrect data transfer sizes.
Fixed: 521759269
Change-Id: I7663ece2d54af96d358096de7df3b6ea8a9bb3ee
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7914862
Commit-Queue: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Brandon Jones <bajones@chromium.org>
Auto-Submit: Alexander Cooper <alcooper@chromium.org>
Reviewed-by: Brandon Jones <bajones@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1644133}
---
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index ceef9fac1..f0d6cd6 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -664,27 +664,6 @@
WebGLRenderingContextBase* context_;
};
-class ScopedUnpackParametersResetRestore {
- STACK_ALLOCATED();
-
- public:
- explicit ScopedUnpackParametersResetRestore(
- WebGLRenderingContextBase* context,
- bool enabled = true)
- : context_(context), enabled_(enabled) {
- if (enabled)
- context_->ResetUnpackParameters();
- }
-
- ~ScopedUnpackParametersResetRestore() {
- if (enabled_)
- context_->RestoreUnpackParameters();
- }
-
- private:
- WebGLRenderingContextBase* context_;
- bool enabled_;
-};
class ScopedDisableRasterizerDiscard {
STACK_ALLOCATED();
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
index 403c91eb..6257ff0 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
@@ -2056,6 +2056,30 @@
}
};
+class ScopedUnpackParametersResetRestore {
+ STACK_ALLOCATED();
+
+ public:
+ explicit ScopedUnpackParametersResetRestore(
+ WebGLRenderingContextBase* context,
+ bool enabled = true)
+ : context_(context), enabled_(enabled) {
+ if (enabled) {
+ context_->ResetUnpackParameters();
+ }
+ }
+
+ ~ScopedUnpackParametersResetRestore() {
+ if (enabled_) {
+ context_->RestoreUnpackParameters();
+ }
+ }
+
+ private:
+ WebGLRenderingContextBase* context_;
+ bool enabled_;
+};
+
} // namespace blink
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
diff --git a/third_party/blink/renderer/modules/xr/xr_cube_map.cc b/third_party/blink/renderer/modules/xr/xr_cube_map.cc
index ba9e4991..d13bc79e 100644
--- a/third_party/blink/renderer/modules/xr/xr_cube_map.cc
+++ b/third_party/blink/renderer/modules/xr/xr_cube_map.cc
@@ -96,6 +96,10 @@
DCHECK(!texture->HasEverBeenBound() ||
texture->GetTarget() == GL_TEXTURE_CUBE_MAP);
+ // Enforce a temporary reset of unpack parameters to avoid stale WebGL state
+ // over-reading CPU vectors.
+ ScopedUnpackParametersResetRestore unpack_params(context);
+
auto* gl = context->ContextGL();
texture->SetTarget(GL_TEXTURE_CUBE_MAP);
gl->BindTexture(GL_TEXTURE_CUBE_MAP, texture->Object());
Original Bug Report
OOB Heap Read in XRCubeMap::updateWebGLEnvironmentCube via Stale WebGL Unpack State
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: An out-of-bounds heap read potentially exists in XRCubeMap::updateWebGLEnvironmentCube when updating WebGL environment cube maps. The function uploads tightly-packed CPU-backed vectors to the page’s WebGL context without resetting stale pixel-store unpack parameters such as UNPACK_ROW_LENGTH. A web page could potentially pre-configure these parameters to cause the GLES2 client to over-read the allocated vector buffers into a JS-readable WebGL texture.
Affected files:
third_party/blink/renderer/modules/xr/xr_cube_map.ccthird_party/blink/renderer/modules/xr/xr_webgl_binding.cc
Estimated timestamp from git blame: 2020-01-16
Root Cause Analysis
In XRCubeMap::updateWebGLEnvironmentCube (located in third_party/blink/renderer/modules/xr/xr_cube_map.cc), raw pointers to tightly packed CPU buffers (Vector<uint16_t> or converted Vector<uint8_t>) are uploaded directly to the page’s WebGL context via gl->TexImage2D() without resetting the pixel-store unpack state.
If the calling page has previously configured ES3/WebGL2 unpack parameters (such as GL_UNPACK_ROW_LENGTH, GL_UNPACK_SKIP_ROWS, or GL_UNPACK_SKIP_PIXELS) using gl.pixelStorei(), these parameters remain active on the ContextGL(). When TexImage2D is invoked on the client-side command buffer wrapper (GLES2Implementation), the GLES2 client uses these parameters to compute the expected size of the input pointer:
PixelStoreParams params = GetUnpackParameters(k2D);
GLES2Util::ComputeImageDataSizesES3(width, height, 1, format, type, params, ...);
If UNPACK_ROW_LENGTH is configured to a value significantly larger than the texture width, the GLES2 client constructs an inflated base::span over the source pointer. During the pixel copy loop in CopyRectToBuffer, the client strides through the source pointer using the inflated row length, copying out-of-bounds heap memory into the transfer buffer used to upload the texture data.
Potential Trigger Steps
Note: These steps are theoretical/potential as our tooling does not currently run code to verify live exploits.
- A page initializes an immersive-ar WebXR session with the
light-estimationfeature enabled. - The page initializes a WebGL2 rendering context with
xrCompatible: trueand configuresgl.pixelStorei(gl.UNPACK_ROW_LENGTH, 4096). - An
XRWebGLBindingis created for the session. - When a
reflectionchangeevent occurs, the page callsbinding.getReflectionCubeMap(lightProbe). - During
updateWebGLEnvironmentCube, the GLES2 client processes the raw tightly packed buffer of size16x16under the assumption of a row stride of4096pixels. This results in an out-of-bounds read of the heap adjacent to the vector allocation. - The page binds the returned WebGL cube-map texture, attaches its faces to a framebuffer, and uses
readPixelsor shader sampling to read back the leaked heap memory.
Suggested Fix
To resolve this issue, Blink should ensure that the unpack parameters are temporarily reset to their default values prior to invoking raw-pointer uploads in the WebXR pipeline. This can be accomplished by instantiating a ScopedUnpackParametersResetRestore helper class on the rendering context inside XRCubeMap::updateWebGLEnvironmentCube before issuing the TexImage2D calls:
// Enforce a temporary reset of unpack parameters to avoid stale WebGL state over-reading CPU vectors
ScopedUnpackParametersResetRestore unpack_params(context);
This pattern matches the mitigation strategy employed in other Blink WebGL texture upload paths.
Evaluated with Chrome root at commit: 3947e01999a53d4e2382e39736cb79d79c7dffcf
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.