High watch CVSS 4.7 chrome Uninitialized Memory 🔧 Commit mapped

Overview

High
Severity
4.7
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized resource in GPU
DescriptionUninitialized resource in GPU
ComponentGPU
Bug ClassUninitialized Memory
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker550953039
Fix commit5edc4d1363bf (chromium/src) +18/-2
CWECWE-908
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N
CISA KEVNot listed
CreditedTienPA - NGS Holdings
Reported2026-08-22 (per vendor advisory)
Fix landed2026-09-03 (public git)
Disclosed2026-09-22
Patch gap19 days — fix public in source before the advisory

Background

`GLTextureHolder`
GPU-process shared-image helper that owns a GL texture and handles CPU-side UploadFromMemory / ReadbackToMemory transfers via the GL API.
`glTexSubImage2D`
GL call that copies client pixel data into a texture’s storage; on failure it sets a GL error and may leave texture memory uninitialized or partially written.
`glGetError`
GL’s asynchronous error queue; errors accumulate until drained, so a stale error can be misattributed unless the queue is flushed before a checked call.
Uninitialized memory
texture backing whose contents were never validly written; if surfaced as pixels it can leak prior GPU/heap contents.

Root Cause Analysis

GLTextureHolder::UploadFromMemory and ReadbackToMemory invoked glTexSubImage2D (and the readback path) and then returned true unconditionally, without checking whether the GL storage call succeeded. If the driver rejected the call and set a GL error, the texture (or readback buffer) could be left with uninitialized or partially written contents, yet the method reported success to its caller. Because GL errors are queued and only cleared by glGetError, a reliable post-call check also requires first draining any pre-existing errors so a later check is attributable to the storage call itself. Initialize() already performed this drain-and-check pattern, but the upload and readback paths did not, creating an inconsistency.

The fix adds while (api->glGetErrorFn() != GL_NO_ERROR) {} before the storage call and returns api->glGetErrorFn() == GL_NO_ERROR afterward. A false return now signals failure so callers do not treat uninitialized texture memory as valid content.

Key insight
GL’s deferred error model means an unconditional return true after glTexSubImage2D silently masks storage failures; only a drain-then-check bracket, as Initialize() already did, makes a failed upload/readback observable to the caller.

Attack Path

  1. Trigger a failing transfer Drive a shared-image upload/readback whose parameters cause glTexSubImage2D (or the readback) to fail in the GPU process.
  2. Storage left uninitialized The failed call leaves the texture or readback buffer with uninitialized or partially written memory while setting a GL error.
  3. Success is reported anyway The old code returns true unconditionally, so the caller treats the incomplete storage as valid content.
  4. Surface the memory Cause the shared image to be sampled, composited, or read back, exposing the uninitialized bytes as pixel data.

Impact Assessment

An attacker could cause the GPU process to treat uninitialized or partially initialized texture memory as valid image content, potentially disclosing residual GPU/heap contents through rendered or read-back pixels. This is an information-disclosure / uninitialized-resource issue in the GPU component rather than a direct memory-corruption bug. Severity is bounded by the attacker’s ability to reliably force the underlying GL storage call to fail.

Files Changed

  • gpu/command_buffer/service/shared_image/gl_texture_holder.cc

Audit Directions

  • Unchecked GL storage calls
    Search other shared-image and texture backings for glTexImage2D/glTexSubImage2D/glReadPixels sites followed by unconditional return true, and flag any lacking a post-call glGetError check.
  • Missing error drain
    Where a glGetError check exists but no preceding drain loop, the check may be attributing stale errors; verify each checked call brackets it with a drain like Initialize() does.
  • Success without initialization proof
    Audit callers that treat a true/success return as a guarantee of fully written texture or buffer memory, ensuring they handle a newly possible false and never sample uninitialized resources.
From 5edc4d1363bf6bf79ada91fa7ea709e0768f6f26 Mon Sep 17 00:00:00 2001
From: Brandon Jones <[email protected]>
Date: Wed, 02 Sep 2026 17:37:15 -0700
Subject: [PATCH] More GL error checks in GLTextureHolder

Flushes any existing GL errors and checks for new ones in
GLTextureHolder::UploadFromMemory and GLTextureHolder::ReadbackToMemory.
Mirrors the check done in Initialize().

Fixed: 550953039
Change-Id: I0073a2a1b9462e8501ec8b2fd9d70ca2500215c8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8340268
Reviewed-by: Kai Ninomiya <[email protected]>
Auto-Submit: Brandon Jones <[email protected]>
Commit-Queue: Kai Ninomiya <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1691225}
---
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_holder.cc b/gpu/command_buffer/service/shared_image/gl_texture_holder.cc
index 32faf38..d7da0e9 100644
--- a/gpu/command_buffer/service/shared_image/gl_texture_holder.cc
+++ b/gpu/command_buffer/service/shared_image/gl_texture_holder.cc
@@ -345,13 +345,21 @@
   const void* pixels =
       !repacked_data.empty() ? repacked_data.data() : pixmap.addr();
   gl::GLApi* api = gl::g_current_gl_context;
+
+  // Drain any pre-existing GL errors so the post-allocation check below is
+  // attributable to the storage call. Mirrors other allocation checks done in
+  // the command decoder.
+  while (api->glGetErrorFn() != GL_NO_ERROR) {
+  }
+
   {
     gl::ScopedProgressReporter scoped_progress_reporter(progress_reporter_);
     api->glTexSubImage2DFn(gl_target, /*level=*/0, 0, 0, size_.width(),
                            size_.height(), gl_format, gl_type, pixels);
   }
 
-  return true;
+  // Report any failures
+  return api->glGetErrorFn() == GL_NO_ERROR;
 }
 
 bool GLTextureHolder::ReadbackToMemory(const SkPixmap& pixmap) {
@@ -385,6 +393,13 @@
   }
 
   gl::GLApi* api = gl::g_current_gl_context;
+
+  // Drain any pre-existing GL errors so the post-allocation check below is
+  // attributable to the storage call. Mirrors other allocation checks done in
+  // the command decoder.
+  while (api->glGetErrorFn() != GL_NO_ERROR) {
+  }
+
   // ScopedGLFramebuffer must be declared before ScopedFramebufferBinder
   // so that when this scope exits, ScopedFramebufferBinder is destroyed first
   // (restoring the previous framebuffer binding) before the temporary FBO is
@@ -481,7 +496,8 @@
     SwizzleRedAndBlue(pixmap);
   }
 
-  return true;
+  // Report any failures
+  return api->glGetErrorFn() == GL_NO_ERROR;
 }
 
 sk_sp<GrPromiseImageTexture> GLTextureHolder::GetPromiseImage(
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.