Low watch CVSS 4.3 chrome Uninitialized Memory 🔧 Commit mapped

Overview

Low
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized Use in GPU
DescriptionUninitialized Use in GPU
ComponentGPU
Sub-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.
Tracker517000034
Fix commit557967a618dc (chromium/src) +19/-0
CWECWE-457 (Use of uninitialized)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-05-27 (per vendor advisory)
Fix landed2026-06-08 (public git)
Disclosed2026-07-29
Patch gap51 days — fix public in source before the advisory

Background

`ClearLevel3D`
a GLES2DecoderImpl routine that zero-initializes a 3D texture level by uploading zeros via a pixel-unpack buffer (PBO) and repeated glTexSubImage3D calls, used to guarantee textures never expose stale GPU memory.
PBO (Pixel Buffer Object)
a GL buffer bound to GL_PIXEL_UNPACK_BUFFER that supplies the source data for texture uploads; its data store is allocated with glBufferData().
`GL_OUT_OF_MEMORY` / `GL_INVALID_OPERATION`
GL error states; the former can be raised by glBufferData() when VRAM is exhausted, the latter by glTexSubImage3D when the bound PBO lacks a valid/large-enough data store.
`LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER` / `LOCAL_PEEK_GL_ERROR`
decoder macros that drain the driver’s pending GL errors into the command-buffer wrapper and then read them back, letting the decoder observe whether a real driver error occurred.

Root Cause Analysis

ClearLevel3D allocated a PBO with glBufferData() and then issued glTexSubImage3D calls to write zeros into the texture level, but it unconditionally returned true (reporting the level as successfully cleared) regardless of whether those GL calls actually succeeded. Under VRAM pressure glBufferData() can fail with GL_OUT_OF_MEMORY, leaving the PBO with no data store, so every subsequent glTexSubImage3D fails with GL_INVALID_OPERATION and writes nothing — yet the decoder still marked the level cleared, leaving the texture backed by uninitialized GPU memory.

The fix drains any pre-existing driver errors before the clear sequence with LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glClearLevel3D"), then after the upload calls LOCAL_PEEK_GL_ERROR("glClearLevel3D") and returns false if any error is present, so a failed clear no longer marks the level as cleared. Routing the check through PeekGLError also ensures OnOutOfMemoryError() fires so lose_context_when_out_of_memory_ takes effect. Because the level stays marked uncleared, the texture manager will retry or block sampling rather than expose stale memory.

Key insight
The core mistake is treating “issued the clear commands” as “the clear succeeded”: ClearLevel3D returned true without checking GL errors, so an OOM-induced upload failure silently left uninitialized memory marked as cleared. The fix makes success contingent on an actual post-upload GL_NO_ERROR check.

Attack Path

  1. Create a 3D texture level A renderer using WebGL2/WebGPU allocates a 3D texture (or otherwise triggers lazy clearing) so that ClearLevel3D will be invoked to initialize the level.
  2. Induce VRAM exhaustion The attacker drives the GPU process toward memory pressure so that the glBufferData() allocating the clear PBO fails with GL_OUT_OF_MEMORY (inferred as the triggering condition per the patch comment).
  3. Silent clear failure With no PBO data store, each glTexSubImage3D fails GL_INVALID_OPERATION and writes no zeros, but pre-patch ClearLevel3D returns true, so the texture manager records the level as cleared.
  4. Read uninitialized memory The renderer then samples/reads back the texture level, obtaining whatever stale GPU memory backs it, an information leak of uninitialized (potentially cross-context) data. (inferred that the read-back is the disclosure sink; the diff shows only the failed clear.)

Impact Assessment

A renderer that can push the GPU process to VRAM exhaustion can cause a texture level to be marked cleared while actually containing uninitialized GPU memory, then read that memory back, disclosing stale GPU contents within the GPU process. The vendor advisory rates this low severity and does not report in-the-wild exploitation; NVD scores it CVSS 4.3. Whether the leaked memory crosses security contexts is (inferred).

Files Changed

  • gpu/command_buffer/service/gles2_cmd_decoder.cc
  • gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc

Audit Directions

  • Success reported without error check
    Look for GL/driver clear/initialize routines that return true after issuing upload/allocation commands without peeking the resulting GL error, especially those feeding a “cleared/initialized” bit.
  • Allocation-failure-under-pressure paths
    Audit code that allocates GPU buffers with glBufferData()/similar and assumes success, since OOM leaves an unusable buffer that downstream calls silently fail against.
  • Lazy-clear invariants
    Review all ClearLevel* variants and their callers in TextureManager to confirm a failed clear never marks a level cleared and never allows sampling of uninitialized storage.
From 557967a618dceaa36bf5620ff0e4135d9b2959f4 Mon Sep 17 00:00:00 2001
From: Zhenyao Mo <[email protected]>
Date: Mon, 08 Jun 2026 14:57:40 -0700
Subject: [PATCH] gpu: Fix ClearLevel3D uninitialized memory exposure under OOM

glBufferData() may fail with GL_OUT_OF_MEMORY under VRAM pressure,
leaving the PBO without a data store and causing subsequent calls to
glTexSubImage3D to fail GL_INVALID_OPERATION without writing zeros.
Returning true in this case erroneously reports the texture level as
cleared, exposing uninitialized memory.

This CL drains driver errors before the clear sequence and checks for
any errors afterward, returning false if any error occurred so that the
level is not marked as cleared.

Bug: 517000034
Change-Id: I3ec7dc0b667dfec9e4df0d69d7875bf66cc4eedc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7902883
Auto-Submit: Zhenyao Mo <[email protected]>
Reviewed-by: Geoff Lang <[email protected]>
Commit-Queue: Geoff Lang <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1643473}
---

diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 7b387bf..f8dddd5 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -12310,6 +12310,10 @@
 
   TRACE_EVENT1("gpu", "GLES2DecoderImpl::ClearLevel3D", "size", size);
 
+  // Drain any pre-existing driver errors so the post-upload check below only
+  // reflects errors generated by this clear sequence.
+  LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("glClearLevel3D");
+
   {
     ScopedPixelUnpackState reset_restore(&state_);
     GLuint buffer_id = 0;
@@ -12339,6 +12343,15 @@
       texture_manager()->GetTextureInfoForTarget(&state_, texture->target());
   api()->glBindTextureFn(texture->target(),
                          bound_texture ? bound_texture->service_id() : 0);
+
+  // glBufferData() may fail with GL_OUT_OF_MEMORY under VRAM pressure, leaving
+  // the PBO without a data store and causing every glTexSubImage3D above to
+  // fail GL_INVALID_OPERATION without writing zeros. Do not report the level as
+  // cleared in that case; routing the error through PeekGLError also ensures
+  // OnOutOfMemoryError() fires so lose_context_when_out_of_memory_ applies.
+  if (LOCAL_PEEK_GL_ERROR("glClearLevel3D") != GL_NO_ERROR) {
+    return false;
+  }
   return true;
 }
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index 32589d7..d828410 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -803,6 +803,9 @@
     base::span<GLsizei> depth,
     GLuint bound_pixel_unpack_buffer) {
   InSequence seq;
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
   EXPECT_CALL(*gl_, PixelStorei(GL_UNPACK_ALIGNMENT, 1))
       .Times(1)
       .RetiresOnSaturation();
@@ -852,6 +855,9 @@
         .RetiresOnSaturation();
   }
   EXPECT_CALL(*gl_, BindTexture(target, _)).Times(1).RetiresOnSaturation();
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
 }
 
 void GLES2DecoderTestBase::SetupExpectationsForFramebufferClearing(
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index 32589d7..d828410 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -803,6 +803,9 @@
     base::span<GLsizei> depth,
     GLuint bound_pixel_unpack_buffer) {
   InSequence seq;
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
   EXPECT_CALL(*gl_, PixelStorei(GL_UNPACK_ALIGNMENT, 1))
       .Times(1)
       .RetiresOnSaturation();
@@ -852,6 +855,9 @@
         .RetiresOnSaturation();
   }
   EXPECT_CALL(*gl_, BindTexture(target, _)).Times(1).RetiresOnSaturation();
+  EXPECT_CALL(*gl_, GetError())
+      .WillOnce(Return(GL_NO_ERROR))
+      .RetiresOnSaturation();
 }
 
 void GLES2DecoderTestBase::SetupExpectationsForFramebufferClearing(
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.