Low chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized Use in GPU
DescriptionUninitialized Use in GPU
ComponentGPU
Bug ClassUninitialized Memory
Tracker498382925
Fix commit09708b952e81 (chromium/src) +7/-3
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-05

Files Changed

  • gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
From 09708b952e816617db04d5f42b7ca88f233ebe10 Mon Sep 17 00:00:00 2001
From: Corentin Wallez <[email protected]>
Date: Wed, 01 Apr 2026 09:06:53 -0700
Subject: [PATCH] gles2_cmd_decoder_passthrough: Fix incorrect offset for TF varying reflection

Bug: 498382925
Change-Id: Id1b066667b4c6cf0a67d7d4eb6c680757818f3c8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7719959
Auto-Submit: Corentin Wallez <[email protected]>
Reviewed-by: Geoff Lang <[email protected]>
Commit-Queue: Geoff Lang <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1608576}
---

diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index 2c8508d..6d640db0 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -4319,8 +4319,9 @@
   api()->glGetProgramivFn(service_program, GL_TRANSFORM_FEEDBACK_VARYINGS,
                           &num_transform_feedback_varyings);
 
-  // Resize the data to fit the headers and info objects so that strings can be
-  // appended.
+  // Resize the data to fit the headers and info objects. Strings will be
+  // appended at the end of the buffer and info objects will point to the
+  // strings using their offset in the buffer.
   const base::CheckedNumeric<size_t> buffer_header_size(
       sizeof(TransformFeedbackVaryingsHeader));
   const base::CheckedNumeric<size_t> buffer_block_size(
@@ -4357,12 +4358,15 @@
     varying_info.size = size;
     varying_info.type = type;
 
+    // Add the string at the end and make the info object point at it using its
+    // offset.
     DCHECK(length + 1 <= max_transform_feedback_varying_length);
-    varying_info.name_length = data->size();
+    varying_info.name_offset = data->size();
     varying_info.name_length = length + 1;
     AppendStringToBuffer(data, transform_feedback_varying_name_buf.data(),
                          length + 1);
 
+    // Put the header info in the previously reserved space in the buffer.
     InsertValueIntoBuffer(
         data, varying_info,
         (buffer_header_size +
Loading diff…

Original Bug Report

reported by [email protected]

GPU stack memory leak in DoGetTransformFeedbackVaryingsCHROMIUM

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: A copy-paste typo in the GLES2 passthrough decoder leaves the name_offset field of the TransformFeedbackVaryingInfo struct uninitialized. This struct is subsequently copied to a command buffer bucket returned to the renderer. On Android official builds where stack variables are not auto-initialized, this potentially leaks 4 bytes of GPU process stack memory per varying, providing an ASLR bypass primitive.

Affected files:

  • gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc

Estimated timestamp from git blame: 2017-03-24

Description

In the GPU process’s passthrough command buffer decoder, there is a copy-paste typo when populating transform feedback varying information.

In gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc, the function GLES2DecoderPassthroughImpl::DoGetTransformFeedbackVaryingsCHROMIUM iterates through a program’s varyings and populates a stack-allocated TransformFeedbackVaryingInfo struct:

    TransformFeedbackVaryingInfo varying_info;
    varying_info.size = size;
    varying_info.type = type;

    DCHECK(length + 1 <= max_transform_feedback_varying_length);
    varying_info.name_length = data->size();  // <-- TYPO HERE
    varying_info.name_length = length + 1;

The line varying_info.name_length = data->size(); was clearly intended to be varying_info.name_offset = data->size();. Because it incorrectly assigns to name_length (which is immediately overwritten on the next line), the 4-byte name_offset field of the 16-byte struct is never initialized.

The uninitialized struct is then copied into the response vector data via InsertValueIntoBuffer, which uses memcpy. Finally, the vector is placed into a command buffer bucket and returned to the renderer.

On Android official builds, the compiler flag init_stack_vars is set to false (in build/config/compiler/BUILD.gn) to optimize binary size and performance. As a result, the uninitialized name_offset field will contain 4 bytes of leftover data from the GPU process’s stack. Leaking this data to a compromised renderer provides a reliable ASLR bypass primitive against the GPU process.

Potential Attacker Steps

Note: These are suggested steps based on static analysis, as our tooling agent does not yet have the ability to run code to verify a full exploit chain.

  1. Compromise the Renderer: The attacker first gains arbitrary code execution in the sandboxed renderer process.
  2. Context Creation: The attacker establishes a WebGL2 or OpenGL ES 3.0 context.
  3. Program Setup: The attacker compiles and links a shader program containing one or more transform feedback varyings.
  4. Command Execution: The attacker crafts and sends the GetTransformFeedbackVaryingsCHROMIUM command via the GPU command buffer, providing the valid program ID and a target bucket ID.
  5. Data Retrieval: The attacker issues GetBucketStart / GetBucketData commands to read the populated bucket from shared memory.
  6. Information Leak: The attacker parses the returned binary structure and extracts the 4 bytes at the name_offset position for each varying. These 4 bytes contain leaked stack memory from the GPU process, which can be used to defeat ASLR.

Proposed Fix

Correct the typo by assigning the offset to the correct field. Additionally, explicitly zero-initialize the struct at declaration to protect against any future changes to the struct definition.

    TransformFeedbackVaryingInfo varying_info = {}; // Explicitly zero-initialize
    varying_info.size = size;
    varying_info.type = type;

    DCHECK(length + 1 <= max_transform_feedback_varying_length);
    varying_info.name_offset = data->size();        // Fix typo
    varying_info.name_length = length + 1;

Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33


Results from 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