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.
Tracker512971896
Fix commitc7e5a77b65ae (angle/angle) +35/-42
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-13 (per vendor advisory)
Fix landed2026-08-11 (public git)
Disclosed2026-08-25
Patch gapNot computed: several commits cite this bug and it is not yet established which one is the fix.

Background

WebGL context
An ANGLE GL context created with WebGL-compatibility attributes, which imposes extra validation and shader-hardening rules that native GL does not require.
Hardened context
A separate ANGLE context flag (mHardenedContext, queried via isHardenedContext()) that requests the same defensive validation and initialization as WebGL for non-WebGL callers.
`isWebGL()` / `isHardenedContext()`
Predicates guarding numerous safety checks: attachment uniqueness, uniform-buffer binding validation, shader variable initialization, indirect-array clamping, and transform-feedback conflict checks.
Shader init options
Compile options such as initGLPosition, initOutputVariables, initGLPosition, and clampIndirectArrayBounds that zero-initialize outputs/locals to avoid reading uninitialized values.

Root Cause Analysis

Throughout ANGLE, defensive checks were gated on the disjunction context->isWebGL() || context->isHardenedContext(), treating the two flags as needing to be tested together. Because the two predicates were maintained independently, any safety gate that tested only isHardenedContext() (without the WebGL clause) would silently fail to apply to WebGL contexts, leaving a WebGL context without a protection the hardened path assumed — including shader output/local-variable initialization that prevents reading uninitialized values.

The fix makes the WebGL flag imply the hardened flag at construction: mHardenedContext(mWebGLContext || GetHardenedContext(attribs)). With that invariant established, every isWebGL() || isHardenedContext() site can be collapsed to a single isHardenedContext() check without behavior loss, and, crucially, any hardened-only gate elsewhere now also covers WebGL. This closes the gap where a WebGL context could bypass a hardened-context protection and expose uninitialized resource state.

Key insight
The core mistake was maintaining WebGL and hardened as parallel, independently tested flags, so a check written against only isHardenedContext() could miss WebGL; the fix folds WebGL into hardened at context creation so a single isHardenedContext() predicate reliably covers both.

Attack Path

  1. Create a WebGL context A page obtains a WebGL context, which sets mWebGLContext but, pre-patch, not mHardenedContext.
  2. Reach a hardened-only gate Exercise a code path whose protection (e.g. a shader-initialization or validation gate) was conditioned on isHardenedContext() alone rather than also isWebGL() (such a gate is not shown in the diff; its existence is (inferred)).
  3. Bypass the protection Because the WebGL context did not report as hardened, that gate is skipped, so the corresponding initialization/validation does not run.
  4. Observe uninitialized state The unprotected path yields an uninitialized resource/value readable from the WebGL context ((inferred) as to the exact sink).

Impact Assessment

An attacker running WebGL content could bypass a hardened-context protection and reach an uninitialized resource in the GPU process, an information-disclosure/uninitialized-memory issue. The precondition is a WebGL context reaching a safety gate that tested only the hardened flag. The advisory classifies this as an uninitialized resource in ANGLE; the specific unprotected sink is not shown in the diff and is (inferred), and no exploited-in-the-wild status is stated.

Files Changed

  • src/libANGLE/renderer/d3d/FramebufferD3D.cpp
  • src/libANGLE/renderer/vulkan/ShaderVk.cpp
  • src/libANGLE/renderer/gl/ShaderGL.cpp
  • src/libANGLE/validationES2.cpp
  • src/libANGLE/Context.cpp
  • src/libANGLE/renderer/metal/ShaderMtl.mm
  • src/libANGLE/validationES3.cpp
  • src/libANGLE/Framebuffer.cpp
  • src/libANGLE/renderer/d3d/ShaderD3D.cpp
  • src/libANGLE/validationES.cpp
  • src/libANGLE/VertexArray.cpp
  • src/libANGLE/Shader.cpp

Audit Directions

  • Redundant parallel predicates
    Search for isWebGL() || / isHardenedContext() (and inverses) and confirm no gate anywhere tests only one when both must be covered.
  • Flags with an intended implication
    Audit context/mode flags that should subsume one another but are set independently at construction, creating coverage gaps.
  • Uninitialized-value protections behind mode gates
    Review shader-init and resource-init options guarded by context modes to ensure every hardened-equivalent context enables them.
From c7e5a77b65ae275b50ccd6fbe92ad023ee7aba93 Mon Sep 17 00:00:00 2001
From: Shahbaz Youssefi <syoussefi@chromium.org>
Date: Thu, 06 Aug 2026 14:40:21 -0400
Subject: [PATCH] Automatically mark webgl contexts as hardened

No need to do `webgl || hardened` everywhere, checking for hardened
is sufficient and includes webgl.

Bug: chromium:512971896
Change-Id: Ic461f760f21439fd3168b6b48526ec77a36ad9b7
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/8214571
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
---
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 5089ace..460e606 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -724,7 +724,7 @@
       mCurrentReadSurface(static_cast<egl::Surface *>(EGL_NO_SURFACE)),
       mDisplay(display),
       mWebGLContext(GetWebGLContext(attribs)),
-      mHardenedContext(GetHardenedContext(attribs)),
+      mHardenedContext(mWebGLContext || GetHardenedContext(attribs)),
       mBufferAccessValidationEnabled(false),
       mRequiresRobustBehavior(false),
       mExtensionsEnabled(GetExtensionsEnabled(attribs, mWebGLContext)),
diff --git a/src/libANGLE/Framebuffer.cpp b/src/libANGLE/Framebuffer.cpp
index 5261ffd..c87fa59 100644
--- a/src/libANGLE/Framebuffer.cpp
+++ b/src/libANGLE/Framebuffer.cpp
@@ -1568,10 +1568,10 @@
             err::kFramebufferIncompleteMultisampleNonFixedSamplesWithRenderbuffers);
     }
 
-    // The WebGL conformance tests implicitly define that all framebuffer
-    // attachments must be unique. For example, the same level of a texture can
-    // not be attached to two different color attachments.
-    if (context->isWebGL() || context->isHardenedContext())
+    // The WebGL conformance tests implicitly define that all framebuffer attachments must be
+    // unique. For example, the same level of a texture can not be attached to two different color
+    // attachments. The same restriction is applied to hardened contexts.
+    if (context->isHardenedContext())
     {
         if (!mState.colorAttachmentsAreUniqueImages())
         {
diff --git a/src/libANGLE/Shader.cpp b/src/libANGLE/Shader.cpp
index 76c5b76..2a64478 100644
--- a/src/libANGLE/Shader.cpp
+++ b/src/libANGLE/Shader.cpp
@@ -629,9 +629,9 @@
     options.objectCode       = true;
     options.emulateGLDrawID  = true;
 
-    // Add default options to WebGL shaders to prevent unexpected behavior during
-    // compilation.
-    if (context->isWebGL() || context->isHardenedContext())
+    // Add default options to WebGL shaders to prevent unexpected behavior during compilation.
+    // Similarly protect hardened contexts.
+    if (context->isHardenedContext())
     {
         options.initGLPosition             = true;
         options.limitCallStackDepth        = true;
diff --git a/src/libANGLE/VertexArray.cpp b/src/libANGLE/VertexArray.cpp
index 14db914..2c80758 100644
--- a/src/libANGLE/VertexArray.cpp
+++ b/src/libANGLE/VertexArray.cpp
@@ -523,7 +523,7 @@
             boundBuffer->addRef();
             boundBuffer->onNonTFBindingChanged(1);
             boundBuffer->addVertexArrayBinding(context, bindingIndex);
-            if (context->isWebGL() || context->isHardenedContext())
+            if (context->isHardenedContext())
             {
                 mCachedBufferPropertyTransformFeedbackConflict.set(
                     bindingIndex, boundBuffer->hasTFBBindingConflict());
@@ -765,7 +765,7 @@
         }
     }
 
-    if (context->isWebGL() || context->isHardenedContext())
+    if (context->isHardenedContext())
     {
         for (size_t bindingIndex : bufferBindingMask)
         {
@@ -868,7 +868,7 @@
         }
     }
 
-    if (context->isWebGL() || context->isHardenedContext())
+    if (context->isHardenedContext())
     {
         if (buffer->hasTFBBindingConflict())
         {
@@ -918,7 +918,7 @@
             break;
 
         case angle::SubjectMessage::BindingChanged:
-            if (context->isWebGL() || context->isHardenedContext())
+            if (context->isHardenedContext())
             {
                 bufferBindingMask.reset(kElementArrayBufferIndex);
 
diff --git a/src/libANGLE/renderer/d3d/FramebufferD3D.cpp b/src/libANGLE/renderer/d3d/FramebufferD3D.cpp
index 1a3460b..204b3a7 100644
--- a/src/libANGLE/renderer/d3d/FramebufferD3D.cpp
+++ b/src/libANGLE/renderer/d3d/FramebufferD3D.cpp
@@ -265,7 +265,7 @@
     // D3D11 does not allow for overlapping RenderTargetViews.
     // If WebGL compatibility (or hardened context) is enabled, this has already been checked at a
     // higher level.
-    if (!context->isWebGL() && !context->isHardenedContext())
+    if (!context->isHardenedContext())
     {
         if (!mState.colorAttachmentsAreUniqueImages())
         {
diff --git a/src/libANGLE/renderer/d3d/ShaderD3D.cpp b/src/libANGLE/renderer/d3d/ShaderD3D.cpp
index 33351fd..8aaa5ab 100644
--- a/src/libANGLE/renderer/d3d/ShaderD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ShaderD3D.cpp
@@ -286,8 +286,7 @@
     }
 #endif
 
-    const bool isHardened = context->isWebGL() || context->isHardenedContext();
-    if (isHardened)
+    if (context->isHardenedContext())
     {
         options->clampIndirectArrayBounds = true;
     }
diff --git a/src/libANGLE/renderer/gl/ShaderGL.cpp b/src/libANGLE/renderer/gl/ShaderGL.cpp
index ab40bee..a58eb4e 100644
--- a/src/libANGLE/renderer/gl/ShaderGL.cpp
+++ b/src/libANGLE/renderer/gl/ShaderGL.cpp
@@ -128,7 +128,7 @@
 
     options->initGLPosition = true;
 
-    const bool isHardened = context->isWebGL() || context->isHardenedContext();
+    const bool isHardened = context->isHardenedContext();
     if (isHardened || (features.initFragmentOutputVariables.enabled &&
                        mState.getShaderType() == gl::ShaderType::Fragment))
     {
diff --git a/src/libANGLE/renderer/metal/ShaderMtl.mm b/src/libANGLE/renderer/metal/ShaderMtl.mm
index fd75a48..5fb6ca4 100644
--- a/src/libANGLE/renderer/metal/ShaderMtl.mm
+++ b/src/libANGLE/renderer/metal/ShaderMtl.mm
@@ -70,8 +70,7 @@
 
     options->forceDeferNonConstGlobalInitializers = true;
 
-    const bool isHardened = context->isWebGL() || context->isHardenedContext();
-    if (isHardened && mState.getShaderType() != gl::ShaderType::Compute)
+    if (context->isHardenedContext() && mState.getShaderType() != gl::ShaderType::Compute)
     {
         options->initOutputVariables = true;
     }
diff --git a/src/libANGLE/renderer/vulkan/ShaderVk.cpp b/src/libANGLE/renderer/vulkan/ShaderVk.cpp
index 352282e..b2103a4 100644
--- a/src/libANGLE/renderer/vulkan/ShaderVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ShaderVk.cpp
@@ -25,7 +25,7 @@
 {
     ContextVk *contextVk = vk::GetImpl(context);
 
-    if (context->isWebGL() || context->isHardenedContext())
+    if (context->isHardenedContext())
     {
         // Only WebGL requires initialization of local variables, others don't.
         // Extra initialization in spirv shader may affect performance.
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 7b3f9e1..c5a6a2e 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -615,8 +615,7 @@
         }
     }
 
-    if (ANGLE_UNLIKELY(context->isWebGL() || context->isBufferAccessValidationEnabled() ||
-                       context->isHardenedContext()))
+    if (ANGLE_UNLIKELY(context->isHardenedContext() || context->isBufferAccessValidationEnabled()))
     {
         // Uniform buffer validation
         for (size_t uniformBlockIndex = 0; uniformBlockIndex < executable.getUniformBlocks().size();
@@ -628,8 +627,7 @@
             const OffsetBindingPointer<Buffer> &uniformBuffer =
                 state.getIndexedUniformBuffer(blockBinding);
 
-            if (uniformBuffer.get() == nullptr &&
-                (context->isWebGL() || context->isHardenedContext()))
+            if (uniformBuffer.get() == nullptr && context->isHardenedContext())
             {
                 // undefined behaviour
                 return gl::err::kUniformBufferUnbound;
@@ -1754,8 +1752,7 @@
                         return false;
                     }
 
-                    if ((context->isWebGL() || context->isHardenedContext()) &&
-                        *readColorBuffer == *attachment)
+                    if (context->isHardenedContext() && *readColorBuffer == *attachment)
                     {
                         ANGLE_VALIDATION_ERROR(GL_INVALID_OPERATION, kBlitSameImageColor);
                         return false;
@@ -1809,8 +1806,7 @@
                     return false;
                 }
 
-                if ((context->isWebGL() || context->isHardenedContext()) &&
-                    *readBuffer == *drawBuffer)
+                if (context->isHardenedContext() && *readBuffer == *drawBuffer)
                 {
                     ANGLE_VALIDATION_ERROR(GL_INVALID_OPERATION, kBlitSameImageDepthOrStencil);
                     return false;
@@ -3811,7 +3807,7 @@
     }
 
     // Detect texture copying feedback loops for WebGL or hardedend contexts.
-    if (context->isWebGL() || context->isHardenedContext())
+    if (context->isHardenedContext())
     {
         // zoffset cannot be non-zero for a cube map destination
         ASSERT(!(texType == TextureType::CubeMap && zoffset != 0));
@@ -4160,7 +4156,7 @@
         }
 
         // Do some additional WebGL/hardened-specific validation
-        if (ANGLE_UNLIKELY(context->isWebGL() || context->isHardenedContext()))
+        if (ANGLE_UNLIKELY(context->isHardenedContext()))
         {
             const TransformFeedback *transformFeedbackObject = state.getCurrentTransformFeedback();
             if (state.isTransformFeedbackActive() &&
@@ -4204,7 +4200,7 @@
             }
         }
 
-        if (ANGLE_UNLIKELY(context->isWebGL() || context->isHardenedContext()))
+        if (ANGLE_UNLIKELY(context->isHardenedContext()))
         {
             // UB: Detect rendering feedback loops for WebGL or hardened context.
             if (framebuffer->formsRenderingFeedbackLoopWith(
@@ -4413,7 +4409,7 @@
 
     if (elementArrayBuffer)
     {
-        if (ANGLE_UNLIKELY(context->isWebGL() || context->isHardenedContext()) &&
+        if (ANGLE_UNLIKELY(context->isHardenedContext()) &&
             elementArrayBuffer->hasTFBBindingConflict())
         {
             return kElementArrayBufferBoundForTransformFeedback;
@@ -6860,7 +6856,7 @@
         ANGLE_VALIDATION_ERROR(GL_INVALID_OPERATION, kBufferMapped);
         return false;
     }
-    if (pixelPackBuffer != nullptr && (context->isWebGL() || context->isHardenedContext()) &&
+    if (pixelPackBuffer != nullptr && context->isHardenedContext() &&
         pixelPackBuffer->hasTFBBindingConflict())
     {
         ANGLE_VALIDATION_ERROR(GL_INVALID_OPERATION, kPixelPackBufferBoundForTransformFeedback);
@@ -6913,7 +6909,7 @@
         }
     }
 
-    if (context->isWebGL() || context->isHardenedContext())
+    if (context->isHardenedContext())
     {
         // WebGL 2.0 disallows the scenario:
         //   GL_PACK_SKIP_PIXELS + width > DataStoreWidth
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index cb9dbb6..3b52b4d 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -1963,7 +1963,7 @@
             return false;
         }
 
-        if (context->isWebGL() || context->isHardenedContext())
+        if (context->isHardenedContext())
         {
             if (ANGLE_UNLIKELY(pixelUnpackBuffer->hasTFBBindingConflict()))
             {
@@ -2219,7 +2219,7 @@
             return false;
         }
 
-        if (context->isWebGL() || context->isHardenedContext())
+        if (context->isHardenedContext())
         {
             if (ANGLE_UNLIKELY(pixelUnpackBuffer->hasTFBBindingConflict()))
             {
@@ -3441,7 +3441,7 @@
         }
     }
 
-    if (context->isWebGL() || context->isHardenedContext())
+    if (context->isHardenedContext())
     {
         if (buffer->hasTFBBindingConflict())
         {
@@ -3966,7 +3966,7 @@
     }
 
     // Do some additional WebGL-specific validation
-    if (ANGLE_UNLIKELY(context->isWebGL() || context->isHardenedContext()))
+    if (ANGLE_UNLIKELY(context->isHardenedContext()))
     {
         if (buffer->hasTFBBindingConflict())
         {
@@ -4038,7 +4038,7 @@
     }
 
     // Do some additional WebGL-specific validation
-    if (ANGLE_UNLIKELY(context->isWebGL() || context->isHardenedContext()))
+    if (ANGLE_UNLIKELY(context->isHardenedContext()))
     {
         if (buffer->hasTFBBindingConflict())
         {
diff --git a/src/libANGLE/validationES3.cpp b/src/libANGLE/validationES3.cpp
index 29c4ec3..2dc82fe 100644
--- a/src/libANGLE/validationES3.cpp
+++ b/src/libANGLE/validationES3.cpp
@@ -655,8 +655,7 @@
     Buffer *pixelUnpackBuffer = context->getState().getTargetBuffer(BufferBinding::PixelUnpack);
     if (pixelUnpackBuffer != nullptr)
     {
-        if ((context->isWebGL() || context->isHardenedContext()) &&
-            pixelUnpackBuffer->hasTFBBindingConflict())
+        if (context->isHardenedContext() && pixelUnpackBuffer->hasTFBBindingConflict())
         {
             ANGLE_VALIDATION_ERROR(GL_INVALID_OPERATION,
                                    kPixelUnpackBufferBoundForTransformFeedback);
@@ -685,7 +684,7 @@
         }
     }
 
-    if (context->isWebGL() || context->isHardenedContext())
+    if (context->isHardenedContext())
     {
         // Define:
         //   DataStoreWidth  = (GL_UNPACK_ROW_LENGTH ? GL_UNPACK_ROW_LENGTH : width)
@@ -2923,7 +2922,7 @@
         return false;
     }
 
-    if ((context->isWebGL() || context->isHardenedContext()) &&
+    if (context->isHardenedContext() &&
         (readBuffer->hasTFBBindingConflict() || writeBuffer->hasTFBBindingConflict()))
     {
         ANGLE_VALIDATION_ERROR(GL_INVALID_OPERATION, kBufferBoundForTransformFeedback);
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential info leak due to premature robust-resource-init state commitment 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’s front-end robust resource initialization logic marks textures and framebuffers as initialized before executing the underlying backend operation. If the backend operation fails, the resource’s state remains marked as initialized despite the GPU memory being unwritten. This could allow an attacker to sample uninitialized GPU memory, leading to a potential cross-origin information leak.

Affected files:

  • third_party/angle/src/libANGLE/Texture.cpp
  • third_party/angle/src/libANGLE/Framebuffer.cpp
  • third_party/angle/src/libANGLE/Context.cpp

Estimated timestamp from git blame: Unknown (Google3 checkout)

Root Cause Analysis

In ANGLE’s front-end, the robust-resource-init feature tracks whether a resource (like a texture level or a framebuffer attachment) has been initialized to prevent reading uninitialized GPU memory. A logic flaw exists where the front-end commits the InitState::Initialized state before the backend implementation executes the write. If the backend operation subsequently fails, the resource is falsely flagged as initialized.

Texture Initialization

In third_party/angle/src/libANGLE/Texture.cpp, ensureSubImageInitialized is called before dispatching to the backend at sub-image entry points (e.g., setSubImage).

angle::Result Texture::ensureSubImageInitialized(..., const Box &area) {
    if (doesSubImageNeedInit(context, imageIndex, area)) {
        ANGLE_TRY(initializeContents(context, GL_NONE, imageIndex));
    }
    setInitState(GL_NONE, imageIndex, InitState::Initialized); // Always committed
    return angle::Result::Continue;
}

When an attacker issues an upload that exactly covers the full extent of the resource, doesSubImageNeedInit returns false, bypassing explicit zero-fill initialization. setInitState(Initialized) is then unconditionally called, expecting the backend to fully overwrite the texture.

Subsequently, Texture::setSubImage calls mTexture->setSubImage(...). If this backend operation fails (returning an error), the ANGLE_TRY macro aborts the function. However, the InitState::Initialized state is never rolled back.

Framebuffer Initialization

A similar pattern exists in third_party/angle/src/libANGLE/Framebuffer.cpp within ensureClearAttachmentsInitialized. The front-end marks attachments as initialized before calling the backend’s clear method. A comment in the code explicitly acknowledges this:

> // If the impl encounters an error during a a full (non-partial) clear, the attachments will > // still be marked initialized. This simplifies design, allowing this method to be called before > // the clear.

Impact

If an attacker can induce a backend failure during a full-resource upload or clear, the resource’s GPU memory remains completely uninitialized but ANGLE considers it initialized. Subsequent sampling or readPixels operations will bypass zero-fill mitigations, allowing the attacker to read recycled GPU memory. This memory may contain cross-origin data, leading to an information leak.

Crucially, while an Out-Of-Memory (OOM) error typically triggers context loss (mitigating the leak), a non-OOM error does not. For example, in the Metal backend (TextureMtl.mm), ANGLE_CHECK_GL_MATH is used to validate pitch and skip bytes. If these calculations overflow, a GL_INVALID_OPERATION is generated. According to ErrorSet::handleError in Context.cpp, GL_INVALID_OPERATION does not trigger context loss. The WebGL context survives, allowing the attacker to read the uninitialized texture.

Suggested Steps to Trigger (Potential)

Note: These are potential steps as our tooling agent cannot execute code to verify them.

  1. Establish a WebGL context in a browser window.
  2. Create a texture and allocate its storage using gl.texStorage2D(). Its state is MayNeedInit.
  3. Issue a gl.texSubImage2D() call where the upload area exactly matches the entire texture dimensions.
  4. Craft the parameters (e.g., unpack alignment, row length, or offsets) such that they pass front-end validation but cause an integer overflow or validation failure in the specific backend implementation (e.g., triggering ANGLE_CHECK_GL_MATH in Metal’s setSubImageImpl).
  5. The front-end marks the texture as Initialized, but the backend fails and returns GL_INVALID_OPERATION.
  6. Because the error is not an OOM, the context survives.
  7. Execute gl.readPixels() or sample the texture in a shader. Because the state is Initialized, ANGLE permits the read without zeroing the memory, exposing uninitialized GPU memory.

Proposed Fix

Do not commit the InitState::Initialized state until the backend operation has successfully completed. This could be achieved by modifying ensureSubImageInitialized and ensureClearAttachmentsInitialized to return a state update token or an RAII helper that only applies the Initialized state upon successful return of the surrounding function. Alternatively, move the setInitState calls to occur immediately after the ANGLE_TRY(mTexture->setSubImage(...)) and ANGLE_CONTEXT_TRY(mState.getDrawFramebuffer()->clear(...)) calls succeed.

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