Medium chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized resource in WebGL
DescriptionUninitialized resource in WebGL
ComponentWebGL
Bug ClassUninitialized Memory
Tracker501644790
Fix commit4e6d3cc9d4ed (chromium/src) +7/-5
CISA KEVNot listed
CreditedGoogle
Disclosed2026-09-08

Changed Functions

FunctionChangeNotes
if
third_party/blink/renderer/modules/webgl/gl_string_query.h
modified

Files Changed

  • third_party/blink/renderer/modules/webgl/gl_string_query.h
From 4e6d3cc9d4edf19b2532d7c140a8e1bf26faef36 Mon Sep 17 00:00:00 2001
From: Brandon Jones <[email protected]>
Date: Thu, 30 Jul 2026 10:20:25 -0700
Subject: [PATCH] Avoid returning uninitialized memory from GLStringQuery

Replaces the existing DCHECK in GLStringQuery::Run() to ensure that
if the log function fails to return a string of the expected length
either an empty string or truncated string to fit the given length
is returned.

Fixed: 501644790
Change-Id: Ic0ee0fc3c238548d1957b12fb101e52c6af26447
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8165135
Reviewed-by: Kai Ninomiya <[email protected]>
Commit-Queue: Kai Ninomiya <[email protected]>
Auto-Submit: Brandon Jones <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1671193}
---

diff --git a/third_party/blink/renderer/modules/webgl/gl_string_query.h b/third_party/blink/renderer/modules/webgl/gl_string_query.h
index 14d1c6f..2a9020f9 100644
--- a/third_party/blink/renderer/modules/webgl/gl_string_query.h
+++ b/third_party/blink/renderer/modules/webgl/gl_string_query.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_GL_STRING_QUERY_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_GL_STRING_QUERY_H_
 
-#include "base/check_op.h"
 #include "base/memory/raw_ptr.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
@@ -72,15 +71,18 @@
   String Run(GLuint id) {
     GLint length = 0;
     Traits::LengthFunction(gl_, id, &length);
-    if (!length)
+    if (length <= 0) {
       return g_empty_string;
+    }
     GLsizei returned_length = 0;
     StringBuffer<LChar> log_buffer(length);
     Traits::LogFunction(gl_, id, length, &returned_length,
                         log_buffer.Span().data());
-    // The returnedLength excludes the null terminator. If this check wasn't
-    // true, then we'd need to tell the returned String the real length.
-    DCHECK_EQ(returned_length + 1, length);
+    // The returnedLength excludes the null terminator.
+    if (returned_length <= 0 || returned_length > length) {
+      return g_empty_string;
+    }
+    log_buffer.Shrink(returned_length);
     return String::Adopt(log_buffer);
   }
 
Loading diff…

Original Bug Report

reported by [email protected]

Uninitialized memory leak in WebGL GLStringQuery APIs

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.

Overview: A potential Time-of-Check to Time-of-Use (TOCTOU) race condition in Blink’s WebGL string query APIs allows uninitialized heap memory to be returned to JavaScript. If a GPU context loss occurs exactly between fetching a string’s length and its contents, the underlying buffer remains unwritten and is returned in full because validation relies on a debug-only DCHECK. This exposes sensitive renderer memory to an attacker.

Affected files:

  • third_party/blink/renderer/modules/webgl/gl_string_query.h
  • third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
  • third_party/blink/renderer/modules/webgl/webgl_debug_shaders.cc

Estimated timestamp from git blame: 2025-08-27

Summary

A potential uninitialized memory disclosure exists in the GLStringQuery::Run template used by several WebGL APIs (such as getShaderInfoLog). By intentionally inducing a GPU context loss at a precise moment, an attacker may force the API to return uninitialized memory allocated from the renderer’s BufferPartition heap.

Technical Details

The vulnerability resides in third_party/blink/renderer/modules/webgl/gl_string_query.h within the GLStringQuery::Run method. Retrieving an info log involves two synchronous round-trips to the GPU process:

  1. Length Retrieval: Traits::LengthFunction(gl_, id, &length); fetches the exact byte length needed for the string.
  2. Uninitialized Allocation: StringBuffer<LChar> log_buffer(length); is allocated. This class delegates to StringImpl::CreateUninitialized, which uses PartitionAlloc’s BufferPartition without zero-initialization. The memory contains whatever was previously freed there.
  3. Content Retrieval: Traits::LogFunction(gl_, id, length, &returned_length, log_buffer.Span().data()); fetches the actual string bytes.

A race condition exists if the GPU process crashes (context loss) exactly between step 1 and step 3.

When LogFunction (e.g., GLES2Implementation::GetShaderInfoLog) executes with a lost context, its underlying WaitForCmd() calls fail. As a result, it safely aborts the operation, leaving the user-provided destination buffer (log_buffer) completely untouched, and sets returned_length to 0.

However, in GLStringQuery::Run, the only check ensuring the buffer was successfully populated is:

DCHECK_EQ(returned_length + 1, length);

Because DCHECK macros are compiled out in Release builds, the execution silently proceeds to:

return String::Adopt(log_buffer);

This adopts the raw, uninitialized memory buffer into a DOMString of the original expected length. This string is then passed back to the attacker’s JavaScript.

Affected APIs

  • WebGLRenderingContext.getShaderInfoLog()
  • WebGLRenderingContext.getProgramInfoLog()
  • WEBGL_debug_shaders.getTranslatedShaderSource()

Potential Steps to Reproduce

Note: Our tooling agent does not run code, so these are potential steps an attacker would follow based on the static analysis.

  1. The attacker hosts a malicious page that initializes a WebGL context and compiles a shader with deliberate warnings (ensuring the info log has a non-zero length).
  2. In a separate Web Worker, the attacker repeatedly runs an extremely heavy WebGL workload designed to crash the GPU process (e.g., triggering a TDR or OOM).
  3. On the main thread, the attacker aggressively loops gl.getShaderInfoLog(shader).
  4. When the GPU crash aligns perfectly in the tiny window between the two synchronous IPC calls in GLStringQuery::Run, the function returns a string containing uninitialized renderer heap data.
  5. The attacker reads the JavaScript string to exfiltrate cross-origin data, such as recently freed URLs, script contents, or JSON responses.

Suggested Fix

The validation check should be enforced in release builds, or the buffer should be explicitly shrunk to the reported bytes written before adoption.

Changing DCHECK_EQ to CHECK_EQ is the simplest fix:

CHECK_EQ(returned_length + 1, length);

Alternatively, cleanly handle the failure by shrinking the buffer or returning an empty string if returned_length is 0:

if (returned_length == 0 && length > 1) {
  return g_empty_string;
}
log_buffer.Shrink(returned_length);
return String::Adopt(log_buffer);

Evaluated with Chrome root at commit: 096fc8fdbfacf2546485756d03f160a3d04fcc9b


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