← All issues

Clear-Site-Data "cache" failed to evict BFCache/MemoryCache via SecurityOriginHash ODR violation

20790ef

C++ ODR은 하나의 template이 모든 translation unit에서 정확히 하나의 정의만 가져야 한다는 규칙입니다. HashSet<Ref<SecurityOrigin>>에는 두 가지 DefaultHash specialization 후보가 존재합니다. 하나는 SecurityOriginHash.h에서 제공하는 내용 기반의 SecurityOriginHash이고, 다른 하나는 항상 사용 가능한 포인터 기반의 PtrHash입니다. SecurityOriginHash.h를 포함하지 않은 translation unit이 HashSet을 인스턴스화하면, 서로 다른 hashing semantics를 가진 add()/contains() inline 정의가 binary 안에 모두 포함되고, linker는 그중 하나를 조용히 선택합니다.

Source/WebCore/page/SecurityOrigin.h

+namespace WTF {
+// Ref<SecurityOrigin>에 대한 내용 기반 DefaultHash specialization은
+// 여기서 선언되지만 실제 정의는 SecurityOriginHash.h에만 존재합니다.
+// 따라서 SecurityOriginHash.h를 포함하지 않고 Ref<SecurityOrigin>을
+// hash-table key로 사용하면, 포인터 hashing으로 조용히 fallback되는
+// 대신 명시적인 compile error가 발생합니다.
+template<typename> struct DefaultHash;
+template<> struct DefaultHash<Ref<WebCore::SecurityOrigin>>; // SecurityOriginHash.h에서 정의
+} // namespace WTF

WebProcess.cpp는 포인터 기반 hashing으로 origin set을 구성했고, WebCore는 새로 생성한 SecurityOrigin 객체를 내용 기반 hashing으로 조회했습니다. 서로 다른 hashing 방식이 적용된 만큼 포인터 주소가 일치할 수는 없었고, contains()는 항상 false를 반환했습니다. 결과적으로 어떤 cache도 비워지지 않은 셈입니다. 이 ODR 위반이 실질적인 영향을 미치게 된 것은 commit 307882@main에서 컨테이너를 RefPtr에서 Ref로 변환하면서부터입니다. 이번 수정은 SecurityOrigin.h에서 DefaultHash<Ref<SecurityOrigin>>를 불완전한 타입으로 forward-declare하여, 향후 발생할 수 있는 조용한 miscompile을 명시적인 compile error로 전환합니다.

서버는 로그아웃 시 Clear-Site-Data: "cache"를 통해 캐시된 리소스를 삭제합니다. 그러나 이 기능이 조용히 실패하면서, 로그아웃 이후의 BFCache 및 memory-cache 항목이 그대로 접근 가능한 상태로 남게 되었습니다. commit 307882@main부터 이번 수정이 적용되기 전까지 처리된 모든 응답이 이 영향을 받았습니다.

Clear-Site-Data의 다른 directive("cookies", "storage")도 유사한 origin-set 구성 경로를 공유하는지, 그리고 동일한 문제가 잔존하는지 점검이 필요합니다. ODR 위반 패턴은 기계적으로 탐지할 수 있습니다. translation unit 전체에서 HashSet<Ref<를 검색하고, 별도 header에 내용 기반 DefaultHash가 정의된 각 타입 T에 대해 인스턴스화 지점에서 해당 header가 항상 포함되는지 확인하십시오. 이를 누락하면 다른 타입도 조용히 포인터 기반 hashing으로 fallback될 수 있습니다. 마지막으로 BackForwardCache::clearEntriesForOriginsMemoryCache::removeResourcesWithOrigins가 모든 호출 지점에서 내용 기반 hashing을 사용하는지 확인해야 합니다. 아울러 어떤 호출자도 PtrHash를 다시 선택할 수 있는 translation unit에서 origin set을 재구성하지 않는지도 점검이 필요합니다.