Medium chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized Use in ANGLE
DescriptionUninitialized Use in ANGLE
ComponentANGLE
Bug ClassUninitialized Memory
Tracker500524833
Fix commitef5a8a5275da (angle/angle) +24/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Files Changed

  • src/libANGLE/renderer/gl/BlitGL.cpp
  • src/tests/gl_tests/RobustResourceInitTest.cpp
From ef5a8a5275da668cf381adc8f87ee17767be4573 Mon Sep 17 00:00:00 2001
From: Geoff Lang <[email protected]>
Date: Tue, 14 Apr 2026 16:17:19 -0400
Subject: [PATCH] GL: Disable rasterizer discard for robust resource init

If the frontend had enabled rasterizer discard, the backend robust
resource init clear operations would be discarded. Ensure that
rasterizer discard is disabled.

Bug: chromium:500524833
Change-Id: I7d990ba518c50dc1ccc9683eee51515c46affb4b
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7759472
Reviewed-by: Shahbaz Youssefi <[email protected]>
Commit-Queue: Geoff Lang <[email protected]>
---

diff --git a/src/libANGLE/renderer/gl/BlitGL.cpp b/src/libANGLE/renderer/gl/BlitGL.cpp
index 154be25..0fec270 100644
--- a/src/libANGLE/renderer/gl/BlitGL.cpp
+++ b/src/libANGLE/renderer/gl/BlitGL.cpp
@@ -162,6 +162,7 @@
     }
 
     stateManager->setScissorTestEnabled(false);
+    stateManager->setRasterizerDiscardEnabled(false);
 
     return angle::Result::Continue;
 }
diff --git a/src/tests/gl_tests/RobustResourceInitTest.cpp b/src/tests/gl_tests/RobustResourceInitTest.cpp
index b70fbdf..56ca97b 100644
--- a/src/tests/gl_tests/RobustResourceInitTest.cpp
+++ b/src/tests/gl_tests/RobustResourceInitTest.cpp
@@ -726,6 +726,29 @@
     EXPECT_GL_NO_ERROR();
 }
 
+// Test that having rasterizer discard enabled still causes textures to be properly initialized
+TEST_P(RobustResourceInitTestES3, RasterizerDiscardDuringInit)
+{
+    ANGLE_SKIP_TEST_IF(!hasGLExtension());
+
+    GLTexture tex;
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
+    ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+    glEnable(GL_RASTERIZER_DISCARD);
+    ANGLE_GL_PROGRAM(blue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
+    drawQuad(blue, essl1_shaders::PositionAttrib(), 1.0f);
+
+    checkNonZeroPixels(&tex, 0, 0, 0, 0, GLColor::transparentBlack);
+}
+
 // Calling invalidate should not lead to uninitialized memory being read.
 TEST_P(RobustResourceInitTestES3, InvalidateThenReadBack)
 {
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 b70fbdf..56ca97b 100644
--- a/src/tests/gl_tests/RobustResourceInitTest.cpp
+++ b/src/tests/gl_tests/RobustResourceInitTest.cpp
@@ -726,6 +726,29 @@
     EXPECT_GL_NO_ERROR();
 }
 
+// Test that having rasterizer discard enabled still causes textures to be properly initialized
+TEST_P(RobustResourceInitTestES3, RasterizerDiscardDuringInit)
+{
+    ANGLE_SKIP_TEST_IF(!hasGLExtension());
+
+    GLTexture tex;
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    GLFramebuffer fbo;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
+    ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+    glEnable(GL_RASTERIZER_DISCARD);
+    ANGLE_GL_PROGRAM(blue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
+    drawQuad(blue, essl1_shaders::PositionAttrib(), 1.0f);
+
+    checkNonZeroPixels(&tex, 0, 0, 0, 0, GLColor::transparentBlack);
+}
+
 // Calling invalidate should not lead to uninitialized memory being read.
 TEST_P(RobustResourceInitTestES3, InvalidateThenReadBack)
 {
Loading diff…

Original Bug Report

reported by [email protected]

Robust resource initialization bypass in ANGLE-GL via RASTERIZER_DISCARD

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 security team.

Overview: A potential vulnerability in ANGLE’s native OpenGL backend allows robust resource initialization to be bypassed when GL_RASTERIZER_DISCARD is enabled. This results in the driver ignoring glClear commands while the front-end marks the buffer as initialized, potentially leading to the disclosure of uninitialized GPU memory.

Affected files:

  • third_party/angle/src/libANGLE/renderer/gl/BlitGL.cpp
  • third_party/angle/src/libANGLE/renderer/gl/FramebufferGL.cpp
  • third_party/angle/src/libANGLE/renderer/gl/RenderbufferGL.cpp
  • third_party/angle/src/libANGLE/renderer/gl/StateManagerGL.cpp
  • third_party/angle/src/libANGLE/Framebuffer.cpp
  • third_party/angle/src/libANGLE/Context.cpp

Estimated timestamp from git blame: 2018-12-06

Summary

The ANGLE native OpenGL backend fails to disable GL_RASTERIZER_DISCARD before performing robust resource initialization clears. This leads to a situation where the driver ignores the clear command (per the OpenGL specification), but ANGLE’s state tracking incorrectly marks the resource as initialized. This bypasses the security guarantee that all allocated GPU memory is zero-initialized before use, leading to a potential cross-origin information leak of recycled GPU VRAM.

Details

In ANGLE, robust resource initialization ensures that newly allocated textures, renderbuffers, and framebuffers are cleared to zero before they are accessed by a WebGL context. On the native GL backend, this is handled by BlitGL.cpp through functions like clearFramebuffer.

These functions rely on SetClearState (third_party/angle/src/libANGLE/renderer/gl/BlitGL.cpp:139-167) to configure the OpenGL state for the clear operation. However, SetClearState fails to explicitly disable GL_RASTERIZER_DISCARD.

Crucially, during Context::prepareForDraw (third_party/angle/src/libANGLE/Context.inl.h), syncDirtyObjects (which triggers robust resource initialization) is called before syncDirtyBits (which pushes context state to the native driver). This means if a user previously enabled rasterizer discard and latched it to the driver, that state remains active in the native driver when ensureDrawAttachmentsInitialized triggers a clear via BlitGL.

Per the OpenGL specification, when GL_RASTERIZER_DISCARD is enabled, glClear and glClearBuffer* commands are silently ignored by the driver.

As a result, the robust initialization clear is skipped by the driver, but ANGLE incorrectly marks the resource as initialized. An attacker can then disable rasterizer discard and read the uninitialized GPU memory.

Potential Reproduction Steps

Note: These steps describe the theoretical path an attacker would take to trigger this issue. A working PoC has not been executed in this environment.

  1. Initialize a WebGL2 context using the ANGLE native-GL backend.
  2. Enable rasterizer discard: gl.enable(gl.RASTERIZER_DISCARD).
  3. Perform a dummy draw call to push the GL_RASTERIZER_DISCARD state to the native driver.
  4. Create a new FBO with a fresh, uninitialized color renderbuffer and bind it.
  5. Perform a draw call (e.g., gl.drawArrays(gl.TRIANGLES, 0, 3)).
    • ANGLE’s prepareForDraw calls syncDirtyObjects, which attempts robust initialization via BlitGL::clearFramebuffer.
    • Because GL_RASTERIZER_DISCARD is still active natively, the driver ignores the glClear command.
    • ANGLE incorrectly marks the FBO attachments as InitState::Initialized.
  6. Disable rasterizer discard: gl.disable(gl.RASTERIZER_DISCARD).
  7. Call gl.readPixels on the FBO. Because it is marked as initialized, ANGLE allows the read, returning uninitialized, recycled GPU memory.

Suggested Fix

Modify SetClearState in third_party/angle/src/libANGLE/renderer/gl/BlitGL.cpp to explicitly disable GL_RASTERIZER_DISCARD before any clearing operations are performed:

angle::Result SetClearState(StateManagerGL *stateManager,
                            bool colorClear,
                            bool depthClear,
                            bool stencilClear,
                            GLbitfield *outClearMask)
{
    // ... existing code ...
    stateManager->setScissorTestEnabled(false);
    stateManager->setRasterizerDiscardEnabled(false); // Add this line

    return angle::Result::Continue;
}

Evaluated with Chrome root at commit: 137d451a126685dd5010e6609db9f6d4a78d8234


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