CVE-2026-16358
Overview
Background
- `WebRenderBridgeParent`
- the parent-process (GPU/compositor) endpoint that services WebRender IPC requests on behalf of a content-process bridge child.
- External image
- a GPU texture or shared surface, referenced by an opaque
wr::ExternalImageId, that WebRender composites without owning the underlying buffer directly. - `OwnsExternalImageId`
- a
CompositorManagerpredicate that reports whether a given external image id was actually registered by this manager/compositor. - Site isolation
- the security boundary that keeps one content process from reading or manipulating another origin’s graphics resources.
Root Cause Analysis
WebRenderBridgeParent::UpdateSharedExternalImage accepted an attacker-controllable aExtId from a content-process IPC message and used it to update a shared surface without first verifying that the receiving bridge/compositor actually owned that external image id. The violated invariant is that a WebRenderBridgeParent may only manipulate external image ids that belong to its own CompositorManager; nothing enforced that a caller’s aExtId fell within the ids this endpoint had legitimately registered.
The fix inserts an ownership gate — GetCompositorBridge()->GetCompositorManager()->OwnsExternalImageId(aExtId) — that logs a gfxCriticalNote and returns false before any surface bookkeeping runs when the id is not owned. This works because it restores the missing cross-boundary check, so a compromised or malicious content process can no longer address another process’s external image resources through this path.
aExtId, letting one bridge act on external images it did not own; the fix adds an explicit OwnsExternalImageId guard that rejects unowned ids before use.Attack Path
- Compromise a content process An attacker running code in a content process gains the ability to craft arbitrary WebRender IPC to the parent.
- Forge an external image id
The attacker sends an
UpdateSharedExternalImagerequest carrying anaExtIdthat belongs to a different origin’s compositor rather than its own. - Bypass the ownership boundary
Before the fix,
UpdateSharedExternalImagenever checked ownership, so it proceeded to update the shared surface keyed by that foreign id. - Cross the isolation boundary The operation manipulates a shared external image outside the caller’s process, breaking the site-isolation guarantee for graphics resources.
Impact Assessment
Files Changed
gfx/layers/wr/WebRenderBridgeParent.cpp
Audit Directions
- Unvalidated IPC identifiersAudit every parent-side WebRender/compositor handler that consumes an id (
ExternalImageId,WrImageKey, surface handles) from a content process for a matching ownership or scope check before use. - Ownership-gate parityCompare sibling methods on
WebRenderBridgeParentandCompositorManagerto confirm each mutation path that touches shared surfaces enforces the sameOwnsExternalImageId-style predicate. - Site-isolation resource boundariesReview shared-surface and texture-sharing code for places where a resource is looked up by caller-supplied key without confirming the caller’s process actually registered it.
Patch
diff --git a/gfx/layers/wr/WebRenderBridgeParent.cpp b/gfx/layers/wr/WebRenderBridgeParent.cpp
index 4f6de1027bd..6a00d9bc1a7 100644
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -990,6 +990,12 @@ bool WebRenderBridgeParent::UpdateSharedExternalImage(
return true;
}
+ if (!GetCompositorBridge()->GetCompositorManager()->OwnsExternalImageId(
+ aExtId)) {
+ gfxCriticalNote << "We do not own extId:" << wr::AsUint64(aExtId);
+ return false;
+ }
+
auto key = wr::AsUint64(aKey);
auto it = mSharedSurfaceIds.find(key);
if (it == mSharedSurfaceIds.end()) {