[4] DocumentThreadableLoader null-WeakPtr dereference on m_document
Severity: Medium | Component: WebCore loader — DocumentThreadableLoader / CrossOriginPreflightChecker | 8384754
Rated Medium because the diff fixes a null WeakPtr dereference on m_document reachable from CORS preflight / redirect / failure callbacks after the originating document has been detached. The RELEASE_ASSERT in WeakPtr::operator* produces a deterministic, web-content-triggered renderer abort; no read/write primitive is exposed because the assert fires before any pointer arithmetic on the null storage occurs.
This patch adds liveness checks before dereferencing m_document in DocumentThreadableLoader and CrossOriginPreflightChecker. Previously the WeakPtr was dereferenced through document() / protectedDocument(). Because m_document can be null, the existing pattern relied on WeakPtr::operator*'s RELEASE_ASSERT, which aborts the process on null. The fix converts each access site to a local RefPtr (extending the document's lifetime across the call window) preceded by an explicit null check, and changes document() to return Document* so the null case is visible in the type system. The header file is also removed from UncheckedCallArgsCheckerExpectations, indicating it now passes WebKit's safer-CPP unchecked-arg static check.
Source/WebCore/loader/DocumentThreadableLoader.h
- Document& document() { return *m_document; }
+ Document* document() { return m_document; }
Source/WebCore/loader/DocumentThreadableLoader.cpp
void DocumentThreadableLoader::makeCrossOriginAccessRequest(ResourceRequest&& request) {
...
- Ref document = *m_document;
+ RefPtr document = m_document;
+ if (!document)
+ return;
...
void DocumentThreadableLoader::preflightFailure(...) {
- RefPtr frame = m_document->frame();
+ RefPtr document = m_document;
+ if (!document)
+ return;
+ RefPtr frame = document->frame();
Source/WebCore/loader/CrossOriginPreflightChecker.cpp
void CrossOriginPreflightChecker::validatePreflightResponse(...) {
- RefPtr frame = loader.document().frame();
+ RefPtr loaderDocument = loader.document();
+ if (!loaderDocument) { ASSERT_NOT_REACHED(); return; }
+ RefPtr frame = loaderDocument->frame();
Patch Details
The fix touches every site in DocumentThreadableLoader.cpp and CrossOriginPreflightChecker.cpp that previously dereferenced m_document: shouldSetHTTPHeadersToKeep, makeCrossOriginAccessRequest, cancel, didReceiveResponse, didFail, preflightFailure, loadRequest, securityOrigin, contentSecurityPolicy, crossOriginEmbedderPolicy, logErrorAndFail, plus validatePreflightResponse, notifyFinished, startPreflight, and doPreflight in the preflight checker. The accessor signature change (Document& document() → Document* document()) propagates the null case into the type system at every call site. No business logic is restructured; the fix is uniformly "capture into RefPtr, null-check, only then dereference".
Stale WeakPtr dereference whose RELEASE_ASSERT-on-null was reachable from web-content-driven loader callbacks.
Background
WeakPtr<T> in WebKit is a non-owning smart pointer that goes null when the referent is destroyed; calling operator* or operator-> on a null WeakPtr triggers a RELEASE_ASSERT (always-on, even in release builds), which aborts the process. RefPtr<T> is a reference-counted owning smart pointer — assigning a WeakPtr into a RefPtr either captures a strong reference (extending lifetime for the scope) or evaluates to null if the referent has already been destroyed.
DocumentThreadableLoader is the WebCore class that performs asynchronous and synchronous loads on behalf of a Document — it is the backend for fetch(), XMLHttpRequest, EventSource, and similar APIs, and it drives CORS preflight via CrossOriginPreflightChecker. The loader is RefCounted and can outlive its originating Document: when a frame is detached or a document is replaced, the document is destroyed but in-flight loaders (still owned by the network/CORS state machine) keep running and eventually deliver completion or error callbacks. The loader stores its document as WeakPtr<Document, WeakPtrImplWithEventTargetData> m_document, precisely so it does not extend the document's lifetime.
Analysis
Pre-fix, DocumentThreadableLoader::document() returned a Document& produced by dereferencing m_document with operator*. Because the loader can outlive its document in several scenarios — asynchronous CORS preflight in flight, redirect callbacks, error reporting after frame detach, service-worker-mediated paths — many code paths in this file dereferenced m_document without first checking liveness. Any of those paths could trip the RELEASE_ASSERT in WeakPtr::operator* and crash the WebContent process.
This is a recurring class in WebKit: components that legitimately outlive their owning Document cache it as a WeakPtr to avoid lifetime extension, but then dereference it with operator* / operator-> instead of converting to RefPtr and null-checking. The RELEASE_ASSERT in WeakPtr::operator* converts these latent UAF-shaped bugs into deterministic process aborts — that is a defense-in-depth win against memory corruption, but it leaves a large surface of web-content-reachable crashes.
Aaaa Aaa Aaaaaaa Aaa Aaaa Aa Aaaaaaa Aaaa a Aaaaaaaaaaaa Aaaaaaaaa Aaaa Aaaaaaaa Aaaa Aaaaaaaaa Aaaaaa a Aaaaaaa Aaaa Aaaaaa Aaaaaaa Aa a Aaaaaaaaaa Aaaaaaa Aaaaaa Aa Aaaaaa Aa Aaaaaa Aaa Aaaaaaaa Aaaaaaaaa Aaaa Aaaaaaaaaaaaa Aaaaaa Aaa Aaaaaaaa a Aaaaaa Aaa Aaaaaaa Aaaaaaaa Aaa Aaaaaa Aa Aaaaa Aaa Aaaaaa a Aaaaa Aaa Aaaaaaaaa Aa Aa Aaaaaaa Aaaa Aaaaaaaaa Aaaaaaaaaaa Aaaaaaaaa Aa Aaaaaaa Aaaaaaaaa Aaaaaaa Aaa Aaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaa Aaa Aaaaaaaa Aaaaaaaaaaaa Aaaaaaaaaa Aaaaaaa Aaaa Aaaaa Aaaa Aa Aaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaa Aaaaaaaaa Aaaaa Aaaaa Aaaaaa
Aaa Aaaaaaaaaaaa Aaa Aaaa Aaaaaaaaa Aaaaaaaaaaaa Aa Aaaaaa Aaaaaaaaaaaa Aa Aaa Aaaa Aaaaaaa Aaaaaaaaaa Aaaaaaa Aa Aaaaaa Aaa Aaaa Aaaa Aaaa Aaa Aaaa Aaaaaa Aaa Aaaaaa Aaaaa Aaaaaa Aa Aaaaaa Aaa Aaa Aaaa Aaaaaaa Aaaaa Aaaa Aa Aaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaa Aaaaaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaaa Aaa a Aaaaaa Aaaaaaa Aaaaaaaa Aaaaaaaa Aaaa Aaaaaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaa Aaa Aaaa Aaaaaaa Aaaaaaaaa Aaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaa a Aaaa Aaaaaaaaaaa Aaaaaaaa Aaaa Aaaaaaaaa Aaa Aaaaaaaa Aa a Aaaaaaaaaaaaaaaa Aaaaaa Aaaa a Aaaaaaaa Aaaa Aaaaaa Aa Aaa Aaaa Aaaa Aaaaa Aaa Aaaaaaaa Aaaaaaaa Aaaaaaa Aaaaaaa Aaaaaaaaaa Aaa Aaaaaaaa Aaaaa Aaaaaaaaa Aaa Aaaaaaaaa Aa Aaaaaaaa Aaaaaaaaaaa a Aaaa Aaaa Aaaaaa Aaaaaaaaaaaa Aaaaaaa Aaa Aaaaa Aaaa Aaa Aaaaaaaaaaa Aaaaaaaa Aaaaaa Aaa Aaaaaaaa Aaaaaaaaa Aaaaaaaa Aaaaa Aaaaaaa a Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaaa Aaaaa Aa Aaa Aaaaaaaaaa Aaaaaaa a a Aaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaaaa Aaa Aaaaaaaaa Aaa a Aaaaaaaaaaaaa Aaaaaaaaaa
🔒How does a cross-origin fetch plus a well-timed frame teardown turn into a deterministic renderer crash, and what is — and isn't — possible beyond the crash itself?
Subscribe to read more
Audit directions
a Aaaaaaaaaaaa Aaaaaaaaaaaaaaa Aaaaaaa Aaaa Aaaaa Aaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaa Aa Aa Aaaaa Aaaaaaaaa Aaaaaaaaa Aaaa Aaa Aaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaaaaaaa Aaaaa Aaa Aaaaaaaaaaa Aaaaaa Aa a Aaaaaaaaaa Aaa Aaaaaa a Aaaaaaaa a Aaaaaaaaaa Aaaaaaaa Aaa Aaaaaaaaaaaa Aaaaa Aaaa Aaaa Aaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaa Aaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaa
a Aaaaaaaaaa Aaaaaaa Aaaa Aaaaaa a Aaaaaaaaa Aaaaaa Aa Aaaaaaaaaaaaa Aa Aaaaaaaa Aaaaaaaaaaaa Aaa Aaaaaaaaa Aaaa Aaaaa Aaa Aaaa Aaaa Aaaa Aaaaaaa Aaa Aaaaaaaaaa Aaaaaaaaaaaaaaaa Aaaa Aaaaaaa Aaaaa Aaaa Aaaaa Aaaaa Aaaaa Aaaaaaa Aaa Aaaaaaaaaaaa Aa Aaa Aaaa Aaa Aaaaa a Aaaaaa Aaaaaaaaaaaaaa Aa Aaa Aaaaaaa Aaaa Aa Aaa Aaaaa a Aaaaaa Aaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaa
a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaaaaaaaaaaaaaa Aaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaa Aa a Aaaaaaaaa Aaaaa Aaaaaaa Aaaa Aaaaaaaaa Aaaaa a Aaaaaaaaaaaa Aaaaa Aaaaaaaaaa Aaaaaaa Aaa Aaaaaaa a Aaa Aaaaaaaaa Aaa Aaaa Aaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaaaaaa Aaaaaaaaa Aaaaa Aaaa Aaaa Aaaaaaa Aaa Aaaa Aaaaa Aa Aaaaa Aaaa
a Aaaaaaaaaaaaaaaa Aaa Aaaaaaaa Aaaaaaaa Aaaaa Aaaaaaaa Aaaaaaaa Aaaaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaa Aaa Aaaaaa Aaaa Aaaaaaaa Aaaaaaaaaaa Aaa Aaaaaaaaaaa Aaaaaaa Aaaaaa Aaaaaaaa Aaa Aaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaa
a Aaaaaaaaa Aaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaa Aaaaaaaaa Aaa Aaaaaaaa Aaaaaaaaa Aa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaa Aaaaaaaaaaa Aaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaaaa Aaa Aaaaaaaa Aaaa Aaa Aaaaaaa Aaaaaaaaaa Aaaaaaa Aaa Aaaaaaaa Aaa Aaaaa Aaaaaa Aaaa Aaaaaaa Aa Aaa Aaaaa Aaa Aaa Aaaaaaaaa a Aaaaaaaaa Aa Aaaaaaaa Aaaa Aaaa Aaaaa Aaaaaa Aaaaa Aaaa Aaaa a Aa Aaaaaaaa Aaaaaaaaa Aa Aaa Aaaaaa
🔒Several reusable audit patterns identified across WebKit's loader and DOM lifecycle code, with concrete starting points for variant discovery.
Subscribe to read more