← All reports

WebGL: Copy context to NativeImage in straight orientation

Component: WebCore graphics | fbe51f1

Source/WebCore/platform/graphics/angle/GraphicsContextGLANGLE.cpp

+static void flipPixelBufferRows(PixelBuffer& pixelBuffer) {
+ ...
+ for (auto bytes = pixelBuffer.bytes(); bytes.size() >= rowStride * 2; ...) {
+ // swap top/bottom rows in place
+ }
+}
 
-RefPtr<NativeImage> GraphicsContextGLANGLE::copyNativeImageYFlipped(SurfaceBuffer source)
+RefPtr<NativeImage> GraphicsContextGLANGLE::copyNativeImage(SurfaceBuffer source)
{
...
+ flipPixelBufferRows(*pixelBuffer);
return createNativeImageFromPixelBuffer(contextAttributes(), pixelBuffer.releaseNonNull());
}

Source/WebCore/PAL/pal/spi/cg/CoreGraphicsSPI.h

+CGImageProviderRef CGImageProviderCreate(CGSize, CGImageComponentType, CGColorSpaceRef, void* info, const void* callbacks, CFDictionaryRef auxiliaryInfo);
+CGImageBlockSetRef CGImageBlockSetCreate(CGImageProviderRef, CGSize, CGRect, size_t count, const CGImageBlockRef blocks[], void* info, const CGImageBlockSetCallbacks*);
+CGImageRef CGImageCreateWithImageProvider(CGImageProviderRef, const CGFloat* decode, bool shouldInterpolate, CGColorRenderingIntent);

NativeImage는 WebCore에서 사용되는 컨텍스트 간 pixel handle입니다. CGImage 또는 IOSurface 기반으로 구현되며, 렌더링된 콘텐츠가 서로 다른 canvas 타입 사이를 이동하거나 compositor로 전달될 때마다 사용됩니다. GraphicsContextGL은 실제 GL 렌더링을 GPU process에서 out-of-process로 수행합니다. Web process는 RemoteGraphicsContextGLProxy를 통해 이를 호출하며, 이 요청은 IPC를 거쳐 RemoteGraphicsContextGL로 전달되고, 결과로 생성된 NativeImage가 다시 반환됩니다.

이번 커밋은 copyNativeImageYFlippedcopyNativeImage로 대체하며, vertical flip 작업을 별도의 ImageBuffer에 뒤집힌 콘텐츠를 그리는 방식 대신 copy 단계 안으로 옮깁니다. Cocoa 경로에서는 BlitFramebuffer를 통해 IOSurface 기반 CVPixelBuffer로 flip을 수행합니다. 반면 cross-platform ANGLE 경로에서는 새로 추가된 flipPixelBufferRows() 헬퍼가 읽어들인 PixelBuffercreateNativeImageFromPixelBuffer() 호출 전에 in-place로 뒤집습니다. 새로 추가된 CoreGraphics image-provider SPI 덕분에 Cocoa 경로의 NativeImage는 IOSurface를 직접 wrap해서 CoreAnimation에 넘길 수 있게 되었습니다. 이에 맞춰 NativeImage::create()와 GPU-process IPC 메시지들도 함께 재구성되었고, 더 이상 필요 없어진 drawing-buffer copy-on-write 관련 코드(prepareForDrawingBufferWrite/IfBound, IOSurfaceDrawingBuffer.cpp)는 제거되었습니다.

Before:
WebGL draw ──► CGIOSurfaceContext (flipped) ──► CGImage wraps it (y-flipped NativeImage)
           ──► drawn flipped into extra ImageBuffer ──► copied out
  (copy-on-write triggered on next WebGL draw/context discard)

After (Cocoa, GraphicsContextGLCocoa::copyNativeImage):
WebGL draw ──► BlitFramebuffer flip into IOSurface-backed CVPixelBuffer
           ──► NativeImage::create(CVPixelBufferRef) ──► CGImageProviderCreate wraps IOSurface
           ──► handed to CA directly

After (generic ANGLE, GraphicsContextGLANGLE::copyNativeImage):
WebGL draw ──► readCompositedResults() into PixelBuffer ──► flipPixelBufferRows() (in-place CPU row swap)
           ──► createNativeImageFromPixelBuffer()

WebGL에서 NativeImage로 전달되는 매 과정마다 발생하던 불필요한 copy/draw 단계를 제거하며, 2D, WebGL, WebGPU, bitmaprenderer 컨텍스트 간 zero-copy pixel 공유를 위한 사전 작업 성격을 명확히 드러냅니다. 새로 추가된 SPI는 AllowedSPI.toml[[temporary-usage]] 항목 아래 등록되어 있습니다. 즉 WebKit에서의 이 SPI 사용은 영구적인 의존이 아니라, 별도 rdar를 통해 정리 대상으로 추적되고 있다는 의미입니다.

앞으로 주목해야 할 패턴은, attacker가 제어하는 dimension 값이 callback 기반 image provider로 흘러들어가는 구조입니다. 이 provider가 기대하는 buffer 크기는 생성 시점에 한 번 정해지고, 이후 사용 시점에는 다시 계산되지 않습니다. 좁게 보면, 여기서 CGImageProviderCreate는 callback 구조체(copyImageBlockSet, copyIOSurface, releaseInfo)로 IOSurface를 wrap하는데, 이때 WebGL canvas의 dimension과 format이 info로 함께 전달됩니다. attacker가 제어 가능한 canvas 크기나 premultiplied-alpha flag가 CG가 기대하는 buffer 크기와 실제 할당된 크기를 어긋나게 만들 수 있는지, 그리고 flipPixelBufferRowsNativeImageCG.cppcopyIOSurface에서 이루어지는 크기 계산이 서로 일치하는지 확인할 필요가 있습니다. 넓게 보면, 같은 형태의 패턴이 WebCore 내 다른 지점에서도 나타납니다. provider나 callback 구조체에 할당과 별도로 협상된 크기 값을 넘기는 곳들입니다. CGDataProviderCreateWithData 사용처, media 파이프라인에서의 CVPixelBuffer wrapping, WebGPU texture import가 여기에 해당합니다. 이런 패턴을 찾는 실마리는 생성 호출에 전달되는 크기와 callback 본문 내부에서 사용되는 크기가 서로 별개로 존재하는지를 보는 것입니다. 가장 넓게 보면, Web process에서 GPU process 경계를 넘나드는 IOSurface/CVPixelBuffer 객체의 lifetime과 release-callback 정확성 문제는, release가 상대 측이 타이밍을 제어하는 callback 위에서 실행되는 모든 refcounted handle과 동일한 class of bug에 속합니다. 이와 유사한 구조를 가진 RemoteGraphicsContextGLProxy의 reply 경로나, platform handle을 반환하는 다른 Remote*Proxy 메서드들의 release-ordering 가정도 함께 점검할 필요가 있습니다.