← Chrome — 2026-W36

b3572c490ce7a9f4f1b59520a7597c8c2e8cc7cd  [M152] [HiC] Fix privacy leaks due to feature flag checks

severity low class InfoDisclosure CVSS 6.5 signal score 3.8 third_party/blink/renderer/core/ AI review CONFIRMED
Structural signals: area:third_party/blink/renderer/core/ (+2.5), surgical (+1.3)

The bug at a glance

A same-origin subframe that turns on the HTML-in-Canvas (drawElement) origin trial via its own response headers could call drawElementImage with cross-origin content carrying filters, and the main frame’s cross-origin filter-removal code would skip removal because the main frame itself did not enable the feature. Rendering cross-origin content through CSS/SVG filters is a known cross-origin pixel/side-channel exposure path, so a cross-origin privacy leak is in scope; it is medium rather than high because it requires the attacker to control a same-origin subframe that enables the origin trial and relies on filter-based inference rather than direct read of pixels.

The security decision (strip filters from origin-tainted canvas-subtree content) was gated on a feature-flag check evaluated in the wrong document’s execution context. Because the flag was read from the painting frame rather than the frame that actually produced the canvas subtree, an attacker-controlled subframe could enable the trial while the embedding frame reported it disabled, bypassing the filter-removal safeguard.

Root cause

In paint_property_tree_builder.cc the computation of is_filter_disallowed combined three conditions: RuntimeEnabledFeatures::CanvasDrawElementEnabled(execution_context) AND object_.IsInCanvasSubtree() AND operations.OriginTainted(). The feature-flag term is per-ExecutionContext. When HTML-in-Canvas is enabled only in the subframe (via subframe response headers / origin trial), the main frame’s execution context returns false for CanvasDrawElementEnabled, so is_filter_disallowed is false and the origin-tainted filter is kept and applied during painting, exposing cross-origin content through the filter. The fix drops the flag check entirely and relies on state.is_in_canvas_subtree (the actual structural condition) plus OriginTainted(), so origin-tainted filters are removed in any canvas subtree regardless of which frame’s flag is set. The commit notes this is safe because canvas-subtree filters are not exposed to a11y and are otherwise unobservable, so removing them even when HiC is disabled has no downside.

Key code

Filter-removal now keys on the structural canvas-subtree state instead of the per-frame feature flag.

bool is_filter_disallowed =
    state.is_in_canvas_subtree && filter_info.operations.OriginTainted();
if (!(filter_info.operations.IsEmpty() || is_filter_disallowed)) {
  state.filter_info = ...; // filter kept
}

Patch walkthrough

  • third_party/blink/renderer/core/paint/paint_property_tree_builder.cc — Two sites (regular filter at ~L2533 and backdrop-filter at ~L4293) that compute is_filter_disallowed had the condition RuntimeEnabledFeatures::CanvasDrawElementEnabled(object_.GetDocument().GetExecutionContext()) && object_.IsInCanvasSubtree() && OriginTainted(). Both are replaced with state.is_in_canvas_subtree && OriginTainted(), removing the frame-local feature-flag gate so origin-tainted filters inside a canvas subtree are stripped irrespective of whether the painting frame enabled the trial.

Background

HTML-in-Canvas (drawElement / CanvasDrawElement) — An origin-trial feature letting a canvas draw live DOM elements (drawElementImage); the drawn content lives in a ‘canvas subtree’ that is painted but not independently accessible.

OriginTainted filter operations — Filter operations are flagged origin-tainted when their inputs reference cross-origin content; applying/compositing such filters on cross-origin pixels is a cross-origin exposure risk, so Blink removes them from paint property nodes.

Per-ExecutionContext runtime flags — RuntimeEnabledFeatures checks are evaluated against a specific document’s ExecutionContext. A subframe can enable an origin-trial feature through its own response headers independently of its embedder.

IsInCanvasSubtree / state.is_in_canvas_subtree — Structural predicate indicating the LayoutObject is part of content drawn into a canvas; this is the true condition the safeguard should depend on, not the frame-level feature flag.

Vulnerability window

  1. introduction — The filter-removal safeguard was written gating on CanvasDrawElementEnabled(execution_context), implicitly assuming the painting frame and the frame enabling the feature are the same. (inferred)
  2. discovery — Reported as bug 550141694: a non-HiC frame embedding a same-origin subframe that enables the trial could keep cross-origin filters active. (inferred)
  3. fix — Change-Id I3dc8f8d9… replaced the flag check with the structural canvas-subtree check; merged to main @{#1689553} and cherry-picked to M152 (branch 7977) under bugs 556632614,550141694.

Proof of concept

Conceptual only: a page that does not enable HTML-in-Canvas embeds a same-origin iframe whose response headers enable the drawElement origin trial; the iframe uses drawElementImage to draw cross-origin content with a filter applied. Pre-fix the embedder’s painting code keeps the origin-tainted filter, allowing filter-mediated inference about cross-origin pixels. No working exploit is provided and the diff shows no PoC.

Exploitation

  1. setup — Attacker controls a same-origin subframe that enables the HiC origin trial via its own headers while the embedding frame leaves it disabled. (inferred)
  2. trigger — Subframe draws cross-origin content into a canvas subtree with an origin-tainted filter; the embedder paints it without stripping the filter. (inferred)
  3. leak — Filter output varying with cross-origin pixel content provides a cross-origin side channel. (inferred)

Detection & hunting

For defenders and detection engineers:

  • Painted origin-tainted filters inside canvas subtrees — Instrument paint_property_tree_builder to log when FilterInfo with OriginTainted() operations is retained inside a canvas subtree; pre-fix builds retain them when the painting frame’s flag is off.
  • Frame/subframe feature-flag mismatch — Flag documents where a child frame enables CanvasDrawElement but an ancestor does not, which is the exposure precondition.

Audit directions

  • Feature-flag-gated security checks — Audit other RuntimeEnabledFeatures checks that gate cross-origin/privacy safeguards and are evaluated against the painting/embedding frame’s ExecutionContext rather than the frame that produced the content; the correct gate is usually the structural condition.
  • Origin-tainted content handling in canvas/drawElement — Review all code paths that decide whether to keep filters, backdrop-filters, or effects on canvas-subtree / origin-tainted content for similar frame-context confusion.

Before / after

Loading diff…