570bf70b7d828ddf96816db824e75887e2432b54 [WebGL] Don't try to reuse an invalid CanvasSnapshotProvider
Triage note: Adds a !IsValid() guard before reuse in WebGL VideoFrame snapshotting. Defensive hardening against using an invalid GPU resource; framed as a precursor refactor (bug 40230609), so watch, not exploitable.
The bug at a glance
The change adds a single validity precondition — !snapshot_provider->IsValid() — to the guard that decides whether a cached CanvasSnapshotProvider can be reused for a WebGL/VideoFrame snapshot. Before the fix, a provider whose underlying GPU context had been lost (or otherwise entered an invalid state) could pass the size/format/alpha reuse test and be reused, so downstream code operated on a provider backed by a dead GPU context. In practice a lost-context provider yields failed/empty snapshots and, in the worst case, dereferences of resources torn down with the GPU context — a source of renderer crashes rather than a demonstrated attacker-controlled UAF. The commit is explicitly framed as a precursor to refactoring how CanvasSnapshotProvider instances are created for VideoFrame objects, i.e. defensive hardening with a latent-safety motive, not a targeted exploit fix. GPU-context loss is largely environmental (driver reset, TDR, resource eviction) and only weakly attacker-inducible from the web, and the failure mode trends toward graceful-failure/crash rather than corruption. That places it honestly at watch-tier: real latent-safety improvement, low severity, CVSS ~3.7.
Induce or await a GPU context loss (aggressive context creation/destruction to force ARB_robustness resets, or driver TDR) while a WebGL context keeps a cached CanvasSnapshotProvider that matches the next requested size/format/alpha, then trigger a snapshot (e.g. VideoFrame creation from the canvas). Pre-fix, the stale-but-matching provider is reused despite its lost context. The attacker leverage is marginal because context loss is mostly non-deterministic and environment-driven.
Root cause
In webgl_rendering_context_base.cc, a loop walks cached snapshot providers and decides whether an existing snapshot_provider can be reused instead of allocating a new one. The reuse condition compared Size(), SharedImageFormat()/requested format, and AlphaType(); if all matched, the provider was reused. It never asked whether the provider itself was still valid. A CanvasSnapshotProvider wraps GPU-backed resources (shared images) tied to a GPU context; when that context is lost, the provider is no longer usable but its cached size/format/alpha metadata still match a new request, so the equality checks pass and the dead provider is reused. The fix prepends !snapshot_provider->IsValid() to the disqualifying condition: if the provider is invalid, the branch that rejects/replaces the provider is taken (rather than reusing it), forcing a fresh, valid provider to be created. The immediately preceding !snapshot_provider break check is unchanged.
Key code
Reuse guard now rejects an invalid (lost-context) provider before comparing size/format/alpha
if (!snapshot_provider) {
break;
}
- if (snapshot_provider->Size() != size ||
+ if (!snapshot_provider->IsValid() || snapshot_provider->Size() != size ||
(snapshot_provider->GetSharedImageFormat() != format &&
requested_formats_[i] != format) ||
snapshot_provider->GetAlphaType() != alpha_type ||
Patch walkthrough
third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc— One-line strengthening of the snapshot-provider reuse predicate: !snapshot_provider->IsValid() is OR’d in first, so an invalid provider short-circuits into the not-reusable path regardless of matching size/format/alpha. This prevents handing a provider whose GPU context is lost to downstream snapshot code, which would otherwise produce failed snapshots or touch torn-down GPU resources. The change is minimal and behavior-preserving for the healthy case (a valid provider still evaluates the same size/format/alpha checks); it only diverges when the cached provider is invalid, where it now correctly declines reuse.
Background
CanvasSnapshotProvider — Blink helper that owns/produces GPU-backed snapshots (shared images) of canvas/WebGL content, used e.g. to create VideoFrame objects from a canvas. It is cached and matched by size/format/alpha so repeated snapshots can reuse backing resources.
IsValid() — Reports whether the provider’s underlying resources/GPU context are still usable. A provider whose GPU context was lost becomes invalid even though its cached descriptor (size/format/alpha) is unchanged.
GPU context loss — The GPU process/driver can drop a context (TDR, driver reset, resource eviction, or robustness resets). WebGL surfaces this as a lost context; resources bound to it are no longer valid. Loss is chiefly environmental and only weakly web-inducible.
VideoFrame-from-canvas path — The commit notes this is a precursor to simplifying CanvasSnapshotProvider creation for VideoFrame objects, indicating the reuse cache is exercised on the canvas-to-VideoFrame snapshot flow that this guard protects.
Vulnerability window
- context — Bug 40230609 is a long-lived WebGL/snapshot tracking item; the reuse cache lacked a validity gate.
- fix — Commit 570bf70b7d (main@{#1564849}, 2026-01-06) by Dale Curtis adds the IsValid() gate, explicitly as a precursor to refactoring VideoFrame CanvasSnapshotProvider creation. Reviewed/CQ by Colin Blundell, Auto-Submit.
- status — Landed as WebGL hardening with no security tag; framed as refactor groundwork. Watch-tier.
Proof of concept
No test ships with this diff, so no PoC is reconstructed and no exploit is demonstrated. Honest repro sketch (crash/failure only, not an exploit): obtain a WebGL context, take a canvas snapshot (e.g. build a VideoFrame from the canvas) so a CanvasSnapshotProvider is cached, then force a GPU context loss (WEBGL_lose_context extension, or provoke a driver reset via heavy allocation/TDR), then immediately request another snapshot with the same size/format/alpha so the cached-but-invalid provider matches. Pre-fix this reuses the lost-context provider, exercising the failure. Because context loss is nondeterministic and the outcome is typically a failed/empty snapshot or a crash on torn-down GPU resources, this is a reliability/robustness recipe, not a controllable primitive; no exploit code is provided.
Exploitation
- trigger — Requires a cached provider whose GPU context has been lost while its size/format/alpha still match the next request. Context loss is mostly environmental; WEBGL_lose_context gives some web-side control but the timing against the cache is fragile.
- primitive — Reuse of a lost-context provider yields failed snapshots and, at worst, access to GPU resources torn down with the context — a crash-class outcome. The diff shows no attacker-controlled write or type-confusion primitive.
- ceiling — Renderer-local, robustness-grade. Non-deterministic trigger, graceful-failure-dominant. Not exploitable-grade; value is chiefly as safety groundwork for the upcoming VideoFrame provider refactor.
Detection & hunting
For defenders and detection engineers:
- Lost-context correlated crashes —
- Snapshot failure telemetry —
Audit directions
- Other cached-GPU-resource reuse predicates —
- VideoFrame CanvasSnapshotProvider refactor follow-ups —
- Context-loss invalidation completeness —