Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactObject corruption in Compositing
DescriptionObject corruption in Compositing
ComponentCompositing
Bug ClassLogic Error
Tracker497486030
Fix commit92a617477bcb (chromium/src) +7/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-12

Files Changed

  • components/viz/service/display/display_resource_provider.cc
From 92a617477bcb53e15d84cad0cec866ce9d0c153e Mon Sep 17 00:00:00 2001
From: kylechar <kylechar@chromium.org>
Date: Wed, 01 Apr 2026 06:59:51 -0700
Subject: [PATCH] Guard against ResourceId collisions

Have DisplayResourceProvider check for ResourceId collisions. In theory
a collision is possible after ResourceIdGenerator wraps at the max
value.

Fixed: 497486030
Change-Id: Id652aeea7211dceba7557235658e9b5c0b788e17
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7719378
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Commit-Queue: Kyle Charbonneau <kylechar@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1608494}
---

diff --git a/components/viz/service/display/display_resource_provider.cc b/components/viz/service/display/display_resource_provider.cc
index 4bf9bad2..b0e5505 100644
--- a/components/viz/service/display/display_resource_provider.cc
+++ b/components/viz/service/display/display_resource_provider.cc
@@ -263,8 +263,13 @@
     }
 
     ResourceId local_id = resource_id_generator_.GenerateNextId();
-    resources_.emplace(local_id,
-                       ChildResource(child_id, transferable_resource));
+    bool inserted =
+        resources_
+            .emplace(local_id, ChildResource(child_id, transferable_resource))
+            .second;
+    // Verify there wasn't a ResourceId collision. A collision is only possible
+    // after `resource_id_generator_` hit the max ID and wrapped around.
+    CHECK(inserted);
     child_info.child_to_parent_map[transferable_resource.id] = local_id;
   }
 }
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential Cross-Origin GPU Texture Leak via ResourceIdGenerator Wraparound in Viz

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: A lack of collision detection in ResourceIdGenerator::GenerateNextId() combined with improper duplicate key handling in DisplayResourceProvider::ReceiveFromChild allows a compromised renderer to force a 32-bit ID wraparound. This creates an alias to a cross-origin victim’s GPU resource, which the attacker can then render and exfiltrate.

Affected files:

  • components/viz/common/resources/resource_id.h
  • components/viz/service/display/display_resource_provider.cc
  • components/viz/service/display/resolved_frame_data.cc
  • components/viz/service/display/display_resource_provider_skia.cc

Estimated timestamp from git blame: 2021-08-20

Summary

A potential vulnerability in the viz component’s resource management allows a compromised renderer to access and render GPU textures belonging to cross-origin surfaces (such as iframes or other windows) that share the same Display. The issue stems from a lack of collision detection in ResourceIdGenerator::GenerateNextId() when the 32-bit ID counter wraps around, combined with improper handling of duplicate resource IDs in DisplayResourceProvider::ReceiveFromChild.

Technical Details

In the Chromium compositor (Viz), DisplayResourceProvider is responsible for managing resources (like GPU textures) used for compositing a Display. A single DisplayResourceProvider instance is shared across all surfaces within a Display (e.g., the root surface and all its embedded iframes).

Resources are identified by a ResourceId, which is a 32-bit unsigned integer. When a child (a renderer process) sends resources to Viz via SubmitCompositorFrame, Viz assigns a local_id to each resource using a ResourceIdGenerator.

The generator simply increments a 32-bit next_id_. When next_id_ reaches the reserved range (near UINT32_MAX), it wraps back to start_id_. There is no check to ensure that the new local_id is not already in use by another resource in the DisplayResourceProvider.

In DisplayResourceProvider::ReceiveFromChild, the code handles new resources:

    ResourceId local_id = resource_id_generator_.GenerateNextId();
    resources_.emplace(local_id,
                       ChildResource(child_id, transferable_resource));
    child_info.child_to_parent_map[transferable_resource.id] = local_id;

If local_id already exists in the resources_ map (because it is currently held by a victim surface), resources_.emplace will fail because std::unordered_map::emplace does not overwrite existing entries on duplicate keys. However, the code proceeds to update child_info.child_to_parent_map[transferable_resource.id] = local_id;. This creates an alias: the attacker’s child-side resource ID now maps to the same local_id as the victim’s resource.

When the compromised renderer subsequently submits a CompositorFrame containing a TextureDrawQuad that references this aliased resource ID, the Viz service resolves the ID to the victim’s local_id and retrieves the victim’s ChildResource and its associated GPU mailbox. The victim’s texture is then sampled and rendered into the attacker’s frame.

Potential Attack Scenario

Note: This is a suggested attack path; our tooling agent does not have the ability to run code to confirm this end-to-end.

  1. Renderer Compromise: An attacker compromises a renderer process (e.g., via a V8 bug) and gains the ability to send custom CompositorFrame IPCs to the Viz service.
  2. Victim Setup: The attacker embeds a cross-origin iframe (the victim) that holds a long-lived GPU resource (e.g., a static WebGL canvas or a video frame). This resource occupies a local_id in the shared DisplayResourceProvider.
  3. Resource Flooding: The attacker churns ResourceIds by repeatedly calling SubmitCompositorFrame with a large number of dummy resources. Since SubmitCompositorFrame allows [UnlimitedSize] arrays, the attacker can quickly cycle through the 4.29 billion possible IDs without triggering out-of-memory errors by ensuring the resources are unused and immediately released.
  4. Forcing a Collision: At 60fps with 100,000 resources per frame, the 32-bit ResourceIdGenerator wraps around in approximately 12 minutes. The attacker continues until it generates a local_id that collides with the victim’s resource.
  5. Malicious Aliasing: When the collision occurs, resources_.emplace fails, but the attacker’s child_to_parent_map is successfully updated to alias the victim’s local_id.
  6. Unauthorized Rendering: The attacker submits a TextureDrawQuad using the aliased child-side ID. The victim’s cross-origin texture is rendered within the attacker’s controlled region.
  7. Data Exfiltration: The attacker can then use timing side-channels (such as applying computationally expensive CSS/SVG filters to the leaked texture and measuring rendering performance) to slowly exfiltrate the visual contents of the cross-origin iframe back to their JavaScript environment.

Suggested Fix

In DisplayResourceProvider::ReceiveFromChild, check the result of resources_.emplace to ensure the ID was successfully inserted. If it fails due to a collision, the code should generate a new ID and try again, or crash the child process if it attempts to submit an excessive number of resources that exhaust the 32-bit ID space.

    ResourceId local_id;
    bool inserted = false;
    while (!inserted) {
      local_id = resource_id_generator_.GenerateNextId();
      auto result = resources_.emplace(local_id,
                         ChildResource(child_id, transferable_resource));
      inserted = result.second;
      // Optionally add a safeguard against infinite loops if the ID space is fully exhausted.
    }
    child_info.child_to_parent_map[transferable_resource.id] = local_id;

Evaluated with Chrome root at commit: a9cbf6e8b275fe4147435aa905f3b7f5a656f5f0


Results from 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