627c7dc35ba395df3ed86ad662ba56f8111f3bbb [Blink] Fetch ClientSharedImage early in CreateExternalTexture
The bug at a glance
On GPU context loss, lease->GetSharedImage() can return null, and the draw path dereferenced it unchecked (shared_image->size()), a null-pointer dereference that crashes the renderer. Impact is limited to a renderer-process crash with no shown memory-corruption primitive, and triggering it depends on racing a GPU context loss.
An attacker who can induce GPU context loss (e.g. heavy WebGPU/video workload or a reset) while a video frame is imported via WebGPU importExternalTexture can turn the resulting null SharedImage into a renderer crash.
Root cause
CreateExternalTexture() acquired a WebGpuSharedImageLease but only fetched lease->GetSharedImage() at the end. The copy path guarded with IsGpuContextLost(), but the draw path dereferenced lease->GetSharedImage()->size() directly; if context loss made GetSharedImage() return null, this was a null-pointer dereference. The fix fetches shared_image once immediately after acquiring the lease and returns {} if it is null, and adds a post-operation null recheck to catch context loss during the copy/draw.
Key code
Early fetch and null-check of leased SharedImage
scoped_refptr<gpu::ClientSharedImage> shared_image = lease->GetSharedImage();
if (!shared_image) {
return {};
}
...
params.dest_rect = gfx::RectF(shared_image->size());
Patch walkthrough
third_party/blink/renderer/modules/webgpu/external_texture_helper.cc— Fetches lease->GetSharedImage() once right after acquiring the lease and bails on null, removes the outer IsGpuContextLost() guard from the copy path (now redundant), uses the non-null local in both copy and draw paths, and rechecks GetSharedImage() after the operation to detect context loss mid-copy/draw.third_party/blink/renderer/platform/graphics/gpu/webgpu_shared_image_cache.h— Moves WebGpuSharedImageLease::IsGpuContextLost() back to private since callers no longer need the explicit guard, reducing the API surface.
Background
CreateExternalTexture — Blink WebGPU helper that imports a media/video frame into a GPUExternalTexture backed by a leased SharedImage.
WebGpuSharedImageLease — RAII handle over a GPU SharedImage whose GetSharedImage() can become null after GPU context loss.
GPU context loss — A GPU process/context reset invalidates backing resources, causing accessors to return null mid-operation.
Vulnerability window
- Reachability — Web content imports a video frame into WebGPU via importExternalTexture, reaching CreateExternalTexture().
- Trigger — GPU context loss occurs so lease->GetSharedImage() returns null while the draw path proceeds.
- Primitive — The draw path dereferences the null SharedImage (shared_image->size()), crashing the renderer (inferred as a null-deref DoS).
- Escalation — No escalation beyond crash is evident from the diff.
Proof of concept
Conceptually: on a WebGPU device, repeatedly call device.importExternalTexture with a video frame while concurrently forcing GPU context loss (heavy allocation/reset). If the loss lands after the lease is acquired but the draw path reads the SharedImage, GetSharedImage() returns null and is dereferenced. No exploit code provided; this is a crash, not a corruption primitive.
Exploitation
- Setup — Create a WebGPU device and a video source for external-texture import.
- Trigger — Induce GPU context loss concurrent with importExternalTexture (inferred timing dependence).
- Crash — Null SharedImage dereference in the draw path crashes the renderer (inferred).
Detection & hunting
For defenders and detection engineers:
- grep for lease->GetSharedImage()-> or GetSharedImage()->size() without a prior null check — Flag SharedImage accessors dereferenced without null handling across GPU context-loss boundaries.
- renderer null-deref crash stacks in external_texture_helper.cc — Correlate crashes with GPU context-loss events during WebGPU external texture import.
Audit directions
- Other WebGPU/canvas paths using WebGpuSharedImageLease or ClientSharedImage after possible context loss — Ensure GetSharedImage() results are null-checked before and after GPU operations.
- Media PaintCanvasVideoRenderer copy/draw integrations — Audit for unchecked SharedImage dereferences on the draw/paint branch.