[Site Isolation] OffscreenCanvas transferred to a cross-site iframe never paints
Component: WebKit Site Isolation / OffscreenCanvas | 28001e7
LayoutTests/http/tests/site-isolation/offscreen-canvas-cross-site-frames-stay-in-order.html
OffscreenCanvas lets a worker or another context render into a <canvas> via transferControlToOffscreen(). The <canvas> element becomes a placeholder, and frames reach its CALayer through an async contents delegate fed by a PlaceholderRenderingContextSource, which previously assumed the producer lived in the same process. Under Site Isolation the producer, the placeholder and the GPU-process ImageBuffers can sit in different processes, with the UI process mediating. ImageBuffer transfer works by the sender writing a handle, after which the recipient builds its own description of the buffer from that handle. The recipient therefore trusts sender-supplied metadata (size, scale, purpose, rendering mode), and IOSurfaces carry Mach send rights the sender may retain.
This commit makes DetachedOffscreenCanvas serializable across processes, so an OffscreenCanvas postMessage'd to a cross-site iframe now paints the original <canvas> placeholder; previously it was dropped silently because of [NotSerialized]. PlaceholderRenderingContextSource is split into an abstract interface, a same-process LocalPlaceholderRenderingContextSource, and a new WebKit-side RemotePlaceholderRenderingContextSource. The remote source transfers GPU-process buffer ownership and notifies the UI process, which applies the frame to the placeholder layer. The commit also adds validation of transferred ImageBuffer handles and stops a claimed IOSurface from being recycled into the recipient's surface pool.
Cross-site iframe (producer) UI Process Placeholder (<canvas>) process
│ │ │
│ │◄── register(PlaceholderID) ──────┤ (on create; unregister on destroy)
│ │◄── layer-changed(layerID) ───────┤
GPU: takeTransferredBuffer │ │
├── commitFrame(ID, frameID, ──────►│ │
│ handle) ├─ known? ─yes─► asyncSetLayerContents(layer) [fast path]
│ ├─ forward buffer-transfer ───────►│ accepts ownership (readback)
│ │ (throttled; superseded frames released)
│ ├─ unknown? hold newest frame, ping claimed owner
│ │◄── pong (follows any registration)
│◄── placeholder gone ─────────────┤ deliver or release held frame
│ (stop sending) │
Significance
Cross-site OffscreenCanvas transfer now works through new UI-process routing, GPU-process buffer handoff and a capability-style identifier, which adds several new cross-process trust boundaries. The ImageBuffer hardening shows the transfer path was already exposed to a lying sender.
Audit directions
This is a high-value target: it adds cross-process attack surface and contains a security fix. Narrow starting points:
- Capability model.
PlaceholderRenderingContextIdentifieris an unguessable UUID, and knowing it grants permission to commit frames to a placeholder. Audit where it is serialized, logged or exposed, whether the UI process checks that the committing process holds the grant (seeremoveOffscreenCanvasPlaceholderGrantsForProcess), and whether grants go stale after process exit or navigation. - Registration races. Frames for an unknown placeholder are held by the UI process while it pings the claimed owner. Canvases can arrive over routes that bypass the UI process, such as
MessagePort. Look for spoofed owner process identifiers, unbounded held frames, use-after-release of held buffers, and the ordering assumption that the ping reply follows the registration. - Buffer ownership and lifetime. Review the GPU-process
takeTransferredBufferpath, the UI process forwarding the transfer to the<canvas>process, and the release of superseded buffers. Check for double-take, take-after-release and ownership split across three processes. - Handle validation. Confirm
transferHandleDescribescovers every field the recipient derives its description from, and revisit the IOSurface no-recycle fix, since a sender that keeps a send right can still write into a surface the recipient treats as private. - Threading, ordering and decoding.
PlaceholderLayerContentsis now shared between the placeholder and a same-process source updated from the OffscreenCanvas thread, so check lock coverage and attach/detach lifetime.PlaceholderFrameIdentifierreplacesRenderingResourceIdentifier, so check wraparound and reset behavior. A canvas that bounces back to its original process stays on the remote variant, so check re-decode and identity handling there. The newSerializedScriptValuedecode logic forDetachedOffscreenCanvasneeds validation of identifier and process fields.