Fission site-isolation bypass via missing PipelineId namespace check
Summary
Under Fission each content process owns a PipelineId namespace. WebRenderBridgeParent::AddPipelineIdForCompositable, which runs in the GPU process, validated the pipeline against the root guard and this bridge's own set but never checked that the PipelineId's namespace belonged to the sending process, unlike the image, font, and blob key paths in the same file. A compromised content process could therefore register a PipelineId in another process's namespace and overwrite that origin's async image pipeline in the shared manager.
Root cause
The MatchesNamespace check that the resource-key paths enforce is missing on the PipelineId path, and on RemovePipelineIdForCompositable. AddAsyncImagePipeline guards uniqueness only with MOZ_ASSERT, a no-op in release builds, so the shared AsyncImagePipelineManager silently overwrites the victim's pipeline instead of rejecting the duplicate.
Proof of concept
- The threat model is an already-compromised content process. The PoC is a content-side patch guarded by XRE_IsContentProcess that forges the PipelineId namespace, plus an HTML test case with a WebGL canvas and a cross-origin out-of-process iframe.
- Launch with the forged namespace and load the test page.
- The GPU-process parent accepts and registers a PipelineId whose namespace belongs to no such process.
# illustrative log from a vulnerable build with the research patch applied, not a live capture # 999999 is an arbitrary forged probe value chosen for this test, not a captured victim namespace $ MOZ_POC_FORGE_WR_NAMESPACE=999999 ./mach run --temp-profile poc.html [POC-2056558] ACCEPTED cross-namespace PipelineId: sender-namespace=4 pipeline-namespace=999999 (should have been rejected)
Tools
- A local Firefox source build with a small research patch, guarded by XRE_IsContentProcess, that forges the outgoing PipelineId namespace and logs the accepted registration in the GPU process.
- An HTML test case with a WebGL canvas and a cross-origin out-of-process iframe, so Fission places the iframe in a separate content process.
- Firefox headless to load and capture the test page rendering on a stock build.
Impact
Demonstrated: a compromised content process can register a PipelineId in another process's namespace and have it accepted, silently overwriting the victim's entry in the shared pipeline manager, a Fission site-isolation authorization bypass. Inferred from that overwrite, but not separately captured here: cross-origin rendering confusion in the victim tab. This is not memory corruption; downstream consumers are null-guarded and reference-counted, so it does not yield code execution. The AddAsyncImagePipeline uniqueness guard is a MOZ_ASSERT, which is compiled out in release builds but would abort the process in a debug build, so a debug-build crash is possible where a release build silently overwrites instead.
Fix
Enforce the same namespace ownership check on the PipelineId path that the resource-key paths already use, rejecting any PipelineId whose namespace does not belong to the sending process.
if (!MatchesNamespace(aPipelineId)) {
return IPC_FAIL(this, "PipelineId namespace does not belong to the sending process");
}