Medium watch CVSS 6.5 chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized resource in ANGLE
DescriptionUninitialized resource in ANGLE
ComponentANGLE
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.
Tracker513048462
Fix commita9dc249f6b25 (angle/angle) +139/-0
CWECWE-908
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-05-14 (per vendor advisory)
Fix landed2026-07-15 (public git)
Disclosed2026-08-25
Patch gap41 days — fix public in source before the advisory

Background

Robust resource init
ANGLE’s security feature that guarantees every GL resource reads back as zero (or the depth clear value) before its contents are first observed, preventing leaks of stale memory.
Packed depth-stencil
a single GL_DEPTH24_STENCIL8 texture level or renderbuffer whose depth and stencil aspects share one underlying resource allocation.
Init state
ANGLE tracks per-aspect initialization need in mState.mResourceNeedsInit (indexed by DIRTY_BIT_DEPTH_ATTACHMENT / DIRTY_BIT_STENCIL_ATTACHMENT), while the underlying resource carries a single shared InitState.
Partial clear
a glClear that touches only one aspect (e.g. GL_DEPTH_BUFFER_BIT) of a packed depth-stencil attachment.

Root Cause Analysis

When a glClear targeted only the depth (or only the stencil) aspect of a packed depth-stencil attachment, ANGLE treated that clear as satisfying the initialization requirement of the underlying resource. Because the framebuffer tracks depth and stencil init needs separately in mState.mResourceNeedsInit but the texture level / renderbuffer exposes only one shared InitState, marking the cleared aspect Initialized updated the shared state and thereby suppressed robust-init of the other, still-uninitialized aspect. The uncleared aspect (stencil after a depth-only clear, or depth after a stencil-only clear) was consequently left holding whatever stale memory backed the allocation, yet was reported as initialized.

The fix adds two guards in the robust-init check function in Framebuffer.cpp: if only depth is being cleared, the depth attachment is attached with getStencilSize() > 0 (i.e. packed), and mState.mResourceNeedsInit[DIRTY_BIT_DEPTH_ATTACHMENT] is set, it forces *needsInitOut = true and returns, and symmetrically for the stencil-only case. Forcing *needsInitOut = true diverts control to the full-resource init path instead of letting the partial clear stand in for initialization, so both aspects are actually zeroed.

Key insight
The single mistake is conflating a partial (single-aspect) clear with full initialization of a resource whose two aspects share one InitState; the fix detects the packed single-aspect-clear case and falls back to full robust init so the uncleared aspect is not silently marked initialized.

Attack Path

  1. Allocate a packed depth-stencil resource A WebGL2/ES3 page creates a GL_DEPTH24_STENCIL8 texture (or renderbuffer) whose depth and stencil aspects are backed by one uninitialized allocation.
  2. Trigger a single-aspect clear The page attaches only the depth aspect and issues glClear(GL_DEPTH_BUFFER_BIT) (or the stencil-only equivalent), which pre-patch marks the shared InitState as Initialized.
  3. Suppress init of the other aspect The now-Initialized shared state causes robust-init of the uncleared aspect to be skipped, leaving it populated with stale GPU memory.
  4. Read back the uncleared aspect The page samples the texture or uses the stencil test side channel (as the regression tests StencilClearWithDepthInit / DepthClearWithStencilInit do) to observe the uninitialized aspect’s contents (inferred that the leaked bytes correspond to previously used GPU memory).

Impact Assessment

An attacker running WebGL2 content obtains a read of uninitialized GPU-process memory through the uncleared aspect of a packed depth-stencil resource, defeating the robust-resource-init guarantee and enabling an information leak. Preconditions are ES3 support with a GL_DEPTH24_STENCIL8 attachment and robust resource init active (the default for WebGL). The advisory classifies this as UninitializedMemory (“Uninitialized resource in ANGLE”) and does not report exploitation in the wild; any use of the leaked data for further compromise is (inferred).

Files Changed

  • src/tests/gl_tests/RobustResourceInitTest.cpp
  • src/libANGLE/Framebuffer.cpp

Audit Directions

  • Shared init state across sub-resources
    Audit any place where per-aspect or per-subresource initialization is tracked separately but backed by a single shared InitState/dirty flag; partial operations can prematurely flip the shared flag.
  • Partial clears and load/store ops
    Review clear, invalidate, and load/store paths for depth-stencil, multiplanar, and array/mip resources where an operation touching one plane may be mistaken for initializing the whole allocation.
  • Aspect-mask coverage checks
    Verify that init-suppression logic requires all relevant aspects (not just the one being written) to be covered before marking a resource initialized.
From a9dc249f6b25acc6c9c767d55e2eb53a6c841073 Mon Sep 17 00:00:00 2001
From: Tzarial <zork@google.com>
Date: Mon, 22 Jun 2026 14:58:38 +0000
Subject: [PATCH] Fix robust resource init bypass for packed depth/stencil

Clearing only one aspect of a packed depth-stencil attachment was
acting as a partial clear of the underlying resource. The per-resource
init-state flag is shared between the depth and stencil bindings, so
marking one aspect Initialized incorrectly suppresses robust-init
of the other aspect. This patch ensures that if only one aspect is
cleared and the other aspect needs init, we fall back to full init.

Bug: b/513048462
Test: RobustResourceInitTestES3.DepthClearBypassStencilInit
Change-Id: Ic1e6a351e9e59045b9b088c59077d3b729ddd987
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/8003418
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: Tzarial <zork@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
---
diff --git a/src/libANGLE/Framebuffer.cpp b/src/libANGLE/Framebuffer.cpp
index 482f284..42db090 100644
--- a/src/libANGLE/Framebuffer.cpp
+++ b/src/libANGLE/Framebuffer.cpp
@@ -1671,6 +1671,27 @@
         return angle::Result::Continue;
     }
 
+    // Clearing only one aspect of a packed depth-stencil attachment is a partial
+    // clear of the underlying resource. While the framebuffer tracks depth and
+    // stencil initialization needs separately in mState.mResourceNeedsInit, the
+    // underlying resource (texture level or renderbuffer) has a single shared
+    // InitState. Marking one aspect as Initialized updates the shared resource state,
+    // which would incorrectly suppress robust-init of the other aspect.
+    if (depth && !stencil && mState.mDepthAttachment.isAttached() &&
+        mState.mDepthAttachment.getStencilSize() > 0 &&
+        mState.mResourceNeedsInit[DIRTY_BIT_DEPTH_ATTACHMENT])
+    {
+        *needsInitOut = true;
+        return angle::Result::Continue;
+    }
+    if (stencil && !depth && mState.mStencilAttachment.isAttached() &&
+        mState.mStencilAttachment.getDepthSize() > 0 &&
+        mState.mResourceNeedsInit[DIRTY_BIT_STENCIL_ATTACHMENT])
+    {
+        *needsInitOut = true;
+        return angle::Result::Continue;
+    }
+
     // Scissors can affect clearing.
     if (glState.isScissorTestEnabled())
     {
diff --git a/src/tests/gl_tests/RobustResourceInitTest.cpp b/src/tests/gl_tests/RobustResourceInitTest.cpp
index c1924d6..058d729 100644
--- a/src/tests/gl_tests/RobustResourceInitTest.cpp
+++ b/src/tests/gl_tests/RobustResourceInitTest.cpp
@@ -4156,6 +4156,124 @@
     EXPECT_GL_NO_ERROR();
 }
 
+// Tests that partial clear of a packed depth-stencil attachment doesn't
+// incorrectly bypass robust initialization for the other aspect.
+TEST_P(RobustResourceInitTestES3, DepthClearWithStencilInit)
+{
+    ANGLE_SKIP_TEST_IF(!hasGLExtension());
+
+    constexpr int kSize = 16;
+
+    // Create a new FBO with a packed depth-stencil texture.
+    GLTexture dsTex;
+    glBindTexture(GL_TEXTURE_2D, dsTex);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, kSize, kSize);
+
+    GLTexture colorTex;
+    glBindTexture(GL_TEXTURE_2D, colorTex);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kSize, kSize);
+
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dsTex, 0);
+    EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+    // Clear only depth. This should leave the stencil aspect in MayNeedInit state,
+    // which shouldn't be overridden to Initialized.
+    glClear(GL_DEPTH_BUFFER_BIT);
+
+    // Then bind the stencil attachment here so that we can verify it was initialized:
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dsTex, 0);
+
+    // Verify stencil is initialized to 0. We can do this by using the stencil side channel.
+    // robust-init mandates 0.
+    glEnable(GL_STENCIL_TEST);
+    glStencilFunc(GL_EQUAL, 0x00, 0xFF);
+    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+    ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
+    drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.95f, 1.0f, true);
+
+    // Read the color buffer to verify that stencil test passed for all pixels.
+    EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, GLColor::red);
+    EXPECT_GL_NO_ERROR();
+}
+
+// Tests that partial clear of a packed depth-stencil attachment (stencil-only) doesn't
+// incorrectly bypass robust initialization for the depth aspect.
+TEST_P(RobustResourceInitTestES3, StencilClearWithDepthInit)
+{
+    ANGLE_SKIP_TEST_IF(!hasGLExtension());
+
+    constexpr int kSize = 16;
+
+    // Create a new FBO with a packed depth-stencil texture.
+    GLTexture dsTex;
+    glBindTexture(GL_TEXTURE_2D, dsTex);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, kSize, kSize);
+
+    GLTexture colorTex;
+    glBindTexture(GL_TEXTURE_2D, colorTex);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kSize, kSize);
+
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dsTex, 0);
+    EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+    // Clear only stencil. This should leave the depth aspect in MayNeedInit state,
+    // which shouldn't be overridden to Initialized.
+    glClear(GL_STENCIL_BUFFER_BIT);
+
+    // Unbind the depth-stencil texture from the FBO so we can sample it as a texture.
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
+
+    // Bind the texture to texture unit 0 and set filters.
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, dsTex);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    // Verify depth == 1.0 via shader sampling
+    constexpr char kVS[] = R"(#version 300 es
+in vec4 aPosition;
+void main()
+{
+    gl_Position = aPosition;
+})";
+
+    constexpr char kFS[] = R"(#version 300 es
+precision highp float;
+uniform highp sampler2D uDepthTex;
+out vec4 outColor;
+void main()
+{
+    float depth = texture(uDepthTex, vec2(0.5, 0.5)).r;
+    if (abs(depth - 1.0) < 0.001)
+    {
+        outColor = vec4(0.0, 1.0, 0.0, 1.0); // green
+    }
+    else
+    {
+        outColor = vec4(1.0, 0.0, 0.0, 1.0); // red
+    }
+})";
+
+    ANGLE_GL_PROGRAM(depthSampleProg, kVS, kFS);
+    glUseProgram(depthSampleProg);
+    GLint texLoc = glGetUniformLocation(depthSampleProg, "uDepthTex");
+    ASSERT_NE(texLoc, -1);
+    glUniform1i(texLoc, 0);
+
+    drawQuad(depthSampleProg, "aPosition", 0.0f);
+
+    // Read the color buffer to verify that depth was successfully initialized to 1.0f.
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    EXPECT_GL_NO_ERROR();
+}
+
 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(
     RobustResourceInitTest,
     ES3_METAL().enable(Feature::EmulateDontCareLoadWithRandomClear),
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/tests/gl_tests/RobustResourceInitTest.cpp b/src/tests/gl_tests/RobustResourceInitTest.cpp
index c1924d6..058d729 100644
--- a/src/tests/gl_tests/RobustResourceInitTest.cpp
+++ b/src/tests/gl_tests/RobustResourceInitTest.cpp
@@ -4156,6 +4156,124 @@
     EXPECT_GL_NO_ERROR();
 }
 
+// Tests that partial clear of a packed depth-stencil attachment doesn't
+// incorrectly bypass robust initialization for the other aspect.
+TEST_P(RobustResourceInitTestES3, DepthClearWithStencilInit)
+{
+    ANGLE_SKIP_TEST_IF(!hasGLExtension());
+
+    constexpr int kSize = 16;
+
+    // Create a new FBO with a packed depth-stencil texture.
+    GLTexture dsTex;
+    glBindTexture(GL_TEXTURE_2D, dsTex);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, kSize, kSize);
+
+    GLTexture colorTex;
+    glBindTexture(GL_TEXTURE_2D, colorTex);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kSize, kSize);
+
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dsTex, 0);
+    EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+    // Clear only depth. This should leave the stencil aspect in MayNeedInit state,
+    // which shouldn't be overridden to Initialized.
+    glClear(GL_DEPTH_BUFFER_BIT);
+
+    // Then bind the stencil attachment here so that we can verify it was initialized:
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dsTex, 0);
+
+    // Verify stencil is initialized to 0. We can do this by using the stencil side channel.
+    // robust-init mandates 0.
+    glEnable(GL_STENCIL_TEST);
+    glStencilFunc(GL_EQUAL, 0x00, 0xFF);
+    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+    ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
+    drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.95f, 1.0f, true);
+
+    // Read the color buffer to verify that stencil test passed for all pixels.
+    EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, GLColor::red);
+    EXPECT_GL_NO_ERROR();
+}
+
+// Tests that partial clear of a packed depth-stencil attachment (stencil-only) doesn't
+// incorrectly bypass robust initialization for the depth aspect.
+TEST_P(RobustResourceInitTestES3, StencilClearWithDepthInit)
+{
+    ANGLE_SKIP_TEST_IF(!hasGLExtension());
+
+    constexpr int kSize = 16;
+
+    // Create a new FBO with a packed depth-stencil texture.
+    GLTexture dsTex;
+    glBindTexture(GL_TEXTURE_2D, dsTex);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, kSize, kSize);
+
+    GLTexture colorTex;
+    glBindTexture(GL_TEXTURE_2D, colorTex);
+    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kSize, kSize);
+
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dsTex, 0);
+    EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+    // Clear only stencil. This should leave the depth aspect in MayNeedInit state,
+    // which shouldn't be overridden to Initialized.
+    glClear(GL_STENCIL_BUFFER_BIT);
+
+    // Unbind the depth-stencil texture from the FBO so we can sample it as a texture.
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
+
+    // Bind the texture to texture unit 0 and set filters.
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, dsTex);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    // Verify depth == 1.0 via shader sampling
+    constexpr char kVS[] = R"(#version 300 es
+in vec4 aPosition;
+void main()
+{
+    gl_Position = aPosition;
+})";
+
+    constexpr char kFS[] = R"(#version 300 es
+precision highp float;
+uniform highp sampler2D uDepthTex;
+out vec4 outColor;
+void main()
+{
+    float depth = texture(uDepthTex, vec2(0.5, 0.5)).r;
+    if (abs(depth - 1.0) < 0.001)
+    {
+        outColor = vec4(0.0, 1.0, 0.0, 1.0); // green
+    }
+    else
+    {
+        outColor = vec4(1.0, 0.0, 0.0, 1.0); // red
+    }
+})";
+
+    ANGLE_GL_PROGRAM(depthSampleProg, kVS, kFS);
+    glUseProgram(depthSampleProg);
+    GLint texLoc = glGetUniformLocation(depthSampleProg, "uDepthTex");
+    ASSERT_NE(texLoc, -1);
+    glUniform1i(texLoc, 0);
+
+    drawQuad(depthSampleProg, "aPosition", 0.0f);
+
+    // Read the color buffer to verify that depth was successfully initialized to 1.0f.
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    EXPECT_GL_NO_ERROR();
+}
+
 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(
     RobustResourceInitTest,
     ES3_METAL().enable(Feature::EmulateDontCareLoadWithRandomClear),
Loading diff…

Original Bug Report

reported by vm...@google.com

Bypass of robust resource initialization for packed depth-stencil resources in ANGLE

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: ANGLE tracks the initialization state of packed depth-stencil resources using a single shared flag for both depth and stencil aspects. A clear operation performed on only one aspect (e.g., depth) incorrectly marks the entire resource as initialized, potentially allowing uninitialized GPU memory from the other aspect (e.g., stencil) to be exposed. This may result in a cross-origin information leak of GPU memory to web content.

Affected files:

  • third_party/angle/src/libANGLE/Framebuffer.cpp
  • third_party/angle/src/libANGLE/Renderbuffer.cpp
  • third_party/angle/src/libANGLE/Texture.cpp
  • third_party/angle/src/libANGLE/Surface.cpp
  • third_party/angle/src/libANGLE/renderer/FramebufferImpl.cpp
  • third_party/angle/src/libANGLE/Context.cpp

Estimated timestamp from git blame: Unknown (Google3 checkout)

Background

ANGLE’s robust resource initialization ensures that all GPU resources (textures, renderbuffers) are zero-initialized before they are accessed by an application. This is a critical security boundary for WebGL to prevent the leakage of uninitialized GPU memory across origins.

Root Cause Analysis

In ANGLE, resources that use a packed depth-stencil format (e.g., GL_DEPTH24_STENCIL8) manage their initialization state (InitState) using a single shared flag. This is evident in classes like gl::Renderbuffer (third_party/angle/src/libANGLE/Renderbuffer.cpp:397), gl::Texture, and gl::Surface (third_party/angle/src/libANGLE/Surface.cpp:717), where the setInitState method ignores the specific aspect (depth or stencil) being updated and sets a single mInitState variable for the entire resource.

When a clear operation is performed on a framebuffer attachment, gl::Framebuffer::markAttachmentsInitialized (lines 2667-2689 of third_party/angle/src/libANGLE/Framebuffer.cpp) is called to update the state. If an attacker performs a clear of only the depth aspect, the following occurs:

  1. markAttachmentsInitialized is called with depth=true and stencil=false.
  2. It calls mState.mDepthAttachment.setInitState(InitState::Initialized) (line 2680).
  3. The underlying resource’s shared flag is updated to Initialized.
  4. The stencil aspect, which was never cleared or zeroed, is now also considered Initialized by the resource.

While the Framebuffer’s frontend tracking (mState.mResourceNeedsInit) might still have the stencil bit set (since markAttachmentsInitialized didn’t reset it), the backend safety mechanism in rx::InitAttachment (lines 15-24 of third_party/angle/src/libANGLE/renderer/FramebufferImpl.cpp) is bypassed. This function checks attachment->initState() == gl::InitState::MayNeedInit before calling initializeContents. Because the resource’s shared flag is already Initialized, the automated zero-filling is skipped, and the subsequent draw call uses the uninitialized stencil bits.

Potential Steps to Reproduce (Suggested)

  1. Initialize a WebGL2 context with robust resource initialization enabled (default in Chrome).
  2. Create a DEPTH24_STENCIL8 renderbuffer and attach it to a framebuffer as GL_DEPTH_STENCIL_ATTACHMENT.
  3. Clear only the depth aspect: gl.clear(gl.DEPTH_BUFFER_BIT) while ensuring the stencil mask is disabled (e.g., gl.stencilMask(0)).
  4. The resource’s internal InitState is now Initialized for both aspects, despite the stencil aspect containing uninitialized memory.
  5. Perform a draw call that uses the stencil aspect (e.g., enable GL_STENCIL_TEST). The backend will skip initialization because the resource state is Initialized.
  6. Use stencil testing logic and gl.readPixels to observe and extract the values of the uninitialized GPU memory.

Suggested Fix

For packed depth-stencil resources, ANGLE should track the initialization state of the depth and stencil aspects independently. The resource should only be considered fully Initialized if both aspects have been initialized. Alternatively, the logic in markAttachmentsInitialized or setInitState should be modified to ensure that marking one aspect as initialized does not prematurely mark the other aspect as initialized for packed formats.

Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

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