20790ef5e0 Clear-Site-Data: "cache" does not evict BFCache or MemoryCache entries due to SecurityOriginHash visibility
Triage note: A visibility bug made Clear-Site-Data cache directive ineffective, leaving origin-scoped cached data resident - a privacy/data-clearing failure.
Contents
The bug at a glance
This is a data-clearing / privacy-hygiene failure, not a memory-safety bug: a Clear-Site-Data: “cache” HTTP response silently failed to evict the calling origin’s back/forward-cache and memory-cache entries. Origin-scoped content that a site (or a user) explicitly asked to be purged remained resident and reusable, which is a confidentiality/state-persistence weakness of medium severity. There is no attacker-controlled corruption primitive, so it does not rise to high.
The interesting part is that this was an ODR (One Definition Rule) violation, not a logic bug: HashSet<Ref<SecurityOrigin>> resolved to two different DefaultHash definitions depending on whether SecurityOriginHash.h happened to be included at the instantiation point. The fix does not change any runtime logic at all; it converts the silent miscompile into a hard compile error by forward-declaring an incomplete specialization.
Root cause
OBSERVED: The commit message states that BackForwardCache::clearEntriesForOrigins and MemoryCache::removeResourcesWithOrigins “silently failed to evict matching entries” after a Clear-Site-Data: “cache” response. The container involved is a HashSet<Ref<SecurityOrigin>>. The content-based hash for SecurityOrigin is the DefaultHash<Ref<SecurityOrigin>> specialization, whose definition lives in SecurityOriginHash.h.
OBSERVED: When SecurityOriginHash.h was not visible at the point a HashSet<Ref<SecurityOrigin>> was instantiated, DefaultHash<Ref<SecurityOrigin>> fell back to the pointer-based PtrHash primary template in HashFunctions.h. WebProcess.cpp built the set with pointer hashing, while WebCore queried it with the content-based SecurityOriginHash. Both spellings name the identically-typed HashSet<Ref<SecurityOrigin>, DefaultHash<Ref<SecurityOrigin>>>, but with two incompatible definitions of DefaultHash – an ODR violation. The linker keeps one inline add()/contains() arbitrarily, so a freshly-created SecurityOrigin::create(…) queried against a set populated under a different hash never matched.
OBSERVED: The message identifies 307882@main as the point the mismatch became active, because that change converted these containers from RefPtr<SecurityOrigin> to Ref<SecurityOrigin> and thereby changed which DefaultHash definition won the merge. INFERRED: before that conversion, RefPtr<SecurityOrigin> hashing happened to resolve consistently, masking the latent header-visibility hazard.
OBSERVED: contains(SecurityOrigin::create(…)) returning false for an origin that IS in the set means neither the BFCache nor the MemoryCache entry for that origin was cleared, so the cache directive was a no-op for the calling site.
Key code
The compile-time guard added to SecurityOrigin.h (verbatim)
namespace WTF {
// The content-based DefaultHash specialization for Ref<SecurityOrigin> is
// declared here but intentionally defined only in SecurityOriginHash.h. Using
// Ref<SecurityOrigin> as a hash-table key without including SecurityOriginHash.h
// is therefore a hard compile error (instantiating an incomplete DefaultHash)
// rather than a silent fall back to pointer hashing.
template<typename> struct DefaultHash;
template<> struct DefaultHash<Ref<WebCore::SecurityOrigin>>; // Defined in SecurityOriginHash.h
} // namespace WTF
Patch walkthrough
Source/WebCore/page/SecurityOrigin.h— Adds #include <wtf/Ref.h> and, in namespace WTF, forward-declares the generic template<typename> struct DefaultHash and an explicit but undefined specialization template<> struct DefaultHash<Ref<WebCore::SecurityOrigin>>;. Because a full specialization out-ranks the generic PtrHash partial template, Ref<SecurityOrigin> can no longer silently resolve to pointer hashing. The specialization is an incomplete type here (defined only in SecurityOriginHash.h), so using Ref<SecurityOrigin> as a hash-table key without that header is now a hard compile error instead of a silent fall back.Source/WebCore/platform/graphics/cocoa/MediaPlayerPrivateWebM.mm— Adds #import “SecurityOriginHash.h”, which the new incomplete-type guard now requires at the point this file uses a HashSet<Ref<SecurityOrigin>> (its m_origins member). This is the one call site the new compile-time guard flushed out.
Background
Clear-Site-Data: “cache” — An HTTP response header directive instructing the browser to evict cached data associated with the responding origin. Correct handling requires purging entries scoped to that origin from all relevant caches, including WebCore’s in-memory resource cache and the back/forward navigation cache. If the eviction silently no-ops, stale resources and page state survive a directive whose entire purpose is to remove them.
DefaultHash and PtrHash — WTF’s HashSet/HashMap pick a hash functor via DefaultHash<T>. For pointer-like types the primary template resolves to PtrHash, which hashes the pointer bits (object identity). Content-addressed types provide an explicit DefaultHash specialization that hashes the logical value. SecurityOrigin’s content-based specialization lives in SecurityOriginHash.h, so the meaning of a SecurityOrigin hash set depends on whether that header is in scope at instantiation.
One Definition Rule (ODR) violation — When the same instantiated type HashSet<Ref<SecurityOrigin>> is compiled with two different DefaultHash definitions in different translation units, the program has two conflicting definitions of the same inline entity. The linker is free to keep either one, and the result is an ill-defined program: population and lookup can end up using different hashing/equality, so contains() can return false for a member that is present.
Ref<SecurityOrigin> vs RefPtr<SecurityOrigin> conversion (307882@main) — The commit attributes activation of the bug to an earlier refactor that switched these containers from RefPtr to Ref. INFERRED: the change altered template argument deduction/instantiation in a way that changed which DefaultHash definition the linker merged, flipping a previously-benign latent mismatch into an active miscompile that broke cache eviction.
DefaultHash<RefPtr<StringImpl>> idiom — The fix explicitly mirrors an existing WTF pattern where the specialization is declared in StringImpl.h but defined in StringHash.h. Declaring an incomplete specialization in the type’s own header makes any hash-table use without the hashing header a compile error, which is the intended safety net this patch adds for SecurityOrigin.
Vulnerability window
- Latent hazard — The content-based DefaultHash<Ref<SecurityOrigin>> is defined only in SecurityOriginHash.h; any TU instantiating a SecurityOrigin hash set without that header silently uses PtrHash.
- Activation — 307882@main converts the BFCache/MemoryCache origin containers from RefPtr<SecurityOrigin> to Ref<SecurityOrigin>, changing which DefaultHash definition wins the ODR merge and making the population/lookup mismatch active.
- Symptom — A Clear-Site-Data: “cache” response causes clearEntriesForOrigins / removeResourcesWithOrigins to query the set with a freshly created SecurityOrigin; contains() returns false, so no entries are evicted.
- Diagnosis — Root cause identified as header-visibility-dependent hashing (an ODR violation), not the eviction logic itself (bug 317269 / rdar://179387089).
- Fix — Forward-declare the incomplete DefaultHash<Ref<SecurityOrigin>> specialization in SecurityOrigin.h so pointer-hash fallback is impossible and missing SecurityOriginHash.h is a compile error; add the missing include to MediaPlayerPrivateWebM.mm.
Triggering
No new test was added; the commit says it is covered by the existing test http/tests/clear-site-data/bfcache.html. Trigger: load a page in a site that has BFCache/MemoryCache entries, then have that origin return an HTTP response carrying Clear-Site-Data: “cache”; on the buggy build the back/forward-cached page and cached subresources for that origin remain present and reusable instead of being evicted.
Exploitation
- Preconditions — Attacker or site must be able to serve a Clear-Site-Data: “cache” response for its own origin and rely on eviction actually happening (e.g. a logout / data-purge flow).
- Effect — Eviction silently no-ops: origin-scoped page state and resources persist in BFCache/MemoryCache. This is an information-persistence / privacy weakness (stale sensitive content recoverable via back/forward navigation), not a code-execution primitive.
- Honest limits — There is no memory-corruption or attacker-controlled-pointer angle. Impact is confined to defeating an explicit data-clearing directive; severity is bounded by what sensitive data the site had cached.
Detection & hunting
For defenders and SOC / detection engineers:
- Cache eviction efficacy —
- ODR / hashing audits —
Audit directions
- Other content-hashed key types —
- Cross-process origin sets —
- RefPtr->Ref refactors —