[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를 사용하면 worker나 다른 context에서 transferControlToOffscreen()을 통해 <canvas>에 렌더링할 수 있습니다. 이때 <canvas> element는 placeholder 역할만 맡게 됩니다. Frame은 PlaceholderRenderingContextSource가 공급하는 async contents delegate를 거쳐 해당 element의 CALayer에 전달됩니다. 다만 기존 PlaceholderRenderingContextSource는 producer가 같은 process에 있다고 가정했습니다.
Site Isolation 환경에서는 사정이 다릅니다. Producer와 placeholder, GPU process의 ImageBuffer가 서로 다른 process에 놓일 수 있고, 그 사이를 UI process가 중재합니다. ImageBuffer 전송 과정에서는 먼저 sender가 handle을 기록합니다. 이후 recipient는 이 handle을 바탕으로 buffer에 대한 자체 description을 구성합니다. 결과적으로 recipient는 sender가 제공한 metadata(size, scale, purpose, rendering mode)를 신뢰하게 됩니다. 또한 IOSurface에는 Mach send right가 함께 실리는데, sender가 이 권한을 계속 보유할 수 있습니다.
이번 commit으로 DetachedOffscreenCanvas를 process 간에 serialize할 수 있게 되었습니다. 이전에는 [NotSerialized] 때문에 cross-site iframe으로 postMessage된 OffscreenCanvas가 아무런 오류 없이 누락되었습니다. 이제는 같은 상황에서도 원래의 <canvas> placeholder에 정상적으로 그려집니다.
구조 측면에서는 PlaceholderRenderingContextSource가 세 부분으로 분리되었습니다. Abstract interface, 같은 process용 LocalPlaceholderRenderingContextSource, 그리고 WebKit 쪽에 새로 추가된 RemotePlaceholderRenderingContextSource입니다. Remote source는 GPU process의 buffer ownership을 이전한 뒤 UI process에 통지합니다. 그러면 UI process가 해당 frame을 placeholder layer에 적용합니다. 이와 함께 전달된 ImageBuffer handle에 대한 검증이 추가되었습니다. Claim된 IOSurface가 recipient의 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
새로운 UI process routing, GPU process buffer handoff, capability 방식의 identifier를 통해 cross-site OffscreenCanvas 전송이 동작하게 되었으며, 그만큼 cross-process trust boundary가 여러 곳 새로 생겼습니다. ImageBuffer hardening이 함께 들어간 점도 눈여겨볼 만합니다. 전송 경로가 이미 거짓 정보를 보내는 sender에 노출되어 있었음을 드러내기 때문입니다.
Audit directions
Cross-process attack surface가 새로 추가되었고 security fix도 포함되어 있어, 우선순위가 높은 점검 대상입니다. 범위를 좁혀 시작할 수 있는 지점은 다음과 같습니다.
- Capability 모델.
PlaceholderRenderingContextIdentifier는 추측할 수 없는 UUID입니다. 이 값을 알기만 하면 해당 placeholder에 frame을 commit할 권한이 생깁니다. 먼저 이 identifier가 serialize되거나 log에 남거나 외부로 노출되는 지점을 살펴볼 필요가 있습니다. 또한 commit하는 process가 실제로 grant를 보유하는지 UI process가 검사하는지 확인해야 합니다(removeOffscreenCanvasPlaceholderGrantsForProcess참고). Process 종료나 navigation 이후 grant가 stale 상태로 남는지도 점검 대상입니다. - 등록 시점의 race. UI process는 아직 알지 못하는 placeholder로 향하는 frame을 보관한 채, claim된 owner에게 ping을 보냅니다. 문제는 canvas가
MessagePort처럼 UI process를 거치지 않는 경로로도 도달할 수 있다는 점입니다. 이 지점에서는 owner process identifier 위조, 보관되는 frame 수의 상한 부재, 보관된 buffer의 use-after-release를 살펴봐야 합니다. Ping 응답이 등록 이후에 도착한다는 순서 가정도 함께 확인할 필요가 있습니다. - Buffer ownership과 lifetime. GPU process의
takeTransferredBuffer경로를 검토해야 합니다. UI process가<canvas>process로 transfer를 전달하는 과정과, superseded buffer가 해제되는 과정도 함께 대상입니다. 특히 double-take, release 이후의 take, 세 process에 걸쳐 ownership이 나뉘는 상황을 확인할 필요가 있습니다. - Handle 검증. Recipient가 description을 구성할 때 사용하는 모든 field를
transferHandleDescribes가 빠짐없이 다루는지 확인해야 합니다. IOSurface no-recycle fix도 다시 살펴볼 만합니다. Send right를 유지한 sender는 recipient가 private으로 간주하는 surface에 여전히 write할 수 있기 때문입니다. - Threading·순서·decoding. 이제
PlaceholderLayerContents는 placeholder와, OffscreenCanvas thread에서 갱신되는 같은 process의 source가 함께 공유합니다. 따라서 lock이 충분히 적용되는지, attach/detach 시점의 lifetime이 올바른지 점검해야 합니다. 한편PlaceholderFrameIdentifier가RenderingResourceIdentifier를 대체했으므로 wraparound와 reset 동작도 확인 대상입니다. 원래 process로 되돌아온 canvas는 remote variant를 계속 사용하므로, 이 경로의 재decode와 identity 처리도 살펴볼 필요가 있습니다. 마지막으로DetachedOffscreenCanvas에 대한 새로운SerializedScriptValuedecode 로직에는 identifier와 process field 검증이 필요합니다.