CVE-2026-11045
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifgpu/command_buffer/client/gles2_cmd_helper_autogen.h |
modified |
Files Changed
gpu/GLES2/extensions/CHROMIUM/CHROMIUM_enable_feature.txtgpu/GLES2/gl2chromium_autogen.hgpu/GLES2/gl2extchromium.hgpu/command_buffer/build_gles2_cmd_buffer.pygpu/command_buffer/client/gles2_c_lib_autogen.hgpu/command_buffer/client/gles2_cmd_helper_autogen.hgpu/command_buffer/client/gles2_implementation.cc
Patch
From 91c469d813f74136b9eff1861c8ffdbbbc30fe48 Mon Sep 17 00:00:00 2001
From: Gregg Tavares <gman@chromium.org>
Date: Thu, 09 Apr 2026 11:00:16 -0700
Subject: [PATCH] Remove Pepper3D support from gles2_cmd_decoder.cc
Pepper3D is long gone. Pepper3D provided a full implementation
of OpenGL ES 2.0 where as WebGL provides a slightly smaller
subset. We no longer need those extra features so remove them.
Bug: 498727111
Fixed: 498727111
Change-Id: I33d5aeb535389058955562a35721c619b19b6198
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7738780
Commit-Queue: Brandon Jones <bajones@chromium.org>
Auto-Submit: Gregg Tavares <gman@chromium.org>
Reviewed-by: Brandon Jones <bajones@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1612363}
---
diff --git a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_enable_feature.txt b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_enable_feature.txt
deleted file mode 100644
index 100cf0c..0000000
--- a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_enable_feature.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-Name
-
- CHROMIUM_enable_feature
-
-Name Strings
-
- GL_CHROMIUM_enable_feature
-
-Version
-
- Last Modifed Date: August 11, 2011
-
-Dependencies
-
- OpenGL ES 2.0 is required.
-
-Overview
-
- This extension allows certain optional features to be turned on in
- the Chromium OpenGL ES 2.0 implementation.
-
-Issues
-
-
-New Tokens
-
- None
-
-New Procedures and Functions
-
- CommandBufferEnableCHROMIUM(const GLchar *feature)
-
- <feature> is a null terminated string of the feature you wish to enable.
- The requestable feature strings are as follows:
-
- Note: If used this function MUST be called before any other GL commands
- except GetString. Calling any other functions first will result in
- undefined behavior.
-
- pepper3d_allow_buffers_on_mulitple_targets
-
- OpenGL ES 2.0 allows a single buffer to be bound to both the ARRAY_BUFFER
- and ELEMENT_ARRAY_BUFFER bind points. By default Chromium does not allow
- this.
-
- This feature is only provided for full OpenGL ES 2.0 compatibility. It is
- expensive to use and should generally be avoided.
-
- pepper3d_support_fixed_attribs
-
- Turns on support for the type GL_FIXED. By default this feature is off in
- Chromium.
-
- This feature is only provided for full OpenGL ES 2.0 compatibility. It is
- expensive to use and should generally be avoided.
-
-Errors
-
- None.
-
-New State
-
- None.
-
-Revision History
-
- 8/11/2011 Documented the extension
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h
index 8e1b498..18a6fa5e 100644
--- a/gpu/GLES2/gl2chromium_autogen.h
+++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -279,7 +279,6 @@
#define glBindVertexArrayOES GLES2_GET_FUN(BindVertexArrayOES)
#define glFramebufferParameteri GLES2_GET_FUN(FramebufferParameteri)
#define glGetMaxValueInBufferCHROMIUM GLES2_GET_FUN(GetMaxValueInBufferCHROMIUM)
-#define glEnableFeatureCHROMIUM GLES2_GET_FUN(EnableFeatureCHROMIUM)
#define glMapBufferCHROMIUM GLES2_GET_FUN(MapBufferCHROMIUM)
#define glUnmapBufferCHROMIUM GLES2_GET_FUN(UnmapBufferCHROMIUM)
#define glMapBufferSubDataCHROMIUM GLES2_GET_FUN(MapBufferSubDataCHROMIUM)
diff --git a/gpu/GLES2/gl2extchromium.h b/gpu/GLES2/gl2extchromium.h
index 7cad7de..0628039 100644
--- a/gpu/GLES2/gl2extchromium.h
+++ b/gpu/GLES2/gl2extchromium.h
@@ -612,16 +612,6 @@
#endif
#endif /* GL_ARB_texture_rectangle */
-/* GL_CHROMIUM_enable_feature */
-#ifndef GL_CHROMIUM_enable_feature
-#define GL_CHROMIUM_enable_feature 1
-#ifdef GL_GLEXT_PROTOTYPES
-GL_APICALL GLboolean GL_APIENTRY glEnableFeatureCHROMIUM(const char* feature);
-#endif
-typedef GLboolean (GL_APIENTRYP PFNGLENABLEFEATURECHROMIUMPROC) (
- const char* feature);
-#endif /* GL_CHROMIUM_enable_feature */
-
/* GL_ARB_robustness */
#ifndef GL_ARB_robustness
#define GL_ARB_robustness 1
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index 450d0ce..e6a366b 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -1815,15 +1815,6 @@
'type': 'StateSet',
'state': 'ClearStencil',
},
- 'EnableFeatureCHROMIUM': {
- 'type': 'Custom',
- 'data_transfer_methods': ['shm'],
- 'decoder_func': 'DoEnableFeatureCHROMIUM',
- 'cmd_args': 'GLuint bucket_id, GLint* result',
- 'result': ['GLint'],
- 'extension': 'GL_CHROMIUM_enable_feature',
- 'pepper_interface': 'ChromiumEnableFeature',
- },
'CompileShader': {'decoder_func': 'DoCompileShader', 'unit_test': False},
'CompressedTexImage2D': {
'type': 'Custom',
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h
index 503e5db..640a029 100644
--- a/gpu/command_buffer/client/gles2_c_lib_autogen.h
+++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -1283,9 +1283,6 @@
return gles2::GetGLContext()->GetMaxValueInBufferCHROMIUM(buffer_id, count,
type, offset);
}
-GLboolean GL_APIENTRY GLES2EnableFeatureCHROMIUM(const char* feature) {
- return gles2::GetGLContext()->EnableFeatureCHROMIUM(feature);
-}
void* GL_APIENTRY GLES2MapBufferCHROMIUM(GLuint target, GLenum access) {
return gles2::GetGLContext()->MapBufferCHROMIUM(target, access);
}
@@ -2770,10 +2767,6 @@
reinterpret_cast<GLES2FunctionPointer>(glGetMaxValueInBufferCHROMIUM),
},
{
- "glEnableFeatureCHROMIUM",
- reinterpret_cast<GLES2FunctionPointer>(glEnableFeatureCHROMIUM),
- },
- {
"glMapBufferCHROMIUM",
reinterpret_cast<GLES2FunctionPointer>(glMapBufferCHROMIUM),
},
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index 40957f9c..dcaa7e6b 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -2524,16 +2524,6 @@
}
}
-void EnableFeatureCHROMIUM(GLuint bucket_id,
- uint32_t result_shm_id,
- uint32_t result_shm_offset) {
- gles2::cmds::EnableFeatureCHROMIUM* c =
- GetCmdSpace<gles2::cmds::EnableFeatureCHROMIUM>();
- if (c) {
- c->Init(bucket_id, result_shm_id, result_shm_offset);
- }
-}
-
void MapBufferRange(GLenum target,
GLintptr offset,
GLsizeiptr size,
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index b5ab590..77214ff 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -5562,28 +5562,6 @@
return GL_NO_ERROR;
}
-GLboolean GLES2Implementation::EnableFeatureCHROMIUM(const char* feature) {
Regression Test / PoC
diff --git a/gpu/command_buffer/service/buffer_manager_unittest.cc b/gpu/command_buffer/service/buffer_manager_unittest.cc
index d78abca..1b02d28 100644
--- a/gpu/command_buffer/service/buffer_manager_unittest.cc
+++ b/gpu/command_buffer/service/buffer_manager_unittest.cc
@@ -505,7 +505,6 @@
}
TEST_F(BufferManagerTest, BindBufferConflicts) {
- manager_->set_allow_buffers_on_multiple_targets(false);
GLuint client_id = 1;
GLuint service_id = 101;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index abeb2a93..abab007 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -408,13 +408,6 @@
EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
}
-TEST_P(GLES2DecoderTest, EnableFeatureCHROMIUMBadBucket) {
- const uint32_t kBadBucketId = 123;
- cmds::EnableFeatureCHROMIUM cmd;
- cmd.Init(kBadBucketId, shared_memory_id_, shared_memory_offset_);
- EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
-}
-
TEST_P(GLES2DecoderTest, RequestExtensionCHROMIUMBadBucket) {
const uint32_t kBadBucketId = 123;
cmds::RequestExtensionCHROMIUM cmd;
Original Bug Report
Potential GPU OOB Read via BufferManager Target Bypass and Shadow Desync
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 security team.
Overview: A potential vulnerability in the GLES2 validating command decoder allows a compromised renderer to bypass index buffer validation. By exploiting an unguarded legacy feature flag, an attacker can desynchronize a buffer’s CPU shadow copy from its GPU memory via Transform Feedback. This can result in an out-of-bounds read of GPU memory during draw calls, potentially leaking sensitive cross-origin data.
Affected files:
gpu/command_buffer/service/buffer_manager.ccgpu/command_buffer/service/gles2_cmd_decoder.ccgpu/command_buffer/service/transform_feedback_manager.cc
Estimated timestamp from git blame: 2015-05-22
Background
In the GLES2 validating decoder, index buffer (GL_ELEMENT_ARRAY_BUFFER) validation relies on a CPU-side shadow copy (Buffer::shadow_). When glDrawElements is called, the decoder scans this shadow buffer to ensure all indices are within the bounds of the active vertex attributes. To prevent this shadow buffer from desynchronizing with the actual GPU memory, BufferManager::SetTarget strictly prohibits an ELEMENT_ARRAY_BUFFER from being bound to other targets that could modify it on the GPU, such as GL_TRANSFORM_FEEDBACK_BUFFER.
The Vulnerability
There is a flaw in GLES2DecoderImpl::HandleEnableFeatureCHROMIUM (gpu/command_buffer/service/gles2_cmd_decoder.cc). It unconditionally accepts the feature string pepper3d_allow_buffers_on_multiple_targets and sets allow_buffers_on_multiple_targets_ = true on the shared BufferManager. It performs no validation to ensure the requesting context is actually a Pepper3D plugin context.
Once this flag is enabled, BufferManager::SetTarget (gpu/command_buffer/service/buffer_manager.cc) skips the crucial target-exclusivity checks. This allows any standard WebGL or GLES2 context to bind an ELEMENT_ARRAY_BUFFER to a TRANSFORM_FEEDBACK_BUFFER. Because Transform Feedback operations modify buffer data exclusively on the GPU and do not update the CPU shadow_ copy, the two become desynchronized.
Potential Attacker Steps
(Note: These are suggested theoretical steps to trigger the vulnerability; our tooling agent does not have the ability to run a live Proof of Concept).
- Enable Feature: A compromised renderer sends the
EnableFeatureCHROMIUMcommand withpepper3d_allow_buffers_on_multiple_targets. - Setup Shadow Buffer: The attacker generates a buffer, binds it to
GL_ELEMENT_ARRAY_BUFFER, and populates it with small, “safe” indices viaglBufferData. The CPU validates these and stores them in theshadow_array. - Bypass Target Restrictions: The attacker binds this exact same buffer to
GL_TRANSFORM_FEEDBACK_BUFFER. - Desynchronize via Transform Feedback: The attacker executes a Transform Feedback draw using a crafted vertex shader that outputs massive, out-of-bounds index values. The GPU writes these directly to the buffer’s device memory. The CPU-side
shadow_copy remains completely unmodified and continues to hold the safe indices. - Trigger Validation Bypass: The attacker unbinds Transform Feedback, ensures the buffer is bound as
GL_ELEMENT_ARRAY_BUFFER, and callsglDrawElements. - OOB Read Execution: The validating decoder’s
CheckMultiDrawElementsVerticesscans the stale, safe CPUshadow_buffer and approves the draw call. The underlying graphics driver then natively executes the draw using the GPU-side buffer containing the massive, out-of-bounds indices. Becauserobust_buffer_accessdefaults tofalsefor standard contexts using the validating decoder, the driver fetches vertex attributes out-of-bounds.
By carefully configuring the shader pipeline to consume these out-of-bounds vertex attributes and render them to a framebuffer, the attacker can exfiltrate cross-origin GPU process memory via glReadPixels.
Suggested Fix
Restrict the pepper3d_allow_buffers_on_multiple_targets feature so it can only be enabled by legitimate Pepper3D contexts. For example, in GLES2DecoderImpl::HandleEnableFeatureCHROMIUM, verify the context type before modifying the BufferManager state:
if (feature_str.compare("pepper3d_allow_buffers_on_multiple_targets") == 0) {
if (context_type_ == CONTEXT_TYPE_OPENGLES2_FOR_UI || /* checking logic */) {
buffer_manager()->set_allow_buffers_on_multiple_targets(true);
}
}
Alternatively, if this feature is no longer needed by modern Chromium, entirely deprecate and remove the pepper3d_allow_buffers_on_multiple_targets bypass logic from BufferManager.
Evaluated with Chrome root at commit: e9e0fcbb690b1a8c1a26c81c2a9ea23d6e178368
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.