← Chrome — 2026-W41

a8a28009b6c759960a97216f3a068e9cbb20d035  [M150] Reinsert PatternData into pattern map after reconstruction

severity medium class UAF confidence 0.75 signal score 6.8 third_party/blink/renderer/core/ patch-now AI review CONFIRMED
Primitive: dangling unique_ptr<PatternData>& into pattern_map_ slot invalidated when the map is cleared during BuildPatternData -> UAF write
Triage note: Identical to 60126f0149 (M150 cherry-pick, Fixed: 565742179): find()+pointer and re-insert via Set() instead of writing through a reference the map clear could invalidate.
Structural signals: security_assert (+3.0), area:third_party/blink/renderer/core/ (+2.5), surgical (+1.3)

The bug at a glance

Identical bug and fix to 60126f0149: a use-after-free write in LayoutSVGResourcePattern where a reference into pattern_map_ is held across BuildPatternData, which can clear the map. SVG pattern painting is web-reachable with attacker markup and a UAF write is a strong renderer-process primitive, so it rates high. This is the M150-branch security cherry-pick of the same main-line fix.

Same mechanism as 60126f0149, delivered to a different release line. LayoutSVGResourcePattern held std::unique_ptr& into a pattern_map_ slot and kept using it across BuildPatternData, which can clear pattern_map_ and free that slot — leading to a write through a dangling reference. This object is the M150 stable-branch cherry-pick: refs/branch-heads/7871@{#4420}, Bug: 567784342 (merge-tracking) of Fixed: 565742179, from the same original main commit dedbd3f (main@{#1706535}).

Root cause

The pre-fix code bound std::unique_ptr& pattern_data to pattern_map_.insert(&client, nullptr).stored_value->value, a reference into a WTF::HashMap value slot. BuildPatternData(reference_box, paint_flags) can recursively resolve SVG resources and clear/rehash pattern_map_, freeing the backing the reference points at; the subsequent pattern_data = BuildPatternData(…) and pattern_data->paint_flags = paint_flags then write through a dangling reference (UAF write). The fix replaces the held reference with pattern_map_.find(&client) into a raw PatternData*, builds into a local std::unique_ptr new_pattern_data, captures the raw pointer, asserts CHECK(pattern_data), and re-inserts via pattern_map_.Set(&client, std::move(new_pattern_data)) AFTER the build — so the map bucket is re-resolved post-mutation and no stale reference is dereferenced. The source change is byte-for-byte identical to the M153 instance; only the branch (M150 / branch-heads/7871) and merge bug differ.

Key code

M150 cherry-pick: find()+Set() replaces the held pattern_map_ slot reference (identical to the M153 fix)

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 — The sole change, identical source to the M153 cherry-pick (60126f0149) but applied on the M150 branch (file pre-image hash differs: 85e5ccb1 here vs ec602b7 on M153, reflecting branch drift). Before: std::unique_ptr& pattern_data = pattern_map_.insert(&client, nullptr).stored_value->value held a reference into the map slot and wrote through it (reset()/assignment) after BuildPatternData, which can clear pattern_map_ — a UAF. After: find() into a raw PatternData*, null it on paint_flags mismatch, and on rebuild call BuildPatternData into a local unique_ptr, capture the raw pointer, CHECK(pattern_data), then pattern_map_.Set(&client, std::move(new_pattern_data)) so the write to the map happens after the build with a freshly-resolved bucket. No map reference is held across the build.

Background

LayoutSVGResourcePattern::pattern_map_ — Per-resource WTF::HashMap<client, std::unique_ptr> caching resolved SVG paint data per client.

BuildPatternData re-entrancy — Building a pattern’s tile can recursively resolve other SVG resources and trigger invalidation that clears pattern_map_ during the call, invalidating any reference previously taken into that map.

find()+Set() vs insert().stored_value — insert().stored_value->value yields a reference into the hash-map backing that is unsafe to hold across a mutating call; find() + a post-build Set() re-resolves the bucket and avoids dereferencing a freed slot.

M150 branch — This is the M150 stable cherry-pick (refs/branch-heads/7871@{#4420}, Cr-Branched-From main@{#1639810}, merge bug 567784342) of the same original fix (Fixed: 565742179, dedbd3f, main@{#1706535}) that 60126f0149 delivered to M153 (branch-heads/8010).

Vulnerability window

  1. Introduction — Same latent defect as 60126f0149: a reference into pattern_map_ held across BuildPatternData, present on the M150 line.
  2. Report — Original Chromium issue 565742179 (Fixed); M150 merge tracked under bug 567784342.
  3. Main fix — Original fix dedbd3f9 by Stefan Zager, reviewed by Fredrik Söderquist, Cr-Commit-Position refs/heads/main@{#1706535}.
  4. M150 cherry-pick — Merged to M150 2026-10-05 via CL 8496896, Cr-Commit-Position refs/branch-heads/7871@{#4420} (Cr-Branched-From main@{#1639810}).

Proof of concept

No test or PoC is in the diff (poc.available=false). Conceptually identical to 60126f0149: attacker SVG must make painting a re-enter resource resolution and clear pattern_map_ during BuildPatternData, so the pre-fix held reference dangles and the paint_flags write hits freed heap. Web-facing (rendering SVG suffices, no permissions). The exact invalidation chain is inferred from the commit message, not shown in the diff.

Exploitation

  1. Reach the sink (inferred) — Render SVG filling an element with url(#pattern) so LayoutSVGResourcePattern takes the pre-fix reference into pattern_map_ on the M150 build.
  2. Clear the map mid-build (inferred) — Pattern content triggers SVG resource invalidation that clears/rehashes pattern_map_ during BuildPatternData, freeing the referenced slot.
  3. UAF write (inferred) — Assignment through the dangling std::unique_ptr& and pattern_data->paint_flags = paint_flags write into freed heap — a UAF write in the renderer.
  4. Escalation (inferred) — Reclaiming and controlling the freed PatternData to build a stronger primitive is not demonstrated by the patch and is inferred.

Detection & hunting

For defenders and detection engineers:

  • ASAN/UAF crashes in LayoutSVGResourcePattern —
  • CHECK(pattern_data) abort —
  • Re-entrant pattern_map_ mutation during BuildPatternData —

Audit directions

  • Branch-coverage of the same fix —
  • References/iterators held across re-entrant SVG builds —
  • insert().stored_value->value usage —

Before / after

Loading diff…