High watch CVSS 3.1 chrome Uninitialized Memory 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
3.1
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse of uninitialized resource in GPU
DescriptionUse of uninitialized resource in GPU
ComponentGPU
Sub-componentGPU Security
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.
Tracker536460270
Fix commita73a143f694c (chromium/src) +79/-0
CWECWE-908
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-07-19 (per vendor advisory)
Fix landed2026-07-22 (public git)
Disclosed2026-08-18
Patch gap27 days — fix public in source before the advisory

Background

`CompoundImageBacking`
a GPU shared-image backing composed of several “element” sub-backings (e.g. shared-memory and GPU elements) that must be kept in sync so any accessed backing sees the latest content.
Permanent vs. transient element
permanent elements are the long-lived sub-backings stored in the compound backing, whereas a transient backing is a temporary target that must be initialized (“synced”) from whichever permanent element holds the latest content before it is used.
`NotifyBeginAccess` / `NotifyEndAccess`
hooks invoked when a representation begins/ends accessing a backing; begin-access synchronizes the target from the latest-content element, and end-access proactively copies newly written content back to a permanent element.
`GetElementWithLatestContent()`
returns the permanent element whose content_id_ matches latest_content_id_, or nullptr if none currently holds the latest content.

Root Cause Analysis

In CompoundImageBacking::NotifyBeginAccess (in compound_image_backing.cc), the code obtains the source of truth via ElementHolder* latest_content_element = GetElementWithLatestContent(); and then synchronizes the destination backing from it. If a transient backing had been written but the proactive copy-back in NotifyEndAccess failed, no permanent element carried the latest content id, so GetElementWithLatestContent() returned nullptr.

Pre-patch, the null result was not checked before the sync block, and the sync was silently skipped while the access was still allowed to proceed — letting a caller read/use a destination (e.g. the GPU) backing that was never initialized with valid content.

The fix inserts an early if (!latest_content_element) guard immediately after the call that logs an error and returns false, so the access is aborted exactly like the existing CopyImage()-failure handling; crucially, it does not advance content versioning, so a later Update() (for SHM-backed images) can re-establish a latest-content element and let access recover. The regression test AccessFailsWhenLatestContentUnavailable drives a transient write with no successful copy-back, asserts that neither SHM nor GPU reports latest content, verifies the subsequent GPU read fails and no upload occurred, then confirms access succeeds again after Update().

Key insight
The single mistake was dereferencing/relying on the result of GetElementWithLatestContent() without handling the nullptr case, allowing access to proceed against an unsynchronized (uninitialized) backing; the fix adds an early return false on nullptr before the sync so the caller aborts the access.

Attack Path

  1. Set up a compound backing A client creates a CompoundImageBacking with GPU and shared-memory elements and produces a GPU representation of it.
  2. Write to a transient backing A write access targets a transient backing; NotifyBeginAccess syncs it from shared memory and advances the content version.
  3. Fail the copy-back on end access NotifyEndAccess runs but its proactive copy of the transient content back to a permanent element fails, so no permanent element ends up holding the latest content id.
  4. Begin access with no latest-content element On the next access, GetElementWithLatestContent() returns nullptr; pre-patch the sync block was skipped but access still proceeded.
  5. Use of uninitialized resource The caller reads/uses the never-initialized destination (GPU) backing, exposing uninitialized GPU memory contents (inferred; the diff shows the skipped sync and permitted access, not the concrete disclosure sink).

Impact Assessment

A client of the GPU shared-image service that can induce a failed copy-back for a transient backing can make the service permit access to an unsynchronized, uninitialized backing, resulting in use of an uninitialized GPU resource (potential disclosure of stale/uninitialized GPU memory). The advisory classifies this as use of an uninitialized resource in GPU; no exploited-in-the-wild status is stated, and any escalation beyond reading uninitialized content is (inferred). The precondition is a real-world copy-back failure path (the test simulates it), after which access continues to fail until Update() or resource re-creation.

Changed Functions

FunctionChangeNotes
if
gpu/command_buffer/service/shared_image/compound_image_backing.cc
modified
TEST_F
gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
modified

Files Changed

  • gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
  • gpu/command_buffer/service/shared_image/compound_image_backing.cc

Audit Directions

  • Unchecked "latest/source" lookups feeding a sync
    Search for calls returning an optional/pointer source element (like GetElementWithLatestContent()) whose result is used to synchronize a destination without a nullptr/absent check, letting access proceed on unsynchronized data.
  • Silent skip vs. hard fail on sync failure
    Audit access/begin hooks where a failed or skipped initialization/copy still returns success; the invariant should be that any failure to establish latest content aborts the access (as CopyImage() failure already did here).
  • Proactive copy-back failure handling
    Review other backings where end-access copy-back can fail, and confirm that a later access cannot observe partially-initialized or stale state instead of failing safely.
From a73a143f694c18300b9d87d3d489cddec4124f73 Mon Sep 17 00:00:00 2001
From: vikas soni <[email protected]>
Date: Wed, 22 Jul 2026 11:56:25 -0700
Subject: [PATCH] [GPU Security] Fail CompoundImageBacking access when no latest element.

CompoundImageBacking::NotifyBeginAccess synchronizes the target backing
from the permanent element holding the latest content. If a transient
backing was written but its proactive copy-back in NotifyEndAccess
failed, no permanent element carries the latest content id and
GetElementWithLatestContent() returns nullptr. The sync block was
silently skipped and the access was allowed to proceed against an
unsynchronized backing.

Return false in this case, matching the existing handling for
CopyImage() failure, so the caller aborts the access. Content versioning
is left untouched so a later Update() can restore access. Note that if
there is no SHM backing (e.g. GPU-only backings) or no Update() occurs,
subsequent access calls (including writes) will continue to fail,
requiring client-level context/resource re-creation.

Add a regression test that drives a transient write without a successful
copy-back and verifies read access is refused.

Bug: 536460270
Change-Id: I826b4a872bab19d9532db5f51ca17f137432e4a1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8128128
Reviewed-by: Vasiliy Telezhnikov <[email protected]>
Commit-Queue: vikas soni <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1666517}
---
diff --git a/gpu/command_buffer/service/shared_image/compound_image_backing.cc b/gpu/command_buffer/service/shared_image/compound_image_backing.cc
index 95c3fff3..65bbf02e 100644
--- a/gpu/command_buffer/service/shared_image/compound_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/compound_image_backing.cc
@@ -1256,6 +1256,20 @@
   // it's a transient backing that needs to be initialized. We must find the
   // permanent element that currently holds the latest content and copy from it.
   ElementHolder* latest_content_element = GetElementWithLatestContent();
+  if (!latest_content_element) {
+    // No permanent element holds the most recent content, so the destination
+    // backing cannot be synchronized. This can happen if a transient backing
+    // was written to but its content could not be copied back to a permanent
+    // element on end access. Do not advance versioning so access can recover if
+    // an element re-establishes the latest content (e.g. via Update() for SHM
+    // backings). Note that if there is no SHM backing (e.g. GPU-only backings)
+    // or no Update() occurs, subsequent access calls (including writes) will
+    // continue to fail, requiring client-level context/resource re-creation.
+    LOG(ERROR) << "No element with latest content available for sync to "
+               << backing->GetName();
+    return false;
+  }
+
   bool updated_backing = false;
   bool copy_succeeded = false;
 
diff --git a/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc b/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
index e952d7e..c088b6ea 100644
--- a/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
@@ -507,6 +507,71 @@
   EXPECT_TRUE(GetGpuHasLatestContent(compound_backing));
 }
 
+TEST_F(CompoundImageBackingTest, AccessFailsWhenLatestContentUnavailable) {
+  auto backing = CreateCompoundBacking(
+      {SHARED_IMAGE_USAGE_GLES2_READ, SHARED_IMAGE_USAGE_DISPLAY_READ});
+  auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
+
+  auto factory_rep =
+      manager_.Register(std::move(backing), &memory_type_tracker_);
+
+  auto gl_rep = manager_.ProduceGLTexturePassthrough(
+      compound_backing->mailbox(), &memory_type_tracker_);
+  ASSERT_TRUE(gl_rep);
+  ASSERT_TRUE(HasGpuBacking(compound_backing));
+  auto* gpu_backing = GetGpuBacking(compound_backing);
+
+  // Simulate a write to a transient backing that is not stored as a permanent
+  // element. Begin access syncs it from shared memory and advances the content
+  // version.
+  auto transient = std::make_unique<TestImageBacking>(
+      compound_backing->mailbox(),
+      SharedImageInfo(compound_backing->format(), compound_backing->size(),
+                      compound_backing->color_space(),
+                      compound_backing->surface_origin(),
+                      compound_backing->alpha_type(), compound_backing->usage(),
+                      "Transient"),
+      kTestBackingSize);
+  EXPECT_TRUE(compound_backing->NotifyBeginAccess(
+      transient.get(), RepresentationAccessMode::kWrite,
+      SharedImageAccessStream::kSkia));
+  EXPECT_TRUE(transient->GetUploadFromMemoryCalledAndReset());
+
+  // End access without the transient content being synced back to any
+  // permanent element (as happens when the proactive copy in end access
+  // fails), so no element holds the latest content version.
+  compound_backing->NotifyEndAccess(transient.get(),
+                                    RepresentationAccessMode::kWrite);
+  transient.reset();
+
+  EXPECT_FALSE(GetShmHasLatestContent(compound_backing));
+  EXPECT_FALSE(GetGpuHasLatestContent(compound_backing));
+
+  // A subsequent read on the GPU backing must not proceed since there is no
+  // element to sync content from and the GPU backing was never initialized.
+  {
+    auto gl_access = gl_rep->BeginScopedAccess(
+        GLTextureImageRepresentationBase::kReadAccessMode,
+        SharedImageRepresentation::AllowUnclearedAccess::kNo);
+    EXPECT_FALSE(gl_access);
+  }
+  EXPECT_FALSE(gpu_backing->GetUploadFromMemoryCalledAndReset());
+  EXPECT_FALSE(GetGpuHasLatestContent(compound_backing));
+
+  // After the shared memory element is marked as the latest via Update(),
+  // access should succeed again.
+  compound_backing->Update(nullptr);
+  EXPECT_TRUE(GetShmHasLatestContent(compound_backing));
+  {
+    auto gl_access = gl_rep->BeginScopedAccess(
+        GLTextureImageRepresentationBase::kReadAccessMode,
+        SharedImageRepresentation::AllowUnclearedAccess::kNo);
+    EXPECT_TRUE(gl_access);
+  }
+  EXPECT_TRUE(gpu_backing->GetUploadFromMemoryCalledAndReset());
+  EXPECT_TRUE(GetGpuHasLatestContent(compound_backing));
+}
+
 TEST_F(CompoundImageBackingTest, LazyAllocationFailsCreate) {
   auto backing = CreateCompoundBacking({SHARED_IMAGE_USAGE_GLES2_READ});
   auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc b/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
index e952d7e..c088b6ea 100644
--- a/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/compound_image_backing_unittest.cc
@@ -507,6 +507,71 @@
   EXPECT_TRUE(GetGpuHasLatestContent(compound_backing));
 }
 
+TEST_F(CompoundImageBackingTest, AccessFailsWhenLatestContentUnavailable) {
+  auto backing = CreateCompoundBacking(
+      {SHARED_IMAGE_USAGE_GLES2_READ, SHARED_IMAGE_USAGE_DISPLAY_READ});
+  auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
+
+  auto factory_rep =
+      manager_.Register(std::move(backing), &memory_type_tracker_);
+
+  auto gl_rep = manager_.ProduceGLTexturePassthrough(
+      compound_backing->mailbox(), &memory_type_tracker_);
+  ASSERT_TRUE(gl_rep);
+  ASSERT_TRUE(HasGpuBacking(compound_backing));
+  auto* gpu_backing = GetGpuBacking(compound_backing);
+
+  // Simulate a write to a transient backing that is not stored as a permanent
+  // element. Begin access syncs it from shared memory and advances the content
+  // version.
+  auto transient = std::make_unique<TestImageBacking>(
+      compound_backing->mailbox(),
+      SharedImageInfo(compound_backing->format(), compound_backing->size(),
+                      compound_backing->color_space(),
+                      compound_backing->surface_origin(),
+                      compound_backing->alpha_type(), compound_backing->usage(),
+                      "Transient"),
+      kTestBackingSize);
+  EXPECT_TRUE(compound_backing->NotifyBeginAccess(
+      transient.get(), RepresentationAccessMode::kWrite,
+      SharedImageAccessStream::kSkia));
+  EXPECT_TRUE(transient->GetUploadFromMemoryCalledAndReset());
+
+  // End access without the transient content being synced back to any
+  // permanent element (as happens when the proactive copy in end access
+  // fails), so no element holds the latest content version.
+  compound_backing->NotifyEndAccess(transient.get(),
+                                    RepresentationAccessMode::kWrite);
+  transient.reset();
+
+  EXPECT_FALSE(GetShmHasLatestContent(compound_backing));
+  EXPECT_FALSE(GetGpuHasLatestContent(compound_backing));
+
+  // A subsequent read on the GPU backing must not proceed since there is no
+  // element to sync content from and the GPU backing was never initialized.
+  {
+    auto gl_access = gl_rep->BeginScopedAccess(
+        GLTextureImageRepresentationBase::kReadAccessMode,
+        SharedImageRepresentation::AllowUnclearedAccess::kNo);
+    EXPECT_FALSE(gl_access);
+  }
+  EXPECT_FALSE(gpu_backing->GetUploadFromMemoryCalledAndReset());
+  EXPECT_FALSE(GetGpuHasLatestContent(compound_backing));
+
+  // After the shared memory element is marked as the latest via Update(),
+  // access should succeed again.
+  compound_backing->Update(nullptr);
+  EXPECT_TRUE(GetShmHasLatestContent(compound_backing));
+  {
+    auto gl_access = gl_rep->BeginScopedAccess(
+        GLTextureImageRepresentationBase::kReadAccessMode,
+        SharedImageRepresentation::AllowUnclearedAccess::kNo);
+    EXPECT_TRUE(gl_access);
+  }
+  EXPECT_TRUE(gpu_backing->GetUploadFromMemoryCalledAndReset());
+  EXPECT_TRUE(GetGpuHasLatestContent(compound_backing));
+}
+
 TEST_F(CompoundImageBackingTest, LazyAllocationFailsCreate) {
   auto backing = CreateCompoundBacking({SHARED_IMAGE_USAGE_GLES2_READ});
   auto* compound_backing = static_cast<CompoundImageBacking*>(backing.get());
Loading diff…

Original Bug Report

reported by [email protected]

Potential uninitialized VRAM read in CompoundImageBacking via failed proactive copy-back

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: A vulnerability in CompoundImageBacking allows a compromised renderer to read uninitialized GPU VRAM. If a transient backing’s proactive copy-back fails under memory pressure, subsequent reads skip synchronization, allowing the read of uninitialized textures that bypass the IsCleared() gate.

Affected files:

  • gpu/command_buffer/service/shared_image/compound_image_backing.cc
  • gpu/command_buffer/service/shared_image/wrapped_sk_image_backing.cc
  • gpu/command_buffer/service/shared_image/shared_image_representation.cc

Estimated timestamp from git blame: 2026-05-07

1. Summary of the Issue (Meant for Human Triage)

A potential security vulnerability exists in CompoundImageBacking due to an oversight in handling proactive copy-back failures for transient backings. This vulnerability allows a compromised renderer to bypass synchronization and read physically uninitialized, driver-recycled GPU VRAM, presenting a significant cross-origin data exposure risk (GPU-XO).

When the features::kUseDynamicBackingAllocations flag is enabled (currently FEATURE_DISABLED_BY_DEFAULT), CompoundImageBacking supports dynamic lazy creation of GPU backings. If a transient backing is created (e.g., inside a thread-safe container like DrDC/WebView) and written to, its content must be proactively copied back to permanent backings inside NotifyEndAccess. If this proactive copy-back fails (which an attacker can induce by causing staging-buffer allocation failures via memory-pressure spraying), the code logs an error and continues without updating the permanent backing’s content_id_.

Subsequently, a read access on a lazily allocated GPU backing triggers NotifyBeginAccess. Because no permanent backing matches the latest_content_id_, GetElementWithLatestContent() returns nullptr. This causes the entire synchronization block to be skipped. NotifyBeginAccess returns true (success). Because the CompoundImageBacking container was artificially marked fully cleared upon creation with a shared memory backing, the outer IsCleared() validation passes. The renderer then reads directly from the physically uninitialized backend GPU texture, leaking cross-origin graphical content from recycled driver VRAM.

2. Proof-of-Concept & Detailed Execution Flow

Note: Our tooling agent does not have the ability to run code. The steps below are a potential, theoretically verified sequence based on static analysis of the codebase.

Step-by-Step Sequence:

  1. Precondition: The feature features::kUseDynamicBackingAllocations must be enabled.
  2. Container Creation: A compromised renderer (A-RENDERER) sends a mojom::DeferredSharedImageRequest{kCreateSharedImageWithBuffer} IPC over its GpuChannel to create a CompoundImageBacking backed by a SHARED_MEMORY_BUFFER.
  3. The renderer specifies usage flags (e.g., SHARED_IMAGE_USAGE_DISPLAY_READ | SHARED_IMAGE_USAGE_CPU_WRITE_ONLY) causing SharedImageFactory::CreateSharedImage to fall back to CompoundImageBacking::Create.
  4. During initialization, CompoundImageBacking::ComputeIsThreadSafe evaluates factory->shared_image_manager_->display_context_on_another_thread() to true (e.g., on DrDC or WebView). This forces is_thread_safe to true, making the CompoundImageBacking a thread-safe container.
  5. The constructor initializes the primary shared memory backing as elements_[0], sets has_shm_backing_ = true, and crucially marks the compound container as fully cleared via SetClearedRectInternal(gfx::Rect(size)) (compound_image_backing.cc:1157).
  6. The container initializes latest_content_id_ = 1 and elements_[0].content_id_ = 1.
  7. Transient Write Access: The renderer initiates a Write access on a stream (e.g., kSkia) that triggers dynamic backing allocation.
  8. GetOrAllocateBacking falls back to SharedImageFactory to dynamically allocate a new GPU backing (CreateBackingFromBackingFactory).
  9. Because the compound container is thread-safe but the new GPU backing is not, the code treats it as a transient backing (compound_image_backing.cc:2038). It is returned via out_transient_backing and not added to the permanent elements_ list.
  10. The wrapper calls NotifyBeginAccess on the transient backing with mode = kWrite.
  11. GetElementWithLatestContent() returns elements_[0]. copy_manager_->CopyImage successfully syncs data. latest_content_id_ increments to 2. Because it is transient, access_element is nullptr, so no permanent element’s content_id_ is updated.
  12. Induced Copy Failure: The wrapper calls EndWriteAccess, invoking NotifyEndAccess(backing, kWrite). This detects the transient backing and attempts a proactive copy-back to elements_[0] (compound_image_backing.cc:1361).
  13. The attacker concurrently induces extreme GPU memory pressure (e.g., spraying SharedImages), exhausting staging/transfer buffers. The copy_manager_->CopyImage fails.
  14. The code logs "DCSI: Proactive copy ... failed." but critically continues without updating elements_[0].content_id_ to 2 (compound_image_backing.cc:1370). The transient backing is destroyed. latest_content_id_ is 2, but the permanent element has 1.
  15. Uninitialized Read: The renderer initiates a Read access (e.g., ReadbackARGBImagePixelsINTERNALImmediate) using a stream that requires a new permanent GPU backing (e.g., GLTextureImageBacking).
  16. GetOrAllocateBacking allocates the new GPU backing. Because has_shm_backing_ is true, backing->SetCleared() is called unconditionally (compound_image_backing.cc:2106).
  17. This prematurely marks the new backing as physically initialized, despite containing recycled, uninitialized driver VRAM. It is added as elements_[1] with content_id_ = 0.
  18. NotifyBeginAccess is called with mode = kRead.
  19. GetElementWithLatestContent() looks for an element with content_id_ == 2. Since elements_[0] is 1 and elements_[1] is 0, it returns nullptr (compound_image_backing.cc:1953).
  20. Because latest_content_element is nullptr, the physical synchronization block (if (latest_content_element)) is bypassed entirely (compound_image_backing.cc:1262).
  21. NotifyBeginAccess returns true (success).
  22. The representation wrapper (e.g., SkiaGaneshImageRepresentation::BeginScopedReadAccess) checks IsCleared(). This delegates to CompoundImageBacking::ClearedRect(), which returns the full image bounds set in Step 5.
  23. The gate passes. The renderer reads the newly allocated, physically uninitialized GPU backing. Backend texture allocations (Vulkan, D3D, Metal) do not zero-fill in release builds, exposing recycled driver VRAM to the attacker.

Suggested Fix: In CompoundImageBacking::NotifyBeginAccess, if latest_content_element returns nullptr but latest_content_id_ > 1 (meaning valid data existed but was lost during a failed proactive copy-back), NotifyBeginAccess should explicitly return false to abort the read. Alternatively, in NotifyEndAccess, if the proactive copy-back fails, the container should be marked into a degraded/invalid state that forces subsequent accesses to fail gracefully.

3. Technical Verification Details (Automated Audit Logs)

> Severity: High (S1) > Brief Notes / Reasoning: > The primary variant described in the report is invalid (S4). NotifyBeginAccess is declared as [[nodiscard]] bool, and all representation wrappers (e.g., WrappedSkiaGaneshCompoundImageRepresentation::BeginReadAccess) explicitly check its return value. If CopyImage fails, NotifyBeginAccess returns false, and the wrapper early-returns, safely preventing the read. The report’s claim that it returns void and falls through is incorrect for the current codebase. > > However, the secondary sub-variant (latest_content_element == nullptr) is structurally valid. When features::kUseDynamicBackingAllocations is enabled, a transient backing can be created. During a write access on a transient backing, NotifyBeginAccess increments latest_content_id_. If the proactive copy-back to the SHM backing in NotifyEndAccess fails (e.g., due to staging buffer allocation failure from memory pressure), no permanent element’s content_id_ is updated. A subsequent read on a lazily-allocated GPU backing causes GetElementWithLatestContent() to return nullptr. NotifyBeginAccess skips the copy block entirely and returns true. The read proceeds on the GPU backing, which was marked SetCleared() at creation without being initialized. This results in a cross-origin GPU-memory disclosure (S1 - High Severity). > > Per the severity guidelines, since the valid variant is behind an off-by-default flag (FEATURE_DISABLED_BY_DEFAULT) with no field trial, we assess the severity AS IF the flag were on (S1), but note Security_Impact-None.

Code Reachability Proofs & Evaluated Logic:

  1. ComputeIsThreadSafe Logic: gpu/command_buffer/service/shared_image/compound_image_backing.cc:928-932
    if (!is_thread_safe && base::FeatureList::IsEnabled(features::kUseDynamicBackingAllocations)) {
      is_thread_safe = factory->shared_image_manager_->display_context_on_another_thread();
    }
    
  2. GetOrAllocateBacking transient backing allocation: gpu/command_buffer/service/shared_image/compound_image_backing.cc:2038-2041
    if (is_thread_safe() && !new_backing->is_thread_safe()) {
      out_transient_backing = std::move(new_backing);
      return out_transient_backing.get();
    }
    
  3. NotifyEndAccess Proactive Copy-Back Failure: gpu/command_buffer/service/shared_image/compound_image_backing.cc:1367-1372
    if (copy_manager_->CopyImage(backing, dst_backing)) {
      element.content_id_ = latest_content_id_;
    } else {
      LOG(ERROR) << "DCSI: Proactive copy from " << backing->GetName()
                 << " to " << dst_backing->GetName() << " failed.";
    }
    
  4. NotifyBeginAccess Synchronization Skip: gpu/command_buffer/service/shared_image/compound_image_backing.cc:1258-1262
    ElementHolder* latest_content_element = GetElementWithLatestContent();
    bool updated_backing = false;
    bool copy_succeeded = false;
    if (latest_content_element) {
      // [Bypassed because latest_content_element is nullptr]
    }
    // Falls through to: return true;
    
  5. CreateBackingFromBackingFactory Premature Clear: gpu/command_buffer/service/shared_image/compound_image_backing.cc:2105-2107
    if (has_shm_backing_) {
      backing->SetCleared();
    }
    

Environmental Assumptions:

  • features::kUseDynamicBackingAllocations must be explicitly enabled.
  • Target environments include Vulkan, D3D, or Metal backends where createBackendTexture allocations are recycled and not zero-filled in Release builds to avoid overhead (wrapped_sk_image_backing.cc:232-237).

Evaluated with Chrome root at commit: b96d2ec58f4f5f92b540a723966b199d6e9951b4


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