← All issues

[4] Document.open() UAF via MutationObserver on Cross-Document Element

Severity: High | Component: WebCore DOM | 6767475

Rated High because the observable effect is an ASAN segfault in firstDOMWindow during document.write — a crash consistent with use-after-free or null dereference on the document's DOMWindow association — and the trigger is fully reachable from web content via MutationObserver. The actual fix was in a separate commit (300757@main, later reverted in 300886@main); this commit adds only the regression test, limiting diff-backed analysis of the fix mechanism.

This commit adds only a regression test — no production code change is included. The test creates an element from a DOMParser-parsed document, attaches a MutationObserver that calls document.open() on the main document, then triggers the observer by setting an attribute. The actual fix was landed in commit 300757@main and later reverted in 300886@main.

LayoutTests/fast/dom/Document/open-triggered-by-mutation-observer-on-element-from-a-parsed-doc-crash.html

+ const element = (new DOMParser()).parseFromString("<!DOCTYPE html><p>foo</p>","text/html").documentElement;
+ (new MutationObserver(_ => document.open())).observe(element, {attributes: true});
+ element.setAttribute("class", "bar");
+ document.body.innerHTML = "PASS if no crash.";

Re-entrant document.open() via MutationObserver on a cross-document element invalidates the calling document's DOMWindow association mid-execution.

MutationObserver is a DOM API that delivers notifications when observed DOM nodes are mutated. Observers fire callbacks in JavaScript during microtask checkpoints. document.open() clears the current document and prepares it for writing — this operation can tear down and reinitialize document state including its association with the browsing context's DOMWindow. DOMParser creates a new Document from markup without an associated browsing context — elements from such documents have no live frame or window, but they can still be observed by MutationObserver instances belonging to any document.

The test case reveals the trigger: calling document.open() from within a MutationObserver callback that fires on an element belonging to a different document (one created by DOMParser). The crash occurs in WebCore::firstDOMWindow called from jsDocumentPrototypeFunction_write, indicating that during document.open() in this context, the document's association with its browsing context or DOMWindow becomes invalid. The element being observed belongs to a parser-created document with no browsing context, and the mutation observer callback executes document.open() on the main document during attribute mutation delivery, likely invalidating state that the call stack still references.

🔒

Explores the document lifecycle and memory model implications of this crash, and whether it could escalate beyond denial-of-service

Subscribe to read more

🔒

Multiple re-entrancy audit patterns identified across document lifecycle operations, with concrete starting points for variant discovery

Subscribe to read more