Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds write in GPU
DescriptionOut of bounds write in GPU
ComponentGPU
Bug ClassOOB
Tracker511710468
Fix commit84ea877fa571 (chromium/src) +9/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-27

Files Changed

  • gpu/command_buffer/service/gles2_cmd_decoder.cc
From 84ea877fa571d6659cdc1555eec2802c96c988ea Mon Sep 17 00:00:00 2001
From: Ken Russell <[email protected]>
Date: Mon, 11 May 2026 19:23:57 -0700
Subject: [PATCH] Check currently used program in DoResumeTransformFeedback.

Avoid state mismatches between the validating command decoder and the
underlying OpenGL [ES] driver.

Fixed: 511710468
Change-Id: Id35c507ea2dcff457d5e098d0cb829aa47c54acc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7838123
Commit-Queue: Brandon Jones <[email protected]>
Auto-Submit: Kenneth Russell <[email protected]>
Commit-Queue: Kenneth Russell <[email protected]>
Reviewed-by: Brandon Jones <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1628974}
---

diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index bea723b..b1146584 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -5494,6 +5494,15 @@
                        "transform feedback is not active or not paused");
     return;
   }
+
+  if (state_.current_program.get() !=
+      state_.bound_transform_feedback->active_program()) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glResumeTransformFeedback",
+                       "current program does not match the program active when "
+                       "transform feedback began");
+    return;
+  }
+
   state_.bound_transform_feedback->DoResumeTransformFeedback();
 }
 
Loading diff…

Original Bug Report

reported by [email protected]

Potential GPU OOB Write via Missing Program Validation in ResumeTransformFeedback

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

Overview: The GLES2 command decoder fails to verify that the current program matches the program active when transform feedback was started during glResumeTransformFeedback. An attacker can exploit this by switching to a program with smaller varyings while paused, bypassing command buffer validation and causing the GPU to write out-of-bounds. This could lead to a sandbox escape on platforms with unsandboxed GPU processes like Android.

Affected files:

  • gpu/command_buffer/service/gles2_cmd_decoder.cc
  • gpu/command_buffer/service/transform_feedback_manager.h
  • gpu/command_buffer/service/transform_feedback_manager.cc
  • gpu/command_buffer/service/buffer_manager.cc

Estimated timestamp from git blame: 2018-12-05

Summary

There is a potential out-of-bounds write vulnerability in the GLES2 validating command decoder related to transform feedback. The decoder fails to validate that the currently active program matches the program that was active when transform feedback began when glResumeTransformFeedback is called. This allows a compromised renderer to bypass transform feedback buffer size validation, potentially leading to arbitrary memory corruption in the GPU process. On platforms where the GPU process is unsandboxed (such as Android), this could result in a full sandbox escape.

Technical Details

According to the OpenGL ES 3.0 specification, glResumeTransformFeedback must generate a GL_INVALID_OPERATION error if the program object being used by the current rendering state is not the same program that was active when glBeginTransformFeedback was called.

While Blink’s WebGL layer implements this check, the underlying GLES2 command decoder (gpu/command_buffer/service/gles2_cmd_decoder.cc) does not. A compromised renderer can send raw command buffer commands to bypass Blink’s checks and reach the vulnerable decoder logic.

The vulnerability manifests through the following sequence:

  1. Missing Validation: In GLES2DecoderImpl::DoResumeTransformFeedback, the code only verifies that transform feedback is active and paused. It fails to check if state_.current_program matches state_.bound_transform_feedback->active_program().
  2. State Desync: While transform feedback is paused, the attacker switches to a different program. When they resume, the decoder sets paused_ = false and forwards the command to the driver. On non-conformant drivers (common on Android), the driver accepts the command but remains locked to the output configuration of the original program.
  3. Bypassed Validation: When a draw call is issued, GLES2DecoderImpl::CheckTransformFeedback validates the bound transform feedback buffers. However, it uses the varying sizes from the currently active program (state_.current_program->GetTransformFeedbackVaryingSizes()).

By starting transform feedback with a program having large varyings (Program A), pausing, and switching to a program with small or no varyings (Program B), an attacker can supply very small buffers that pass the decoder’s validation (which checks against Program B). When the draw call reaches the GPU, the driver outputs Program A’s large varyings into the small buffers, resulting in an out-of-bounds write.

Potential Exploitation Steps

Note: These are potential steps based on source code analysis; a full Proof of Concept has not been executed.

  1. A compromised renderer crafts two programs: Program A (large/many transform feedback varyings) and Program B (small/no varyings).
  2. The attacker allocates small GPU buffers sized for Program B.
  3. The attacker calls glUseProgram(Program A).
  4. The attacker calls glBeginTransformFeedback(). The decoder records Program A as the active program for transform feedback.
  5. The attacker calls glPauseTransformFeedback().
  6. The attacker calls glUseProgram(Program B). The decoder allows this because transform feedback is paused.
  7. The attacker calls glResumeTransformFeedback(). The decoder fails to reject this despite the program mismatch.
  8. The attacker issues a draw call (e.g., glDrawArrays).
  9. The decoder validates the small buffers against Program B’s requirements, which succeeds.
  10. The native GPU driver executes the draw call, outputting Program A’s large varyings into the small buffers, overwriting adjacent GPU memory.

Suggested Fix

Update GLES2DecoderImpl::DoResumeTransformFeedback in gpu/command_buffer/service/gles2_cmd_decoder.cc to enforce the program match requirement:

void GLES2DecoderImpl::DoResumeTransformFeedback() {
  DCHECK(state_.bound_transform_feedback.get());
  if (!state_.bound_transform_feedback->active() ||
      !state_.bound_transform_feedback->paused()) {
    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glResumeTransformFeedback",
                       "transform feedback is not active or not paused");
    return;
  }

  if (state_.current_program.get() != 
      state_.bound_transform_feedback->active_program()) {
    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glResumeTransformFeedback",
                       "current program does not match the program active when "
                       "transform feedback began");
    return;
  }

  state_.bound_transform_feedback->DoResumeTransformFeedback();
}

Evaluated with Chrome root at commit: eca8648a4e1cdfdda68c495a6003059fed641955


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.

View on issue tracker