← Chrome — 2026-W31

e16e582198885c627a5ffa4ca5e11f56e6e6030e  [viz] Add checks to reject invalid VTResourceId in SharedElementQuad

severity medium class InputValidation CVSS 6.5 signal score 6.67 services/ AI review CONFIRMED
Structural signals: smartptr_member_added (+4.87), area:services/ (+1.8)

The bug at a glance

A fuzzer-discovered crash in the privileged viz/GPU process: an invalid (default-constructed) ViewTransitionElementResourceId arriving over mojo from a (potentially compromised) renderer was not validated, reaching SharedElementDrawQuad handling in surface_animation_manager and tripping an invalid-token check; the fix rejects it at the IPC boundary and defensively in-method.

A compromised or buggy renderer can send a SharedElementDrawQuad with an invalid element_resource_id across the compositing mojo interface to the GPU process, causing a crash (DoS of the GPU process) and probing a trust-boundary deserialization path.

Root cause

SharedElementQuadState mojom deserialization previously accepted any value ReadElementResourceId produced without checking element_resource_id.IsValid(), so an invalid TransitionToken/resource id flowed into SurfaceAnimationManager where downstream code assumes validity, triggering the fuzzer at surface_animation_manager.cc:272. The patch adds an IsValid() check in the mojo traits Read() (rejecting the message and setting a deserialization crash key) and a defensive guard in surface_animation_manager that logs and returns early on an invalid id. It also reorders the per-quad deserialization so the SharedQuadState is read before quads.Read(i, quad), tightening the deserialization sequence.

Key code

Reject invalid resource id at the mojo deserialization trust boundary

if (!shared_element_quad->element_resource_id.IsValid()) {
    viz::SetDeserializationCrashKeyString(
        "Invalid ViewTransitionElementResourceId in SharedElementDrawQuad");
    return false;
  }

Patch walkthrough

  • services/viz/public/cpp/compositing/quads_mojom_traits.cc — Adds an IsValid() check after ReadElementResourceId in the SharedElementQuadState mojo trait so an invalid resource id causes deserialization failure instead of entering the compositor with a bad token.
  • components/viz/service/transitions/surface_animation_manager.cc — Defensive in-method guard: if the SharedElementDrawQuad’s element_resource_id is invalid, log and return early (treat as handled) instead of proceeding to texture/lookup logic that assumes a valid id (the fuzzer’s crash site).
  • services/viz/public/cpp/compositing/compositor_render_pass_mojom_traits.cc — Reorders deserialization so SharedQuadState is read before quads.Read(i, quad), ensuring quad state is established before the quad is populated during IPC decode.
  • services/viz/public/cpp/compositing/mojom_traits_unittest.cc — Adds tests confirming a valid SharedElementDrawQuad round-trips and an invalid (default-constructed) resource id fails SerializeAndDeserialize, locking in the new validation.

Background

viz / SurfaceAnimationManager — GPU-process compositing service code that manages view-transition shared-element surfaces and quads.

ViewTransitionElementResourceId — Identifier (carrying a TransitionToken) tying a SharedElementDrawQuad to a view-transition element resource; can be default-constructed into an invalid state.

mojom StructTraits Read() — Deserialization hook for IPC messages where untrusted cross-process input must be validated before use.

Vulnerability window

  1. Reachability — A renderer submits a compositor frame containing a SharedElementDrawQuad over the compositing mojo interface to viz.
  2. Trigger — The quad carries an invalid/default ViewTransitionElementResourceId that was not validated during deserialization.
  3. Primitive — SurfaceAnimationManager processes the quad assuming a valid id, reaching the fuzzer-flagged failure/crash at line 272 (DoS of the GPU process).
  4. Escalation — Beyond a crash, feeding unvalidated resource ids into surface lookups could enable confusion of which element/surface is drawn, though no memory-corruption path is shown (inferred).

Proof of concept

Conceptually, from a renderer (or a compromised one) construct a compositor render pass containing a SharedElementDrawQuad whose element_resource_id is left at its default-constructed/invalid value and submit the frame to viz; pre-patch this bypasses validation and reaches SurfaceAnimationManager. No exploit code is provided.

Exploitation

  1. Setup — Compromise or instrument a renderer to craft arbitrary CompositorRenderPass mojo messages (inferred).
  2. Craft — Embed a SharedElementDrawQuad with an invalid ViewTransitionElementResourceId (inferred).
  3. Submit — Send the frame over the compositing interface so viz deserializes and processes it (inferred).
  4. Impact — Pre-patch, trigger the crash/invalid-token path in the GPU process (DoS) (inferred).

Detection & hunting

For defenders and detection engineers:

  • grep for ‘.Read’ IPC traits that populate ids/tokens without a following IsValid()/validity check — Audit mojom StructTraits Read() methods for resource-id/token fields consumed without validation before being handed to service code.
  • LOG(ERROR) ‘Invalid ViewTransitionElementResourceId’ / crash-key ‘Invalid ViewTransitionElementResourceId’ — Monitor GPU-process logs and deserialization crash keys for this signal indicating malformed compositor input.

Audit directions

  • services/viz/public/cpp/compositing/quads_mojom_traits.cc and sibling quad traits — Check every DrawQuad subtype’s mojo Read() for missing validity checks on ids, tokens, resource handles, and rects before they reach viz service logic.
  • components/viz/service/transitions/* — Review other SurfaceAnimationManager entry points that dereference element_resource_id / tokens for the same validity assumption.

Before / after

Loading diff…