← Chrome — 2026-W31

85c3f8f61abfd66fc1491e5a96aaf8c25f4f4fc4  [M152] Sanitize presentation attribute cache hash using EnsureValidHash

severity low class UAF CVSS 8.1 signal score 3.8 third_party/blink/renderer/core/ patch-now AI review CONFIRMED
Structural signals: area:third_party/blink/renderer/core/ (+2.5), surgical (+1.3)

The bug at a glance

The commit message and diff describe a genuine use-after-free: an unsanitized hash equal to the HashTraits DeletedValue sentinel makes Oilpan skip tracing the bucket, so a live CSSPropertyValueSet is swept while the HeapHashMap keeps a reference, and a later lookup returns a freed pointer; a renderer heap UAF reachable from DOM content is high severity.

An attacker-controlled page can shape element tag names and presentation-attribute sets so the cache key hash collides with the deleted-bucket sentinel, inducing premature GC of a cached style object that is then dereferenced on the next lookup.

Root cause

ComputePresentationAttributeCacheHash returned HashInts(tag_hash, attr_hash) directly, without EnsureValidHash, so the result could be one of the HashTraits sentinels (0 or 0xFFFFFFFF). A hash equal to 0xFFFFFFFF (DeletedValue) causes the Oilpan HeapHashMap to place the Member in a bucket treated as deleted; GC then skips tracing that bucket, sweeps the entry, yet the map still holds the stale Member. A subsequent cache lookup for the same key returns the freed pointer (use-after-free). The fix wraps the output in EnsureValidHash to remap sentinel values to a safe hash, matching ElementDataCache and MatchedPropertiesCache.

Key code

Sanitize cache hash so it can never equal the deleted/empty HashTraits sentinels

-  return HashInts(key.tag_name->ExistingHash(), attribute_hash);
+  return EnsureValidHash(
+      HashInts(key.tag_name->ExistingHash(), attribute_hash));

Patch walkthrough

  • third_party/blink/renderer/core/dom/presentation_attribute_style.cc — Adds hash_traits.h and wraps the computed presentation-attribute cache hash in EnsureValidHash so the value can never collide with HashTraits sentinels (0 / 0xFFFFFFFF); this prevents an Oilpan HeapHashMap bucket from being misclassified as deleted and its live CSSPropertyValueSet from being swept, closing a use-after-free.

Background

Presentation attribute cache — A HeapHashMap caching computed CSSPropertyValueSet style for elements keyed by tag name plus presentation attributes/values.

EnsureValidHash — WTF helper that remaps hash values colliding with HashTraits empty/deleted sentinels (0 and 0xFFFFFFFF) to a valid hash.

Oilpan HeapHashMap deleted bucket — A bucket whose hash equals DeletedValue is treated as a tombstone and is not traced during garbage collection, so its referent can be swept.

Vulnerability window

  1. Reachability — Page content with elements bearing presentation attributes populates the presentation-attribute style cache during style resolution.
  2. Trigger — A tag-name/attribute combination hashes to 0xFFFFFFFF, so the cached Member lands in a bucket treated as deleted (inferred, probabilistic).
  3. Primitive — Garbage collection skips the deleted bucket and sweeps the CSSPropertyValueSet while the map still references it, producing a dangling pointer.
  4. Escalation — A later cache lookup for the same key returns and dereferences the freed object, giving a UAF that controlled reallocation could turn into corruption (inferred).

Proof of concept

Conceptually: generate many elements whose tag name and presentation-attribute set vary so that one combination’s HashInts result equals 0xFFFFFFFF, cache its style, force a garbage-collection cycle, then query the same element/key again to hit the freed CSSPropertyValueSet. Finding a colliding key is a ~1-in-2^32 search over attacker-chosen inputs. No exploit code.

Exploitation

  1. Key search — Enumerate tag/attribute combinations until one hashes to the DeletedValue sentinel (inferred).
  2. Populate — Resolve style for that element so the freed-prone Member is stored in the deleted-looking bucket (inferred).
  3. Free — Trigger Oilpan GC so the untraced entry is swept while the map keeps the reference (inferred).
  4. Reuse — Groom the heap and re-query the key to dereference the dangling CSSPropertyValueSet (inferred).

Detection & hunting

For defenders and detection engineers:

  • grep for HashInts(…) returned directly from a Compute*Hash / cache-key hash function without EnsureValidHash — Flag any Oilpan HeapHashMap keyed by a raw composed hash that is not passed through EnsureValidHash.
  • ASAN/Oilpan heap-use-after-free on CSSPropertyValueSet in presentation attribute style lookups — Fuzz varied tag/attribute content with forced GC and watch for UAF in presentation_attribute_style.cc.

Audit directions

  • All Blink Oilpan caches keyed by a custom hash (HeapHashMap/HeapHashSet) — Verify every cache-key hash function wraps its result in EnsureValidHash like ElementDataCache and MatchedPropertiesCache.
  • third_party/blink/renderer/core style/dom caches — Audit other Compute*CacheHash helpers in core/dom and core/css for missing sentinel sanitization.

Before / after

Loading diff…