← Chrome — 2026-W40

67f6fef1e489ce1f328a371ac204d123da72128a  [M154] Reinsert PatternData into pattern map after reconstruction

severity high class UAF confidence 0.9 signal score 6.8 third_party/blink/renderer/core/ patch-now AI review CONFIRMED
Primitive: dangling unique_ptr& slot in pattern_map_ cleared during BuildPatternData
Triage note: Bound std::unique_ptr<PatternData>& to pattern_map_.insert().stored_value->value, then BuildPatternData() can clear pattern_map_, invalidating the reference before writing through it. Fix re-inserts via pattern_map_.Set(). M154 cherry-pick; SVG patterns web-reachable.
Structural signals: security_assert (+3.0), area:third_party/blink/renderer/core/ (+2.5), surgical (+1.3)

The bug at a glance

Use-after-free in Blink SVG paint-server layout, reachable from any page that renders an SVG . A dangling reference into a LayoutSVGResourcePattern::pattern_map_ hash-table slot was written through after BuildPatternData re-entrantly cleared/rehashed the map. UAF-write in the renderer is a strong exploitation primitive; High. M154 release-branch cherry-pick.

The bug is an iterator/reference-invalidation UAF: pattern_map_.insert(&client, nullptr).stored_value->value handed out a std::unique_ptr& into a hash-table slot, and BuildPatternData() can re-enter and clear or rehash pattern_map_, invalidating that reference; the subsequent assignment and pattern_data->paint_flags write then landed in a stale/freed slot. M154 branch (refs/branch-heads/8037) cherry-pick of main fix dedbd3f9dee8.

Root cause

LayoutSVGResourcePattern previously bound std::unique_ptr& pattern_data to pattern_map_.insert(&client, nullptr).stored_value->value, a reference directly into a WTF::HashMap slot, then called BuildPatternData(reference_box, paint_flags) and assigned the result back through that reference. BuildPatternData can re-enter layout/paint and cause pattern_map_ to be cleared or rehashed, which invalidates the earlier AddResult and its stored_value reference; writing through the stale reference is a use-after-free write into a freed or moved hash-table bucket. The fix does a pattern_map_.find(&client) up front and takes a raw PatternData* (iter->value.get() or nullptr), builds the new data into a local new_pattern_data, CHECK()s it, and only then re-inserts with pattern_map_.Set(&client, std::move(new_pattern_data)) after reconstruction completes, so no reference spans the re-entrant mutation.

Key code

layout_svg_resource_pattern.cc: don’t hold a map-slot reference across BuildPatternData; Set() afterward

  auto iter = pattern_map_.find(&client);
  PatternData* pattern_data =
      (iter == pattern_map_.end() ? nullptr : iter->value.get());
  if (pattern_data && pattern_data->paint_flags != paint_flags) {
    pattern_data = nullptr;
  }
  if (!pattern_data) {
    auto new_pattern_data = BuildPatternData(reference_box, paint_flags);
    pattern_data = new_pattern_data.get();
    CHECK(pattern_data);
    pattern_map_.Set(&client, std::move(new_pattern_data));
    pattern_data->paint_flags = paint_flags;
  }

Patch walkthrough

  • third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc — Replaces the std::unique_ptr& bound to pattern_map_.insert(…).stored_value->value with a find() + raw PatternData* snapshot. BuildPatternData’s result is held in a local, CHECK()d non-null, and re-inserted via pattern_map_.Set() only after reconstruction, so no reference or iterator survives a re-entrant clear/rehash. M154 branch cherry-pick of main commit dedbd3f9dee8 (Fixed: 565742179, branch bug 567398861).

Background

LayoutSVGResourcePattern / pattern_map_ — Blink layout object for an SVG paint server. pattern_map_ is a per-resource HashMap from client layout object to std::unique_ptr.

PatternData — Cached pattern paint state (including paint_flags) built by BuildPatternData and stored in pattern_map_.

WTF::HashMap AddResult / stored_value — insert() returns an AddResult whose stored_value points into the bucket. The reference/iterator is invalidated if the map is mutated (cleared, rehashed) before use.

BuildPatternData re-entrancy — Constructing pattern data can run further layout/paint that invalidates SVG resources and clears pattern_map_, so any reference held across it may dangle.

Vulnerability window

  1. Pre-patch — Pattern resolution held a unique_ptr& into a pattern_map_ slot across BuildPatternData, which could clear the map and invalidate that slot -> UAF write.
  2. Main fix 2026-09-29 — Lands dedbd3f9dee8 on main (CL 8464440), Fixed: 565742179.
  3. M154 cherry-pick — Cherry-picked to refs/branch-heads/8037 as 67f6fef1e4 (CL 8483415, branch bug 567398861).

Proof of concept

No test or PoC ships in this cherry-pick diff. Conceptually (inferred): render an SVG that fills an element with a url(#pattern) paint server and arrange for pattern reconstruction to re-enter layout/paint that invalidates the pattern resource, so pattern_map_ is cleared while a reference into it is live; the stale reference is then written. Web-facing reachability is direct: SVG painting is triggered by ordinary markup/CSS/script.

Exploitation

  1. Trigger — A page paints an element using an SVG paint server, and causes pattern reconstruction to re-enter and clear/rehash pattern_map_ during BuildPatternData (inferred).
  2. Free / invalidate — The hash-table clear or rehash frees/moves the bucket the held unique_ptr& referenced, leaving a dangling reference (inferred).
  3. UAF write — Assigning the new unique_ptr and writing pattern_data->paint_flags through the stale reference corrupts freed/reallocated heap; heap grooming could turn this into a controlled write primitive (inferred).

Detection & hunting

For defenders and detection engineers:

  • Reference/iterator into a WTF::HashMap held across a call that can mutate the map — Flag T& x = map.insert(…).stored_value->value (or an iterator) kept live across a non-trivial call; the fixed pattern re-looks-up or Set()s after the call returns.
  • AddResult.stored_value used after a potentially re-entrant callee — Any use of stored_value following a build/layout/paint helper is suspect; re-entrant SVG resource invalidation is a known cause of pattern_map_ clears.
  • ASan heap-use-after-free WRITE in layout/svg paint-server code — Watch for UAF writes in LayoutSVGResourcePattern / SVG resource containers during pattern painting.

Audit directions

  • Sibling SVG paint servers — Review LayoutSVGResourceGradient and other LayoutSVGResourceContainer subclasses for the same insert().stored_value->value-held-across-build pattern.
  • HashMap AddResult lifetime across callbacks — Grep Blink for .insert(…).stored_value held across function calls that can mutate the same map.
  • Re-entrant resource invalidation — Audit paths where building/painting one resource can invalidate the containing resource’s own cache mid-operation.

Before / after

Loading diff…