Moderate firefox UAF 🔧 Commit mapped

Overview

Moderate
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionUse-after-free in the DOM: Core & HTML component
ComponentDOM
Bug ClassUAF
Tracker2075985
Fix commitb3090fe73916 (firefox) +1/-1
CISA KEVNot listed
CreditedMozilla
Disclosed2026-10-27

Background

`HTMLFormElement::SubmitSubmission`
The method in dom/html/HTMLFormElement.cpp that carries out a form submission after the submission object has been built, locating the docshell/link handler that will actually perform the navigation.
`GetComposedDoc()`
Returns a raw Document* for the element’s composed document (null if the element is not in a composed document). Ownership stays with the DOM; the returned pointer must not be held across operations that can drop the document.
`RefPtr<Document>`
A strong reference to a Document; holding it prevents the document from being destroyed while the pointer is in scope.
`nsDocShell` / link handler
The submission needs the document’s docshell (doc->GetDocShell()) to find a container able to perform the navigation; IsEditable() and other checks follow.
Re-entrancy during submission
Form submission can fire events / run script (e.g. formdata/submit handlers already dispatched, mutation, navigation side effects) that can tear down the document mid-method.

Root Cause Analysis

The one-line fix changes Document* doc = GetComposedDoc(); to RefPtr<Document> doc = GetComposedDoc(); in HTMLFormElement::SubmitSubmission. Pre-fix, the composed document was captured as a raw Document* and then used to obtain the docshell: RefPtr<nsDocShell> container = doc ? nsDocShell::Cast(doc->GetDocShell()) : nullptr;. Between capturing the raw doc and dereferencing it, form-submission machinery can run script or otherwise re-enter in a way that releases the last strong reference to the document, freeing it. The subsequent doc->GetDocShell() would then dereference freed memory — a use-after-free of the Document. By holding the document in a RefPtr<Document>, the patch guarantees the document outlives its use within the method, closing the window where it could be freed out from under the raw pointer. The change is a textbook Gecko lifetime fix: the invariant “a DOM object used across potentially re-entrant/script-running work must be held by a strong reference” was violated by using a bare Document*. The advisory’s “use-after-free in DOM: Core & HTML” matches the diff exactly.
Key insight
SubmitSubmission held the composed document in a raw Document* while form-submission work could run script and drop the document’s last reference; dereferencing that pointer to fetch the docshell was a use-after-free, fixed by taking a RefPtr<Document>.

Attack Path

  1. Script-triggered submission Page script calls form.submit() / requestSubmit() or otherwise drives a submission that reaches HTMLFormElement::SubmitSubmission (web-reachable via ordinary form APIs).
  2. Arrange document teardown Register a handler or arrange mutation reachable during the submission flow that detaches/destroys the form’s composed document (e.g. removing the containing browsing context/document mid-submit).
  3. Capture raw `doc` Pre-fix, SubmitSubmission stores the composed document as Document* doc.
  4. Free the document The re-entrant work releases the last reference to that document, freeing it.
  5. Use freed document doc->GetDocShell() dereferences the freed Document.
  6. Primitive achieved Use-after-free of a Document object; with heap grooming the freed allocation could be reclaimed with attacker-controlled data (escalation inferred, not shown by the diff).

Impact Assessment

This runs in the content/renderer process and is reachable from ordinary web content via form-submission APIs, requiring no special privilege. The primitive is a use-after-free of a Document, reached during submission when script can run and drop the document. The diff proves the lifetime bug (raw pointer → RefPtr) but does not demonstrate a working exploit primitive beyond the dangling dereference; escalation to code execution would require controlling the freed allocation (inferred). Mozilla rates it moderate.

Files Changed

  • dom/html/HTMLFormElement.cpp

Audit Directions

  • Raw `GetComposedDoc()`/`GetDocShell()` across script
    Audit other HTMLFormElement and form-submission paths (and DOM code generally) that capture GetComposedDoc() or OwnerDoc() into a raw pointer and use it after any step that can dispatch events or run script.
  • Submission re-entrancy
    Review the whole submit flow (DoSubmit, submission constructors, formdata/submit dispatch) for other bare document/docshell pointers held across event dispatch.
  • Docshell/container lifetimes
    Check that nsDocShell/browsing-context pointers derived from a document stay valid when the document can be torn down mid-operation.
diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp
index b945dd5f775..618ff72ac9d 100644
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -795,7 +795,7 @@ nsresult HTMLFormElement::SubmitSubmission(
   }
 
   // If there is no link handler, then we won't actually be able to submit.
-  Document* doc = GetComposedDoc();
+  RefPtr<Document> doc = GetComposedDoc();
   RefPtr<nsDocShell> container =
       doc ? nsDocShell::Cast(doc->GetDocShell()) : nullptr;
   if (!container || IsEditable()) {
Loading diff…