Medium chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized resource in Dawn
DescriptionUninitialized resource in Dawn
ComponentDawn
Sub-componentGL
Bug ClassUninitialized Memory
Tracker553121488
Fix commitb911f1a5ae55 (dawn) +127/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Changed Functions

FunctionChangeNotes
TEST_P
src/dawn/tests/white_box/GLFramebufferCompletenessTests.cpp
modified

Files Changed

  • src/dawn/tests/white_box/GLFramebufferCompletenessTests.cpp
  • src/dawn/native/opengl/UtilsGL.cpp
  • src/dawn/native/opengl/TextureGL.cpp
  • src/dawn/native/opengl/SwapChainEGL.cpp
From b911f1a5ae55ae23de2088c90cc32422fc6b024d Mon Sep 17 00:00:00 2001
From: Shrek Shao <[email protected]>
Date: Thu, 03 Sep 2026 16:36:21 -0700
Subject: [PATCH] gl: Check framebuffer completeness in ClearTexture and helpers

Texture::ClearTexture in the OpenGL backend creates temporary
framebuffers to clear texture subresources without querying framebuffer
completeness. On OpenGL ES drivers where certain formats cannot be
attached as complete framebuffer attachments (e.g. returning
GL_FRAMEBUFFER_UNSUPPORTED or GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT),
glClearBuffer* silently fails with GL_INVALID_FRAMEBUFFER_OPERATION
while Dawn marks the subresource as initialized, leaving uninitialized
GPU memory.

This CL adds CheckFramebufferComplete checks to Texture::ClearTexture,
CopyImageSubData, and SwapChainEGL::PresentImpl to ensure all temporary
framebuffer attachments are valid and complete before drawing/clearing.

Adds white-box tests in GLFramebufferCompletenessTests using error
injection to simulate framebuffer incompleteness during lazy texture
clearing (2D, 3D, and depth/stencil) and blit subdata copies.

Fixed: 553121488
Change-Id: Iabcc1f4e49b41bcc7147c5100c21907cd9662a9b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/338396
Reviewed-by: Kai Ninomiya <[email protected]>
Commit-Queue: Shrek Shao <[email protected]>
---
diff --git a/src/dawn/native/opengl/SwapChainEGL.cpp b/src/dawn/native/opengl/SwapChainEGL.cpp
index 55e0a09..db9a736 100644
--- a/src/dawn/native/opengl/SwapChainEGL.cpp
+++ b/src/dawn/native/opengl/SwapChainEGL.cpp
@@ -118,6 +118,7 @@
         DAWN_GL_TRY(gl, GenFramebuffers(1, &readFbo));
         DAWN_GL_TRY(gl, BindFramebuffer(GL_READ_FRAMEBUFFER, readFbo));
         DAWN_TRY(mTextureView->BindToFramebuffer(gl, GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0));
+        DAWN_TRY(CheckFramebufferComplete(gl, GL_READ_FRAMEBUFFER));
 
         DAWN_GL_TRY(gl, BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
         DAWN_GL_TRY(gl, Scissor(0, 0, surfaceWidth, surfaceHeight));
diff --git a/src/dawn/native/opengl/TextureGL.cpp b/src/dawn/native/opengl/TextureGL.cpp
index 00a3089..717779e 100644
--- a/src/dawn/native/opengl/TextureGL.cpp
+++ b/src/dawn/native/opengl/TextureGL.cpp
@@ -464,6 +464,7 @@
                     }
                     DAWN_TRY(FramebufferTextureHelper(gl, mTarget, GL_DRAW_FRAMEBUFFER, attachment,
                                                       GetTextureHandle(), level, layer));
+                    DAWN_TRY(CheckFramebufferComplete(gl, GL_DRAW_FRAMEBUFFER));
                     DAWN_TRY(DoClear(aspectsToClear));
                 }
             }
@@ -570,12 +571,14 @@
                         for (GLint z = 0; z < static_cast<GLint>(depth); ++z) {
                             DAWN_GL_TRY(gl, FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment,
                                                                     GetTextureHandle(), level, z));
+                            DAWN_TRY(CheckFramebufferComplete(gl, GL_DRAW_FRAMEBUFFER));
                             DAWN_TRY(DoClear());
                         }
                     } else {
                         DAWN_TRY(FramebufferTextureHelper(gl, mTarget, GL_DRAW_FRAMEBUFFER,
                                                           attachment, GetTextureHandle(), level,
                                                           layer));
+                        DAWN_TRY(CheckFramebufferComplete(gl, GL_DRAW_FRAMEBUFFER));
                         DAWN_TRY(DoClear());
                     }
 
diff --git a/src/dawn/native/opengl/UtilsGL.cpp b/src/dawn/native/opengl/UtilsGL.cpp
index df219b6..e33e7d7 100644
--- a/src/dawn/native/opengl/UtilsGL.cpp
+++ b/src/dawn/native/opengl/UtilsGL.cpp
@@ -161,6 +161,8 @@
                                                         dstHandle, dstLevel, dst.z + layer));
             }
         }
+        DAWN_TRY(CheckFramebufferComplete(gl, GL_READ_FRAMEBUFFER));
+        DAWN_TRY(CheckFramebufferComplete(gl, GL_DRAW_FRAMEBUFFER));
         DAWN_GL_TRY(
             gl, BlitFramebuffer(src.x, src.y, src.x + size.width, src.y + size.height, dst.x, dst.y,
                                 dst.x + size.width, dst.y + size.height, blitMask, GL_NEAREST));
diff --git a/src/dawn/tests/white_box/GLFramebufferCompletenessTests.cpp b/src/dawn/tests/white_box/GLFramebufferCompletenessTests.cpp
index 0a9fc63..d58cfaf 100644
--- a/src/dawn/tests/white_box/GLFramebufferCompletenessTests.cpp
+++ b/src/dawn/tests/white_box/GLFramebufferCompletenessTests.cpp
@@ -26,7 +26,9 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "src/dawn/native/ErrorData.h"
+#include "src/dawn/native/ErrorInjector.h"
 #include "src/dawn/native/opengl/DeviceGL.h"
+#include "src/dawn/native/opengl/TextureGL.h"
 #include "src/dawn/native/opengl/UtilsGL.h"
 #include "src/dawn/tests/DawnTest.h"
 
@@ -88,6 +90,125 @@
                  GLFramebufferStatusAsString(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT));
 }
 
+#if defined(DAWN_ENABLE_ERROR_INJECTION)
+
+// Test that Texture::EnsureSubresourceContentInitialized (which calls Texture::ClearTexture)
+// verifies framebuffer completeness before clearing.
+// Injecting GL_FRAMEBUFFER_UNSUPPORTED on glCheckFramebufferStatus mimics OpenGL ES drivers
+// where certain formats (such as floating-point textures on drivers without
+// GL_EXT_color_buffer_float, or drivers returning GL_FRAMEBUFFER_UNSUPPORTED) cannot be attached
+// as complete framebuffer attachments. On such drivers, glClearBuffer* generates
+// GL_INVALID_FRAMEBUFFER_OPERATION and performs no writes; verifying completeness ensures Dawn
+// catches the error instead of silently marking uninitialized/dirty GPU memory as initialized.
+TEST_P(GLFramebufferCompletenessTests, LazyClearChecksCompleteness) {
+    Device* deviceGL = ToBackend(FromAPI(device.Get()));
+    const OpenGLFunctions& gl = deviceGL->GetGL();
+
+    // 1. 2D Color Texture (e.g. RGBA16Float)
+    {
+        wgpu::TextureDescriptor desc;
+        desc.size = {4, 4, 1};
+        desc.format = wgpu::TextureFormat::RGBA16Float;
+        desc.usage = wgpu::TextureUsage::TextureBinding;
+        wgpu::Texture tex = device.CreateTexture(&desc);
+
+        Texture* texGL = ToBackend(FromAPI(tex.Get()));
+
+        EnableErrorInjector();
+        InjectErrorAt(0u);
+        MaybeError err =
+            texGL->EnsureSubresourceContentInitialized(gl, texGL->GetAllSubresources());
+        EXPECT_TRUE(err.IsError());
+        err.AcquireError();
+        EXPECT_EQ(AcquireErrorInjectorCallCount(), 1u);
+        DisableErrorInjector();
+    }
+
+    // 2. Depth/Stencil Texture
+    {
+        wgpu::TextureDescriptor desc;
+        desc.size = {4, 4, 1};
+        desc.format = wgpu::TextureFormat::Depth24PlusStencil8;
+        desc.usage = wgpu::TextureUsage::TextureBinding;
+        wgpu::Texture tex = device.CreateTexture(&desc);
+
+        Texture* texGL = ToBackend(FromAPI(tex.Get()));
+
+        EnableErrorInjector();
+        InjectErrorAt(0u);
+        MaybeError err =
+            texGL->EnsureSubresourceContentInitialized(gl, texGL->GetAllSubresources());
+        EXPECT_TRUE(err.IsError());
+        err.AcquireError();
+        EXPECT_EQ(AcquireErrorInjectorCallCount(), 1u);
+        DisableErrorInjector();
+    }
+
+    // 3. 3D Color Texture
+    {
+        wgpu::TextureDescriptor desc;
+        desc.dimension = wgpu::TextureDimension::e3D;
+        desc.size = {4, 4, 4};
+        desc.format = wgpu::TextureFormat::RGBA8Unorm;
+        desc.usage = wgpu::TextureUsage::TextureBinding;
+        wgpu::Texture tex = device.CreateTexture(&desc);
+
+        Texture* texGL = ToBackend(FromAPI(tex.Get()));
+
+        EnableErrorInjector();
+        InjectErrorAt(0u);
+        MaybeError err =
+            texGL->EnsureSubresourceContentInitialized(gl, texGL->GetAllSubresources());
+        EXPECT_TRUE(err.IsError());
+        err.AcquireError();
+        EXPECT_EQ(AcquireErrorInjectorCallCount(), 1u);
+        DisableErrorInjector();
+    }
+}
+
+// Test that CopyImageSubData checks framebuffer completeness for both read and draw framebuffers.
+TEST_P(GLFramebufferCompletenessTests, CopyImageSubDataChecksCompleteness) {
+    Device* deviceGL = ToBackend(FromAPI(device.Get()));
+    const OpenGLFunctions& gl = deviceGL->GetGL();
+
+    GLuint srcTex = 0;
+    GLuint dstTex = 0;
+    gl.GenTextures(1, &srcTex);
+    gl.GenTextures(1, &dstTex);
+    gl.BindTexture(GL_TEXTURE_2D, srcTex);
+    gl.TexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 4);
+    gl.BindTexture(GL_TEXTURE_2D, dstTex);
+    gl.TexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 4);
+
+    EnableErrorInjector();
+
+    // 1. Error on GL_READ_FRAMEBUFFER (index 0)
+    {
+        InjectErrorAt(0u);
+        MaybeError err = CopyImageSubData(gl, Aspect::Color, srcTex, GL_TEXTURE_2D, 0, {0, 0, 0},
+                                          dstTex, GL_TEXTURE_2D, 0, {0, 0, 0}, {4, 4, 1});
+        EXPECT_TRUE(err.IsError());
+        err.AcquireError();
+        EXPECT_EQ(AcquireErrorInjectorCallCount(), 1u);
+    }
+
+    // 2. Error on GL_DRAW_FRAMEBUFFER (index 1)
+    {
+        InjectErrorAt(1u);
+        MaybeError err = CopyImageSubData(gl, Aspect::Color, srcTex, GL_TEXTURE_2D, 0, {0, 0, 0},
+                                          dstTex, GL_TEXTURE_2D, 0, {0, 0, 0}, {4, 4, 1});
+        EXPECT_TRUE(err.IsError());
+        err.AcquireError();
+        EXPECT_EQ(AcquireErrorInjectorCallCount(), 2u);
+    }
+
+    DisableErrorInjector();
+    gl.DeleteTextures(1, &srcTex);
+    gl.DeleteTextures(1, &dstTex);
+}
+
+#endif  // defined(DAWN_ENABLE_ERROR_INJECTION)
+
 DAWN_INSTANTIATE_TEST(GLFramebufferCompletenessTests, OpenGLESBackend());
 
 }  // anonymous namespace
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/src/dawn/tests/white_box/GLFramebufferCompletenessTests.cpp b/src/dawn/tests/white_box/GLFramebufferCompletenessTests.cpp
index 0a9fc63..d58cfaf 100644
--- a/src/dawn/tests/white_box/GLFramebufferCompletenessTests.cpp
+++ b/src/dawn/tests/white_box/GLFramebufferCompletenessTests.cpp
@@ -26,7 +26,9 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "src/dawn/native/ErrorData.h"
+#include "src/dawn/native/ErrorInjector.h"
 #include "src/dawn/native/opengl/DeviceGL.h"
+#include "src/dawn/native/opengl/TextureGL.h"
 #include "src/dawn/native/opengl/UtilsGL.h"
 #include "src/dawn/tests/DawnTest.h"
 
@@ -88,6 +90,125 @@
                  GLFramebufferStatusAsString(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT));
 }
 
+#if defined(DAWN_ENABLE_ERROR_INJECTION)
+
+// Test that Texture::EnsureSubresourceContentInitialized (which calls Texture::ClearTexture)
+// verifies framebuffer completeness before clearing.
+// Injecting GL_FRAMEBUFFER_UNSUPPORTED on glCheckFramebufferStatus mimics OpenGL ES drivers
+// where certain formats (such as floating-point textures on drivers without
+// GL_EXT_color_buffer_float, or drivers returning GL_FRAMEBUFFER_UNSUPPORTED) cannot be attached
+// as complete framebuffer attachments. On such drivers, glClearBuffer* generates
+// GL_INVALID_FRAMEBUFFER_OPERATION and performs no writes; verifying completeness ensures Dawn
+// catches the error instead of silently marking uninitialized/dirty GPU memory as initialized.
+TEST_P(GLFramebufferCompletenessTests, LazyClearChecksCompleteness) {
+    Device* deviceGL = ToBackend(FromAPI(device.Get()));
+    const OpenGLFunctions& gl = deviceGL->GetGL();
+
+    // 1. 2D Color Texture (e.g. RGBA16Float)
+    {
+        wgpu::TextureDescriptor desc;
+        desc.size = {4, 4, 1};
+        desc.format = wgpu::TextureFormat::RGBA16Float;
+        desc.usage = wgpu::TextureUsage::TextureBinding;
+        wgpu::Texture tex = device.CreateTexture(&desc);
+
+        Texture* texGL = ToBackend(FromAPI(tex.Get()));
+
+        EnableErrorInjector();
+        InjectErrorAt(0u);
+        MaybeError err =
+            texGL->EnsureSubresourceContentInitialized(gl, texGL->GetAllSubresources());
+        EXPECT_TRUE(err.IsError());
+        err.AcquireError();
+        EXPECT_EQ(AcquireErrorInjectorCallCount(), 1u);
+        DisableErrorInjector();
+    }
+
+    // 2. Depth/Stencil Texture
+    {
+        wgpu::TextureDescriptor desc;
+        desc.size = {4, 4, 1};
+        desc.format = wgpu::TextureFormat::Depth24PlusStencil8;
+        desc.usage = wgpu::TextureUsage::TextureBinding;
+        wgpu::Texture tex = device.CreateTexture(&desc);
+
+        Texture* texGL = ToBackend(FromAPI(tex.Get()));
+
+        EnableErrorInjector();
+        InjectErrorAt(0u);
+        MaybeError err =
+            texGL->EnsureSubresourceContentInitialized(gl, texGL->GetAllSubresources());
+        EXPECT_TRUE(err.IsError());
+        err.AcquireError();
+        EXPECT_EQ(AcquireErrorInjectorCallCount(), 1u);
+        DisableErrorInjector();
+    }
+
+    // 3. 3D Color Texture
+    {
+        wgpu::TextureDescriptor desc;
+        desc.dimension = wgpu::TextureDimension::e3D;
+        desc.size = {4, 4, 4};
+        desc.format = wgpu::TextureFormat::RGBA8Unorm;
+        desc.usage = wgpu::TextureUsage::TextureBinding;
+        wgpu::Texture tex = device.CreateTexture(&desc);
+
+        Texture* texGL = ToBackend(FromAPI(tex.Get()));
+
+        EnableErrorInjector();
+        InjectErrorAt(0u);
+        MaybeError err =
+            texGL->EnsureSubresourceContentInitialized(gl, texGL->GetAllSubresources());
+        EXPECT_TRUE(err.IsError());
+        err.AcquireError();
+        EXPECT_EQ(AcquireErrorInjectorCallCount(), 1u);
+        DisableErrorInjector();
+    }
+}
+
+// Test that CopyImageSubData checks framebuffer completeness for both read and draw framebuffers.
+TEST_P(GLFramebufferCompletenessTests, CopyImageSubDataChecksCompleteness) {
+    Device* deviceGL = ToBackend(FromAPI(device.Get()));
+    const OpenGLFunctions& gl = deviceGL->GetGL();
+
+    GLuint srcTex = 0;
+    GLuint dstTex = 0;
+    gl.GenTextures(1, &srcTex);
+    gl.GenTextures(1, &dstTex);
+    gl.BindTexture(GL_TEXTURE_2D, srcTex);
+    gl.TexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 4);
+    gl.BindTexture(GL_TEXTURE_2D, dstTex);
+    gl.TexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 4);
+
+    EnableErrorInjector();
+
+    // 1. Error on GL_READ_FRAMEBUFFER (index 0)
+    {
+        InjectErrorAt(0u);
+        MaybeError err = CopyImageSubData(gl, Aspect::Color, srcTex, GL_TEXTURE_2D, 0, {0, 0, 0},
+                                          dstTex, GL_TEXTURE_2D, 0, {0, 0, 0}, {4, 4, 1});
+        EXPECT_TRUE(err.IsError());
+        err.AcquireError();
+        EXPECT_EQ(AcquireErrorInjectorCallCount(), 1u);
+    }
+
+    // 2. Error on GL_DRAW_FRAMEBUFFER (index 1)
+    {
+        InjectErrorAt(1u);
+        MaybeError err = CopyImageSubData(gl, Aspect::Color, srcTex, GL_TEXTURE_2D, 0, {0, 0, 0},
+                                          dstTex, GL_TEXTURE_2D, 0, {0, 0, 0}, {4, 4, 1});
+        EXPECT_TRUE(err.IsError());
+        err.AcquireError();
+        EXPECT_EQ(AcquireErrorInjectorCallCount(), 2u);
+    }
+
+    DisableErrorInjector();
+    gl.DeleteTextures(1, &srcTex);
+    gl.DeleteTextures(1, &dstTex);
+}
+
+#endif  // defined(DAWN_ENABLE_ERROR_INJECTION)
+
 DAWN_INSTANTIATE_TEST(GLFramebufferCompletenessTests, OpenGLESBackend());
 
 }  // anonymous namespace
Loading diff…

Original Bug Report

reported by [email protected]

GPU Memory Leak in Dawn GL Texture::ClearTexture via Missing FBO Completeness Check

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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: In Dawn’s OpenGL backend, Texture::ClearTexture performs lazy zero-initialization using temporary framebuffers without checking framebuffer completeness. When an incomplete framebuffer is encountered, glClearBuffer* silently fails in release builds, but the subresource is still marked initialized. Web content could potentially sample the uninitialized texture to disclose uninitialized GPU memory across origins.

Affected files:

  • third_party/dawn/src/dawn/native/opengl/TextureGL.cpp
  • third_party/dawn/src/dawn/native/opengl/PhysicalDeviceGL.cpp

Estimated timestamp from git blame: 2026-07-20

Summary

In Dawn’s OpenGL backend (src/dawn/native/opengl/TextureGL.cpp), Texture::ClearTexture implements lazy zero-clearing of textures by creating temporary framebuffers (FBOs), attaching the target subresource, and issuing glClearBuffer*. However, Texture::ClearTexture does not verify framebuffer completeness via CheckFramebufferComplete() before issuing clear calls. If the framebuffer is incomplete, glClearBuffer* generates GL_INVALID_FRAMEBUFFER_OPERATION and performs no writes, but release builds swallow the GL error and unconditionally mark the subresource as initialized. This can allow web applications to read uninitialized GPU process memory.

Root Cause Analysis

  1. Missing Completeness Gate: Dawn introduced CheckFramebufferComplete(const OpenGLFunctions& gl, GLenum target) in src/dawn/native/opengl/UtilsGL.h:59 to guarantee that callers relying on framebuffer operations never proceed past an incomplete framebuffer. While this check was integrated into CommandBufferGL.cpp for render passes, resolves, and readbacks, it is absent in Texture::ClearTexture (TextureGL.cpp:431-472 for depth/stencil and TextureGL.cpp:525-585 for color):

    // third_party/dawn/src/dawn/native/opengl/TextureGL.cpp:525-580
    GLuint framebuffer = 0;
    DAWN_GL_TRY(gl, GenFramebuffers(1, &framebuffer));
    DAWN_GL_TRY(gl, BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer));
    ...
    DAWN_TRY(FramebufferTextureHelper(gl, mTarget, GL_DRAW_FRAMEBUFFER,
                                      attachment, GetTextureHandle(), level, layer));
    DAWN_TRY(DoClear()); // Issues glClearBufferfv / glClearBufferuiv / glClearBufferiv
    
  2. Silent Failure in Release Builds: In release builds (!defined(DAWN_ENABLE_ASSERTS)), DAWN_GL_TRY(gl, call) expands to (gl.call) (UtilsGL.h:85). Per OpenGL ES 3.2 §9.4.4, calling glClearBuffer* on an incomplete draw framebuffer generates GL_INVALID_FRAMEBUFFER_OPERATION and performs no write operation. The error is not checked and execution continues normally.

  3. Unconditional State Update: Regardless of whether the clear succeeded, TextureGL.cpp:657-658 unconditionally marks the subresource as initialized:

    if (clearValue == TextureBase::ClearValue::Zero) {
        SetIsSubresourceContentInitialized(true, range);
        device->IncrementLazyClearCountForTesting();
    }
    
  4. Incomplete FBO Precondition: Format.cpp:255-301 marks floating-point formats (R16Float, RG16Float, RGBA16Float, R32Float, RG32Float, RGBA32Float) as Cap::Renderable unconditionally. Meanwhile, PhysicalDeviceGL.cpp:161-164 permits GLES 3.2 contexts without checking for GL_EXT_color_buffer_float, relying on a debug-only assertion at PhysicalDeviceGL.cpp:455-459. Under GLES 3.2 core specifications, float formats are not required to be color-renderable; on drivers without GL_EXT_color_buffer_float (or on drivers returning GL_FRAMEBUFFER_UNSUPPORTED), attaching a float texture yields an incomplete framebuffer, triggering this silent clear failure.

Potential Trigger Steps

(Note: These are potential steps based on source code analysis; our analysis tooling does not have code-execution capabilities to run live PoCs.)

  1. Run Chrome on an Android platform where WebGPU defaults to the OpenGLES backend (or where compatibility mode is active).
  2. From web JavaScript, request a WebGPU adapter and device.
  3. Create an uninitialized floating-point texture: const tex = device.createTexture({format: 'rgba16float', size: [256, 256, 1], usage: GPUTextureUsage.TEXTURE_BINDING});.
  4. Create a compute pipeline and shader that reads tex using WGSL textureLoad() and writes values into a storage buffer.
  5. Encode and submit a command buffer executing the compute pass and copying the storage buffer to a mappable readback buffer.
  6. During submit, LazyClearSyncScope (CommandBufferGL.cpp:849) invokes ClearTexture(). On affected GLES drivers, the temporary FBO is incomplete, glClearBufferfv silently no-ops, and Dawn marks the subresource initialized.
  7. The compute shader executes and reads stale texel data from raw GPU driver memory.
  8. The web page calls buffer.mapAsync(GPUMapMode.READ) to read the uninitialized GPU memory bytes.

Impact

Potential disclosure of uninitialized GPU process memory across origins to web JavaScript. On platforms such as Android where the GPU process is shared across origins and unsandboxed, leaked heap data may include other origins’ rendered graphics or GPU process internal memory.

Suggested Fix

  1. Add DAWN_TRY(CheckFramebufferComplete(gl, GL_DRAW_FRAMEBUFFER)); immediately following texture attachment in Texture::ClearTexture (TextureGL.cpp:467, TextureGL.cpp:573, and TextureGL.cpp:579), or
  2. Fall back to CPU buffer upload via DoTexSubImage (TextureGL.cpp:588-656) if the format is not confirmed to be color-renderable by the underlying GL driver.
  3. Ensure SetIsSubresourceContentInitialized(true, range) is only called after the clear has verifiably succeeded.

Evaluated with Chrome root at commit: c6718ebbf2c677c6f63691e28c7801c563f6b19c


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