← Chrome — 2026-W02

b0caaa118ecc2c0898460cc6dbaf5aa38bc71591  [ImageCapture] Check CanvasSnapshotProvider::IsValid()

severity low class UAF CVSS 3.7 confidence 0.5 signal score 3.8 third_party/blink/renderer AI review CONFIRMED
Primitive: ImageCaptureFrameGrabber reused a snapshot provider without validating it, so a provider with a lost/invalid GPU context could be reused.
Triage note: Adds a !IsValid() guard in the frame grabber. Defensive hardening (bug 40230609 cluster); watch, not exploitable.

The bug at a glance

This adds an IsValid() guard before reusing a cached CanvasSnapshotProvider in the ImageCapture frame grabber. Without it, a provider whose GPU context was lost or otherwise became invalid could be reused across frames, driving snapshot/readback against an invalid GPU resource. That is a latent-safety hardening change: the primitive described upstream is ‘reuse of a provider with a lost/invalid GPU context,’ which is a use-after-invalidation / bad-state condition, not a demonstrated attacker-controlled free-and-reuse. GPU context loss is largely environmental (driver reset, TDR, tab/GPU-process churn) rather than directly attacker-controlled and deterministic, and the change is a defensive validity check rather than a fix to a proven exploitable free. The old bug id (40230609, a legacy-numbered issue) and the minimal one-condition diff support treating it as watch-tier: real latent safety motive, low confirmed severity. CVSS 3.7 reflects low, condition-dependent availability/integrity impact.

ImageCaptureFrameGrabber caches a CanvasSnapshotProvider and reuses it whenever size/color-space/alpha-type still match. The reuse predicate never checked whether the provider itself was still valid. If the underlying GPU context was lost between frames, the cached provider is stale but structurally ‘matches,’ so it is reused instead of recreated. The angle: a validity dimension was missing from the reuse cache key, so a resource that should be considered dead was kept alive and driven.

Root cause

In image_capture_frame_grabber.cc the recreate decision was if (!snapshot_provider_ || Size() mismatch || GetColorSpace() mismatch || GetAlphaType() mismatch). This covers null and format-mismatch but not context validity. The patch inserts !snapshot_provider_->IsValid() as an additional recreate trigger, so a provider that reports itself invalid (e.g. lost GPU context / dropped backing) forces reconstruction of a fresh provider before performing the snapshot, rather than operating on a provider whose GPU-backed state is no longer usable. This closes the window where a stale/invalid provider is reused for readback.

Key code

Force provider recreation when the cached provider reports itself invalid (e.g. lost GPU context)

-  if (!snapshot_provider_ ||
+  if (!snapshot_provider_ || !snapshot_provider_->IsValid() ||
       snapshot_provider_->Size() != frame->natural_size() ||
       snapshot_provider_->GetColorSpace() != dest_color_space ||
       snapshot_provider_->GetAlphaType() != alpha_type) {

Patch walkthrough

  • third_party/blink/renderer/modules/imagecapture/image_capture_frame_grabber.cc — The reuse-vs-recreate condition for snapshot_provider_ gains a !snapshot_provider_->IsValid() clause. Placed as the second short-circuit term (after the null check, before the Size/ColorSpace/AlphaType comparisons), it ensures IsValid() is only called on a non-null provider and that an invalid provider is treated the same as a missing or mismatched one, triggering recreation. No other logic changes; the subsequent branch already handles allocating a new provider.

Background

CanvasSnapshotProvider — An abstraction that supplies a snapshot (readback) of GPU/canvas-backed content. It exposes IsValid(), Size(), GetColorSpace() and GetAlphaType(); IsValid() reports whether the underlying (often GPU) resource is still usable.

ImageCaptureFrameGrabber — Blink component behind the ImageCapture API (grabFrame / takePhoto path) that captures a video frame into a bitmap, caching a snapshot provider between frames for efficiency.

GPU context loss — The GPU process/context can be lost (driver reset, TDR, out-of-memory, process restart). Resources bound to the lost context become invalid; using them yields undefined behavior or crashes and must be recreated on a fresh context.

Vulnerability window

  1. introduction — Introduced when the frame grabber’s provider-reuse cache condition was written without a validity check, implicitly assuming a matching provider is always usable.
  2. report — Associated with legacy Bug 40230609; reuse of an invalid provider surfaced as a robustness/safety concern around GPU context loss.
  3. fix — 2026-01-08, Dale Curtis added the IsValid() guard (CL 7406763), reviewed by Colin Blundell.

Proof of concept

No test shipped and no exploit demonstrated. Repro sketch: use ImageCapture (grabFrame in a loop) on a GPU-backed video source, then induce GPU context loss (e.g. driver reset, or a WEBGL_lose_context-style GPU churn in a cooperating tab) between grabs so the cached snapshot provider becomes invalid while size/color-space/alpha-type still match. On the unpatched code the stale provider is reused for the next snapshot. The concrete consequence ranges from a benign failed readback to operating on freed/invalid GPU-backed state; it is not shown to be a controllable UAF, so this is documented as a latent-safety recipe, not an exploit.

Exploitation

  1. precondition — A page using ImageCapture repeatedly on a GPU-backed source, plus a GPU context-loss event between captures with unchanged frame format.
  2. effect — Reuse of an invalid provider performs snapshot/readback against dead GPU state; outcome is condition-dependent (failure or bad-state access) and not demonstrated to be attacker-controlled corruption.

Detection & hunting

For defenders and detection engineers:

  • ImageCapture grabFrame crashes/GPU errors following context loss — Crash or GPU-error telemetry in ImageCaptureFrameGrabber snapshot paths correlated with GPU context-loss events on pre-fix builds are candidate matches.

Audit directions

  • Other cached GPU-resource reuse predicates — Grep Blink for cached providers/resources reused on format-match without an IsValid()/context-validity check (canvas, video, WebGL, media snapshot paths). Missing validity dimensions in reuse cache keys are the recurring pattern here.
  • GPU context-loss handling in media capture — Audit ImageCapture/MediaStream capture paths to confirm every cached GPU-backed object is invalidated or revalidated on context loss before reuse.

Before / after

Loading diff…