CVE-2026-18001
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc |
modified |
Files Changed
third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
Patch
From d886618e97076917bb0acfe1316f64f60ad90f30 Mon Sep 17 00:00:00 2001 From: Ken Russell <[email protected]> Date: Tue, 09 Jun 2026 23:08:26 -0700 Subject: [PATCH] Add missing PBO checks to a couple of texSubImage2D overloads. Tested with new WebGL conformance test being added in https://github.com/KhronosGroup/WebGL/pull/3775 . Co-authored with jetski-cli. Fixed: 521757779 Change-Id: I2df1c84c4d76358d68a16e044f62c6b37edb8437 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7916452 Auto-Submit: Kenneth Russell <[email protected]> Commit-Queue: Kai Ninomiya <[email protected]> Reviewed-by: Kai Ninomiya <[email protected]> Cr-Commit-Position: refs/heads/main@{#1644453} --- diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc index 2ccc970..51568c0 100644 --- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc +++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc @@ -1776,6 +1776,14 @@ GLenum type, HTMLVideoElement* video, ExceptionState& exception_state) { + if (isContextLost()) { + return; + } + if (bound_pixel_unpack_buffer_) { + SynthesizeGLError(GL_INVALID_OPERATION, "texSubImage2D", + "a buffer is bound to PIXEL_UNPACK_BUFFER"); + return; + } WebGLRenderingContextBase::texSubImage2D(script_state, target, level, xoffset, yoffset, format, type, video, exception_state); @@ -1791,6 +1799,14 @@ GLenum type, VideoFrame* frame, ExceptionState& exception_state) { + if (isContextLost()) { + return; + } + if (bound_pixel_unpack_buffer_) { + SynthesizeGLError(GL_INVALID_OPERATION, "texSubImage2D", + "a buffer is bound to PIXEL_UNPACK_BUFFER"); + return; + } WebGLRenderingContextBase::texSubImage2D(script_state, target, level, xoffset, yoffset, format, type, frame, exception_state);
Original Bug Report
WebGL2: Potential raw renderer heap address leak in texSubImage2D overloads with bound PBO
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: WebGL2RenderingContextBase contains two potential 7-argument texSubImage2D overloads that accept HTMLVideoElement and VideoFrame but omit checking if a Pixel Unpack Buffer (PBO) is bound. If a PBO is bound, a raw renderer-process heap address pointer is passed to the client-side GLES2 implementation, which truncates the pointer and converts it to a PBO byte offset. An attacker can potentially exploit this behavior to leak the lower 32 bits of a raw renderer heap address, bypassing ASLR.
Affected files:
third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
Estimated timestamp from git blame: 2016-03-03
Vulnerability Analysis
Root Cause
In third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc (around lines 1769-1797), there are two 7-argument texSubImage2D overloads designed for WebGL1-compatibility that accept HTMLVideoElement and VideoFrame.
Unlike their sibling DOM-source overloads in the same file (such as those for ImageData at L1716, HTMLImageElement at L1737, CanvasRenderingContextHost at L1759, and ImageBitmap at L1810), these two overloads forward execution to the base class WebGLRenderingContextBase without performing a check on bound_pixel_unpack_buffer_ or calling isContextLost():
void WebGL2RenderingContextBase::texSubImage2D(
ScriptState* script_state,
GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLenum format,
GLenum type,
HTMLVideoElement* video,
ExceptionState& exception_state) {
WebGLRenderingContextBase::texSubImage2D(script_state, target, level, xoffset,
yoffset, format, type, video,
exception_state);
}
Because the base class WebGLRenderingContextBase implements WebGL1, it has no knowledge of WebGL2 state such as Pixel Unpack Buffers (PBOs) and cannot check bound_pixel_unpack_buffer_ itself.
Technical Flow and Pointer Coercion
When an attacker calls this 7-argument texSubImage2D overload with a bound PBO, the following potential path is executed:
- Execution reaches
WebGLRenderingContextBase::texSubImage2D(inwebgl_rendering_context_base.cc), which dispatches toTexImageHelperHTMLVideoElementorTexImageHelperVideoFrame. - When uploading a software/CPU-backed image or frame, execution routes to
TexImageSkImagewhere the pixel buffer backing store address is extracted as a raw renderer heap pointer (e.g., viapixmap.addr()orimage_conversion_data.data()). This raw pointer is stored ingl_dataand passed toTexImageBase. TexImageBaseinvokes the GLES2 client-side libraryContextGL()->TexSubImage2D(..., gl_data).- In
GLES2Implementation::TexSubImage2D(gpu/command_buffer/client/gles2_implementation.cc), becausebound_pixel_unpack_buffer_is bound, the function treats the pointer argument as a buffer offset rather than client-side memory. It performs pointer-to-integer conversion viaToGLuint(pixels):On 64-bit platforms, this truncates the 64-bit raw renderer heap pointer to its lower 32 bits, and passes it to the GPU command helper as a 32-bit unsigned offset.inline uint32_t ToGLuint(const void* ptr) { return static_cast<uint32_t>(reinterpret_cast<size_t>(ptr)); } - The service-side GPU interpreter (or ANGLE) reads from the bound PBO at this truncated address offset.
Potential Attack/Exploitation Steps
(Note: These are suggested/potential steps as our tooling agent does not have the capability to run code to confirm)
- Obtain a WebGL2 rendering context.
- Create a CPU-backed software image source, such as a 1x1
VideoFramefrom a standard canvas or a<video>element. - Create and bind a Pixel Unpack Buffer (PBO) filled with a known sequence pattern (e.g.,
data[i] = i). - With the PBO bound, invoke the 7-argument overload
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, video_or_frame). - The pointer to the temporary image data in the renderer heap is truncated to its lower 32 bits, and used as the PBO offset.
- Read back the uploaded texture to a framebuffer. The texels uploaded will contain the data from the PBO located at the byte offset corresponding to the lower 32 bits of the heap address, allowing full recovery of the low 32 bits.
- Alternatively, if the PBO is smaller than the truncated address, the driver generates
GL_INVALID_OPERATION. The attacker can binary-search the exact offset by varying the PBO buffer size.
This provides a highly reliable renderer-process ASLR bypass / information leak oracle.
Suggested Fix
Add the standard isContextLost() and bound_pixel_unpack_buffer_ checks to both of the 7-argument WebGL2 overloads in third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc before forwarding to the base class:
void WebGL2RenderingContextBase::texSubImage2D(
ScriptState* script_state,
GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLenum format,
GLenum type,
HTMLVideoElement* video,
ExceptionState& exception_state) {
if (isContextLost())
return;
if (bound_pixel_unpack_buffer_) {
SynthesizeGLError(GL_INVALID_OPERATION, "texSubImage2D",
"a buffer is bound to PIXEL_UNPACK_BUFFER");
return;
}
WebGLRenderingContextBase::texSubImage2D(script_state, target, level, xoffset,
yoffset, format, type, video,
exception_state);
}
Do the equivalent for the VideoFrame overload at line 1784.
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.