High firefox UAF 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionUse-after-free in the DOM: Core & HTML component
ComponentDOM
Bug ClassUAF
Tracker2082001
Fix commit55a805b5cd01 (firefox) +2/-5
CISA KEVNot listed
CreditedOpenAI Codex Security (kylebot)
Disclosed2026-10-27

Background

`ElementInternals`
the object exposing a form-associated custom element’s internals; it references the containing form via mForm.
Cycle collection
Gecko’s GC for refcounted DOM graphs; a member only participates (and is safely cleared) if it is traversed/unlinked.

Root Cause Analysis

ElementInternals::mForm was a raw HTMLFormElement*, justified by a comment claiming it would always be reset via CustomElementData::Unlink when the target was released or unlinked. That assumption did not hold in all teardown orders, so mForm could outlive the form and dangle — a use-after-free.

The fix makes mForm a RefPtr<HTMLFormElement> and adds it to the cycle-collection traverse (NS_IMPL_CYCLE_COLLECTION_TRAVERSE(..., mForm)), removing the raw-pointer init; the form is now kept alive and participates in cycle collection correctly.

Key insight
A raw back-pointer to the form relied on an unlink invariant that could be violated; making it a cycle-collected RefPtr removes the dangling-pointer window.

Attack Path

  1. ENTRY From content, create a form-associated custom element and attach ElementInternals referencing a containing form.
  2. TRIGGER Tear down / move the element or form in an order where CustomElementData::Unlink does not reset mForm first.
  3. MISSING CHECK mForm is a raw pointer not traversed by cycle collection, so it is neither kept alive nor cleared.
  4. IMPACT A later access to mForm touches freed memory — use-after-free. (Exact teardown order inferred; the raw→RefPtr + traverse change is the proof.)

Impact Assessment

Content-reachable use-after-free of a form object via custom-element internals, a corruption primitive in the content process (escalation inferred). Found by OpenAI Codex Security; advisory severity high; no ITW stated.

Changed Functions

FunctionChangeNotes
for
dom/html/ElementInternals.cpp
modified
mControlNumber
dom/html/ElementInternals.cpp
modified

Files Changed

  • dom/html/ElementInternals.cpp
  • dom/html/ElementInternals.h

Audit Directions

  • Raw DOM back-pointers "safe via Unlink"
    Audit members documented as safe-because-unlink; verify every teardown/move path actually resets them, or convert to cycle-collected RefPtr.
diff --git a/dom/html/ElementInternals.cpp b/dom/html/ElementInternals.cpp
index 101e17623a2..c4c70a64195 100644
--- a/dom/html/ElementInternals.cpp
+++ b/dom/html/ElementInternals.cpp
@@ -40,7 +40,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ElementInternals)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTarget, mSubmissionValue, mState,
                                     mValidity, mValidationAnchor,
-                                    mCustomStateSet);
+                                    mCustomStateSet, mForm);
 
   for (auto& tableEntry : tmp->mAttrElementsMap) {
     auto& [explicitlySetElements, cachedAttrElements] =
@@ -62,7 +62,6 @@ NS_INTERFACE_MAP_END
 ElementInternals::ElementInternals(HTMLElement* aTarget)
     : nsIFormControl(FormControlType::FormAssociatedCustomElement),
       mTarget(aTarget),
-      mForm(nullptr),
       mFieldSet(nullptr),
       mControlNumber(-1) {}
 
diff --git a/dom/html/ElementInternals.h b/dom/html/ElementInternals.h
index d77db940e61..c77734ef60f 100644
--- a/dom/html/ElementInternals.h
+++ b/dom/html/ElementInternals.h
@@ -230,9 +230,7 @@ class ElementInternals final : public nsIFormControl,
   RefPtr<HTMLElement> mTarget;
 
   // The form that contains the target element.
-  // It's safe to use raw pointer because it will be reset via
-  // CustomElementData::Unlink when mTarget is released or unlinked.
-  HTMLFormElement* mForm;
+  RefPtr<HTMLFormElement> mForm;
 
   // This is a pointer to the target element's closest fieldset parent if any.
   // It's safe to use raw pointer because it will be reset via
Loading diff…