CVE-2024-54502
Overview
Background
- DocumentFontLoader
- Per-Document helper that drives pending @font-face loads.
- Re-entrancy via resource loads
- Font/resource loading can fire events (e.g. iframe onload) that run author script mid-operation.
- Ref protectedThis
- Taking a strong reference to self at the top of a method keeps the object alive across callbacks that might otherwise free it.
- makeUniqueWithoutRefCountedCheck
- Creates a unique_ptr to a type that is (unusually) ref-counted, bypassing the standard assertion.
Root Cause Analysis
Document owns a DocumentFontLoader as a plain unique_ptr member (m_fontLoader) with no independent reference counting. DocumentFontLoader::loadPendingFonts() iterates and starts pending font loads, and font loading can synchronously run author script — for example an <iframe onload> handler that removes itself, which can tear down its Document and, transitively, free the DocumentFontLoader while loadPendingFonts() is still executing on it. Operating on the freed loader (or the freed Document it points at) after the callback is a use-after-free.
The fix gives DocumentFontLoader lifetime tied to its Document: it adds ref()/deref() that forward to m_document, is created with makeUniqueWithoutRefCountedCheck, and loadPendingFonts() takes Ref protectedThis { *this } at entry so the loader (and its Document) are kept alive for the duration of the potentially-reentrant load, even if script destroys the document mid-iteration.
The restored invariant is that the font loader cannot be freed while it is running font loads that may re-enter through script. The layout test loads a cached font and removes the loading iframes on their onload, reproducing the destroy-during-load re-entrancy under ASan.
this fixes the UAF.Attack Path
- Cache a font and load it Declare an @font-face used by the page so font loading is triggered.
- Run script during load Use subframes (or other resources) whose onload handlers run while loadPendingFonts() is iterating, e.g. iframes that remove themselves on load.
- Destroy the document mid-load The re-entrant script tears down a Document, freeing its DocumentFontLoader while loadPendingFonts() is still on the stack.
- Use-after-free loadPendingFonts() continues to use the freed loader/document, corrupting memory in the WebContent process.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
Document::ensureFontLoaderSource/WebCore/dom/Document.cpp |
modified | Creates the loader via makeUniqueWithoutRefCountedCheck now that DocumentFontLoader is ref-counted through its Document. |
DocumentFontLoader::loadPendingFontsSource/WebCore/dom/DocumentFontLoader.cpp |
modified | Takes Ref protectedThis { *this } so the loader (and Document) stay alive across font-load callbacks that can run script and destroy the document. |
DocumentFontLoader::ref/derefSource/WebCore/dom/DocumentFontLoader.h |
modified | Forwards ref()/deref() to m_document so the loader shares the Document's lifetime and can be protected by a Ref. |
Audit Directions
- Other unprotected per-Document helpersgrep WebCore/dom for unique_ptr members whose methods run resource loads/callbacks without a Ref protectedThis guard.
- loadPending*/callback loopsAudit loaders that iterate pending work and invoke handlers able to run script or destroy the owner.
- self-destroying iframe re-entrancyReview paths reachable during subframe onload that can synchronously destroy the parent document.