Medium watch CVSS 4.3 chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in GPU
DescriptionInappropriate implementation in GPU
ComponentGPU
Bug ClassLogic Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
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.
Tracker511738693
Fix commit397e5b9cfb29 (chromium/src) +116/-3
CWECWE-346 (Origin validation error)
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-10 (per vendor advisory)
Fix landed2026-06-23 (public git)
Disclosed2026-07-29
Patch gap36 days — fix public in source before the advisory

Background

`DawnEGLImageRepresentation`
a shared-image representation that lets Dawn (WebGPU) access a GPU shared image through an EGLImage backed by an underlying GL representation.
`usage` vs `internal_usage`
usage is the publicly requested texture usage (what the client asked for), while internal_usage is additional usage Dawn needs internally (e.g. lazy-clear writes, render-attachment/copy targets).
`ToSharedImageAccessGLMode`
converts a wgpu::TextureUsage into the GL access mode (read vs write) used by the shared-image locking layer to serialize access.
Read/write access locking
the shared-image system permits concurrent readers but must forbid a reader while a writer holds access; the access mode passed to BeginAccess drives this.

Root Cause Analysis

DawnEGLImageRepresentation::BeginAccess computed the inner GL access mode from usage alone (ToSharedImageAccessGLMode(usage)), ignoring internal_usage. When a caller requested a read-only public usage but a write-capable internal_usage (for example a lazy clear or render-attachment write on an uncleared image), the underlying gl_representation_->BeginAccess was told the access was a read, even though Dawn would internally write the image. This broke the invariant that the access mode reflects the strongest operation performed, so the locking layer treated a writer as a reader and admitted a concurrent GL reader on the same backing during the Dawn internal write.

The fix ORs the two usages together — ToSharedImageAccessGLMode(usage | internal_usage) — so any write implied by internal_usage is reflected in the GL access mode, causing the lock to correctly reject concurrent readers. The PoC test Dawn_WriteUnderReadLock_POC encodes this: it opens a Dawn access with read-only usage and writable internal_usage, then asserts that a second GL representation’s read access on the same mailbox is rejected.

Key insight
The inner GL access mode was derived from the public usage only, dropping the write intent carried by internal_usage, so Dawn’s internal writes were locked as reads; the fix folds internal_usage into the mode via usage | internal_usage.

Attack Path

  1. Create a shared image usable by Dawn and GL A client creates a thread-safe shared image with WebGPU and GLES usages, left uncleared so Dawn will perform an internal lazy-clear (a write) on first use.
  2. Begin a mislabeled Dawn access Via ProduceDawn/BeginScopedAccess, request a read-only public usage but a write-capable internal_usage; the old code computes GL mode from usage only, so the inner BeginAccess runs in read mode.
  3. Admit a concurrent reader during a write While the Dawn access performs its internal write, a second GL representation begins read access on the same backing, which the locking layer wrongly permits.
  4. Concurrent read/write on the backing The reader observes or races the in-flight Dawn write, an inappropriate/unsynchronized access to the shared image. (inferred)

Impact Assessment

An attacker driving Dawn access on a shared image gains a concurrent GL read during a Dawn internal write within the GPU process, an inappropriate-implementation flaw in the shared-image locking, as the advisory states (“Inappropriate implementation in GPU”). Preconditions include the Dawn OpenGLES/EGLImage path with DawnInternalUsages and an image whose internal usage writes (e.g. lazy clear) while another representation reads. The advisory reports no exploited-in-the-wild status; any resulting memory-corruption or information-leak primitive is (inferred).

Changed Functions

FunctionChangeNotes
TEST_P
gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
modified

Files Changed

  • gpu/command_buffer/service/shared_image/dawn_egl_image_representation.cc
  • gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc

Audit Directions

  • Dropped internal_usage in access-mode computation
    Audit other shared-image representations (Dawn/Skia/GL variants) that convert usage to an access mode, ensuring internal_usage is combined with usage wherever internal writes can occur.
  • Read/write classification of lazy operations
    Check that implicit writes such as lazy-clear, mipmap generation, or copy-on-first-use are classified as writes for locking, not reads.
  • Cross-backend wrappers
    Review representations that wrap another representation’s BeginAccess and confirm the wrapper forwards the full, strongest access intent rather than a subset.
From 397e5b9cfb29b5e738704c1780144be027a1330a Mon Sep 17 00:00:00 2001
From: Stephen White <[email protected]>
Date: Tue, 23 Jun 2026 04:12:12 -0700
Subject: [PATCH] Reland "Fix missing internal_usage in DawnEGLImageRepresentation"

This reverts commit 2513b16d406c5adfca74170a384517a1c1355cc4.

Reason for revert: test failures on Pixel 2 suppressed

Original change's description:
> Revert "Fix missing internal_usage in DawnEGLImageRepresentation"
>
> This reverts commit cff2ee05d52d1e882e6e91bb4e3e76aa4a4663b2.
>
> Reason for revert: test failure on Pixel 2.
>
> Failure Link: https://ci.chromium.org/ui/p/chromium/builders/ci/Android%20FYI%20Release%20(Pixel%202)/93457/blamelist
>
> Original change's description:
> > Fix missing internal_usage in DawnEGLImageRepresentation
> >
> > Bug: 511738693
> > Change-Id: I0bbf545cafcb414dee8058f140b3091d21da6d82
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7964199
> > Commit-Queue: Stephen White <[email protected]>
> > Reviewed-by: Corentin Wallez <[email protected]>
> > Cr-Commit-Position: refs/heads/main@{#1649660}
>
> Bug: 511738693
> Change-Id: Ia9164d592d57df790d2c601ae71a39c2b0db9581
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7962098
> Owners-Override: Quyen Le <[email protected]>
> Bot-Commit: [email protected] <[email protected]>
> Commit-Queue: Quyen Le <[email protected]>
> Cr-Commit-Position: refs/heads/main@{#1649952}

Bug: 511738693
Change-Id: I468fe7172e8671e280e5c21f3248442f8bc741db
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7970754
Bot-Commit: [email protected] <[email protected]>
Reviewed-by: Corentin Wallez <[email protected]>
Commit-Queue: Corentin Wallez <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1650906}
---

diff --git a/gpu/command_buffer/service/shared_image/dawn_egl_image_representation.cc b/gpu/command_buffer/service/shared_image/dawn_egl_image_representation.cc
index 7b52a0ea2..298303e 100644
--- a/gpu/command_buffer/service/shared_image/dawn_egl_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/dawn_egl_image_representation.cc
@@ -64,7 +64,8 @@
 wgpu::Texture DawnEGLImageRepresentation::BeginAccess(
     wgpu::TextureUsage usage,
     wgpu::TextureUsage internal_usage) {
-  if (!gl_representation_->BeginAccess(ToSharedImageAccessGLMode(usage))) {
+  if (!gl_representation_->BeginAccess(
+          ToSharedImageAccessGLMode(usage | internal_usage))) {
     return nullptr;
   }
   wgpu::TextureDescriptor texture_descriptor;
diff --git a/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
index 3b24e0f..6b174f5 100644
--- a/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
@@ -49,7 +49,10 @@
 #if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
 #include <dawn/dawn_proc.h>
 #include <dawn/native/DawnNative.h>
+#include <dawn/native/OpenGLBackend.h>
 #include <dawn/webgpu_cpp.h>
+
+#include "ui/gl/gl_implementation.h"
 #endif  // BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
 
 using testing::AtLeast;
@@ -391,9 +394,11 @@
   EXPECT_EQ(dst_pixels[3], 255);
 }
 
+#if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
+
 // TODO(crbug.com/332947916): fix these tests to run on Android/GLES
-#if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES) && \
-    !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
+
 // Test to check interaction between Dawn and skia GL representations.
 TEST_P(EGLImageBackingFactoryThreadSafeTest, Dawn_SkiaGL) {
   // Find a Dawn GLES adapter
@@ -641,6 +646,113 @@
 
   dawnProcSetProcs(nullptr);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
+
+// Verify that DawnEGLImageRepresentation::BeginAccess includes `internal_usage`
+// when computing the inner GL access mode.
+TEST_P(EGLImageBackingFactoryThreadSafeTest, Dawn_WriteUnderReadLock_POC) {
+  // 1) Create the backing on the original GL context BEFORE touching Dawn,
+  //    so EGLImage creation uses a known-good current context.
+  ASSERT_TRUE(context_state_->MakeCurrent(surface_.get(), /*needs_gl=*/true));
+
+  const auto mailbox = Mailbox::Generate();
+  const auto format = viz::SinglePlaneFormat::kRGBA_8888;
+  const gfx::Size size(4, 4);
+  const auto color_space = gfx::ColorSpace::CreateSRGB();
+  const gpu::SharedImageUsageSet usage =
+      SHARED_IMAGE_USAGE_WEBGPU_READ | SHARED_IMAGE_USAGE_WEBGPU_WRITE |
+      SHARED_IMAGE_USAGE_GLES2_READ | SHARED_IMAGE_USAGE_DISPLAY_READ;
+
+  auto backing = backing_factory_->CreateSharedImage(
+      mailbox,
+      {format, size, color_space, kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
+       usage, "POC_WriteUnderReadLock"},
+      gpu::kNullSurfaceHandle, /*is_thread_safe=*/true);
+  ASSERT_NE(backing, nullptr);
+  // Leave it uncleared so Dawn will lazy-clear (an internal write) on first
+  // sample. IsCleared()==false is what a renderer using WEBGPU_MAILBOX_DISCARD
+  // would arrange.
+  ASSERT_FALSE(backing->IsCleared());
+
+  std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
+      shared_image_manager_->Register(std::move(backing),
+                                      memory_type_tracker_.get());
+
+  // 2) Create a Dawn GLES device. featureLevel=Compatibility is required for
+  //    GLES adapter enumeration to succeed.
+  DawnProcTable procs = dawn::native::GetProcs();
+  dawnProcSetProcs(&procs);
+
+  dawn::native::Instance instance;
+  wgpu::RequestAdapterOptions adapter_options;
+  adapter_options.backendType = wgpu::BackendType::OpenGLES;
+  adapter_options.featureLevel = wgpu::FeatureLevel::Compatibility;
+  // Share Chrome's EGL display & GL proc loader with Dawn so the EGLImage
+  // created on Chrome's ANGLE display is importable by Dawn (this mirrors
+  // what WebGPUDecoderImpl does in production).
+  dawn::native::opengl::RequestAdapterOptionsGetGLProc get_gl_proc = {};
+  get_gl_proc.getProc = gl::GetGLProcAddress;
+  gl::GLDisplayEGL* gl_display = gl::GLSurfaceEGL::GetGLDisplayEGL();
+  get_gl_proc.display = gl_display ? gl_display->GetDisplay() : EGL_NO_DISPLAY;
+  adapter_options.nextInChain = &get_gl_proc;
+  std::vector<dawn::native::Adapter> adapters =
+      instance.EnumerateAdapters(&adapter_options);
+  ASSERT_FALSE(adapters.empty()) << "No Dawn GLES adapter";
+
+  wgpu::FeatureName dawn_internal_usage = wgpu::FeatureName::DawnInternalUsages;
+  wgpu::DeviceDescriptor device_descriptor;
+  device_descriptor.requiredFeatureCount = 1;
+  device_descriptor.requiredFeatures = &dawn_internal_usage;
+  wgpu::Device device =
+      wgpu::Device::Acquire(adapters[0].CreateDevice(&device_descriptor));
+  ASSERT_NE(device, nullptr);
+
+  // Dawn made its own EGL context current; restore ours so ProduceDawn (which
+  // creates a GL sibling on the current context) and subsequent GL work use
+  // the original display/context.
+  ASSERT_TRUE(context_state_->MakeCurrent(surface_.get(), /*needs_gl=*/true));
+
+  // 3) Produce the DawnEGLImageRepresentation and begin a scoped access with
+  //    a read-only public `usage` but a write-capable `internal_usage`.
+  auto dawn_representation = shared_image_representation_factory_->ProduceDawn(
+      mailbox, device, wgpu::BackendType::OpenGLES, {}, context_state_);
+  ASSERT_TRUE(dawn_representation);
+
+  const wgpu::TextureUsage kReadOnlyUsage = wgpu::TextureUsage::TextureBinding;
+  const wgpu::TextureUsage kWritableInternal =
+      wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopyDst;
+
+  auto dawn_access = dawn_representation->BeginScopedAccess(
+      kReadOnlyUsage, kWritableInternal,
+      SharedImageRepresentation::AllowUnclearedAccess::kYes);
+  ASSERT_TRUE(dawn_access)
+      << "DawnEGLImageRepresentation::BeginAccess failed (egl wrap)";
+
+  // 4) While the Dawn access (which authorises internal writes) is open, a
+  //    second GL representation tries to begin READ access on the same
+  //    backing. With correct locking this MUST be rejected (concurrent
+  //    reader during a write).
+  auto gl_reader =
+      shared_image_representation_factory_->ProduceGLTexturePassthrough(
+          mailbox);
+  ASSERT_TRUE(gl_reader);
+
+  auto reader_access = gl_reader->BeginScopedAccess(
+      GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
+      SharedImageRepresentation::AllowUnclearedAccess::kYes);
+  bool concurrent_read_admitted = (reader_access != nullptr);
+
+  EXPECT_FALSE(concurrent_read_admitted);
+  reader_access.reset();
+
+  gl_reader.reset();
+  dawn_access.reset();
+  dawn_representation.reset();
+
+  device = wgpu::Device();
+  dawnProcSetProcs(nullptr);
+  factory_ref.reset();
+}
 #endif  // BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
index 3b24e0f..6b174f5 100644
--- a/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
@@ -49,7 +49,10 @@
 #if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
 #include <dawn/dawn_proc.h>
 #include <dawn/native/DawnNative.h>
+#include <dawn/native/OpenGLBackend.h>
 #include <dawn/webgpu_cpp.h>
+
+#include "ui/gl/gl_implementation.h"
 #endif  // BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
 
 using testing::AtLeast;
@@ -391,9 +394,11 @@
   EXPECT_EQ(dst_pixels[3], 255);
 }
 
+#if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
+
 // TODO(crbug.com/332947916): fix these tests to run on Android/GLES
-#if BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES) && \
-    !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
+
 // Test to check interaction between Dawn and skia GL representations.
 TEST_P(EGLImageBackingFactoryThreadSafeTest, Dawn_SkiaGL) {
   // Find a Dawn GLES adapter
@@ -641,6 +646,113 @@
 
   dawnProcSetProcs(nullptr);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
+
+// Verify that DawnEGLImageRepresentation::BeginAccess includes `internal_usage`
+// when computing the inner GL access mode.
+TEST_P(EGLImageBackingFactoryThreadSafeTest, Dawn_WriteUnderReadLock_POC) {
+  // 1) Create the backing on the original GL context BEFORE touching Dawn,
+  //    so EGLImage creation uses a known-good current context.
+  ASSERT_TRUE(context_state_->MakeCurrent(surface_.get(), /*needs_gl=*/true));
+
+  const auto mailbox = Mailbox::Generate();
+  const auto format = viz::SinglePlaneFormat::kRGBA_8888;
+  const gfx::Size size(4, 4);
+  const auto color_space = gfx::ColorSpace::CreateSRGB();
+  const gpu::SharedImageUsageSet usage =
+      SHARED_IMAGE_USAGE_WEBGPU_READ | SHARED_IMAGE_USAGE_WEBGPU_WRITE |
+      SHARED_IMAGE_USAGE_GLES2_READ | SHARED_IMAGE_USAGE_DISPLAY_READ;
+
+  auto backing = backing_factory_->CreateSharedImage(
+      mailbox,
+      {format, size, color_space, kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
+       usage, "POC_WriteUnderReadLock"},
+      gpu::kNullSurfaceHandle, /*is_thread_safe=*/true);
+  ASSERT_NE(backing, nullptr);
+  // Leave it uncleared so Dawn will lazy-clear (an internal write) on first
+  // sample. IsCleared()==false is what a renderer using WEBGPU_MAILBOX_DISCARD
+  // would arrange.
+  ASSERT_FALSE(backing->IsCleared());
+
+  std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
+      shared_image_manager_->Register(std::move(backing),
+                                      memory_type_tracker_.get());
+
+  // 2) Create a Dawn GLES device. featureLevel=Compatibility is required for
+  //    GLES adapter enumeration to succeed.
+  DawnProcTable procs = dawn::native::GetProcs();
+  dawnProcSetProcs(&procs);
+
+  dawn::native::Instance instance;
+  wgpu::RequestAdapterOptions adapter_options;
+  adapter_options.backendType = wgpu::BackendType::OpenGLES;
+  adapter_options.featureLevel = wgpu::FeatureLevel::Compatibility;
+  // Share Chrome's EGL display & GL proc loader with Dawn so the EGLImage
+  // created on Chrome's ANGLE display is importable by Dawn (this mirrors
+  // what WebGPUDecoderImpl does in production).
+  dawn::native::opengl::RequestAdapterOptionsGetGLProc get_gl_proc = {};
+  get_gl_proc.getProc = gl::GetGLProcAddress;
+  gl::GLDisplayEGL* gl_display = gl::GLSurfaceEGL::GetGLDisplayEGL();
+  get_gl_proc.display = gl_display ? gl_display->GetDisplay() : EGL_NO_DISPLAY;
+  adapter_options.nextInChain = &get_gl_proc;
+  std::vector<dawn::native::Adapter> adapters =
+      instance.EnumerateAdapters(&adapter_options);
+  ASSERT_FALSE(adapters.empty()) << "No Dawn GLES adapter";
+
+  wgpu::FeatureName dawn_internal_usage = wgpu::FeatureName::DawnInternalUsages;
+  wgpu::DeviceDescriptor device_descriptor;
+  device_descriptor.requiredFeatureCount = 1;
+  device_descriptor.requiredFeatures = &dawn_internal_usage;
+  wgpu::Device device =
+      wgpu::Device::Acquire(adapters[0].CreateDevice(&device_descriptor));
+  ASSERT_NE(device, nullptr);
+
+  // Dawn made its own EGL context current; restore ours so ProduceDawn (which
+  // creates a GL sibling on the current context) and subsequent GL work use
+  // the original display/context.
+  ASSERT_TRUE(context_state_->MakeCurrent(surface_.get(), /*needs_gl=*/true));
+
+  // 3) Produce the DawnEGLImageRepresentation and begin a scoped access with
+  //    a read-only public `usage` but a write-capable `internal_usage`.
+  auto dawn_representation = shared_image_representation_factory_->ProduceDawn(
+      mailbox, device, wgpu::BackendType::OpenGLES, {}, context_state_);
+  ASSERT_TRUE(dawn_representation);
+
+  const wgpu::TextureUsage kReadOnlyUsage = wgpu::TextureUsage::TextureBinding;
+  const wgpu::TextureUsage kWritableInternal =
+      wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopyDst;
+
+  auto dawn_access = dawn_representation->BeginScopedAccess(
+      kReadOnlyUsage, kWritableInternal,
+      SharedImageRepresentation::AllowUnclearedAccess::kYes);
+  ASSERT_TRUE(dawn_access)
+      << "DawnEGLImageRepresentation::BeginAccess failed (egl wrap)";
+
+  // 4) While the Dawn access (which authorises internal writes) is open, a
+  //    second GL representation tries to begin READ access on the same
+  //    backing. With correct locking this MUST be rejected (concurrent
+  //    reader during a write).
+  auto gl_reader =
+      shared_image_representation_factory_->ProduceGLTexturePassthrough(
+          mailbox);
+  ASSERT_TRUE(gl_reader);
+
+  auto reader_access = gl_reader->BeginScopedAccess(
+      GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
+      SharedImageRepresentation::AllowUnclearedAccess::kYes);
+  bool concurrent_read_admitted = (reader_access != nullptr);
+
+  EXPECT_FALSE(concurrent_read_admitted);
+  reader_access.reset();
+
+  gl_reader.reset();
+  dawn_access.reset();
+  dawn_representation.reset();
+
+  device = wgpu::Device();
+  dawnProcSetProcs(nullptr);
+  factory_ref.reset();
+}
 #endif  // BUILDFLAG(USE_DAWN) && BUILDFLAG(DAWN_ENABLE_BACKEND_OPENGLES)
 
 CreateAndValidateSharedImageRepresentations::
Loading diff…

Original Bug Report

reported by [email protected]

Potential cross-origin GPU memory leak via DawnEGLImageRepresentation missing internal_usage

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: DawnEGLImageRepresentation computes its GL access mode using only the usage parameter, ignoring internal_usage. This potential mismatch allows Dawn to perform writes while the backing holds a non-exclusive read-lock. Concurrent readers can then sample uninitialized GPU memory containing cross-origin data.

Affected files:

  • gpu/command_buffer/service/shared_image/dawn_egl_image_representation.cc
  • gpu/command_buffer/service/shared_image/shared_image_representation.cc

Estimated timestamp from git blame: 2026-04-27

Final Conclusion

The vulnerability is highly exploitable, potentially allowing an attacker to disclose uninitialized, cross-origin GPU memory via a timeline race condition. Note: these are suggested/potential steps, our tooling agent doesn’t yet have the ability to run code.

Technical Details

Initial logic and parameters for SharedImage creation and mailbox association are validated. Standard processing applied for EGLImageBacking allocation. Standard processing applied for WebGPU context binding.

Due to the missing internal_usage bitmask in ToSharedImageAccessGLMode, Dawn receives a writable RenderAttachment texture while the backing records GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM. This mismatch allows a secondary read operation from a parallel GPU context to instantly sample the texture memory without waiting on a write_fence_. The concurrent read bypasses driver-level synchronization and directly captures the uninitialized, pre-clear VRAM residue before Dawn’s lazy-clear write executes on the GPU timeline.

Potential Reproduction Steps

  1. Initial logic and IPC parameters for WebGPU mailbox association are validated.
  2. Standard processing applied for EGLImageBacking read-lock acquisition.
  3. The concurrent read bypasses driver-level synchronization and directly captures the uninitialized VRAM residue before Dawn’s lazy-clear write executes.

Suggested Fix

Update DawnEGLImageRepresentation::BeginAccess to compute the access mode using usage | internal_usage, identical to DawnGLTextureRepresentation::BeginAccess.

Evaluated with Chrome root at commit: eca8648a4e1cdfdda68c495a6003059fed641955


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