High firefox Logic Error 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionUse-after-free in the DOM: Core & HTML component
ComponentDOM
Bug ClassLogic Error
Tracker2072433
Fix commitcaed6fbebca0 (firefox) +34/-8
CISA KEVNot listed
CreditedMozilla
Disclosed2026-09-29

Files Changed

  • dom/base/ContentList.cpp
  • dom/base/ContentList.h
  • dom/base/nsContentUtils.h
diff --git a/dom/base/ContentList.cpp b/dom/base/ContentList.cpp
index 2e64eae77ad..e9898923ec1 100644
--- a/dom/base/ContentList.cpp
+++ b/dom/base/ContentList.cpp
@@ -606,7 +606,8 @@ void ContentList::ContentAppended(nsIContent* aFirstNewContent,
       !nsContentUtils::IsInSameAnonymousTree(mRootNode, container) ||
       !MayContainRelevantNodes(container) ||
       (!aFirstNewContent->HasChildren() &&
-       !aFirstNewContent->GetNextSibling() && !MatchSelf(aFirstNewContent))) {
+       !aFirstNewContent->GetNextSibling() &&
+       !MatchSelf<MatchSelfMode::Insertion>(aFirstNewContent))) {
     MaybeMarkDirty();
     return;
   }
@@ -636,7 +637,7 @@ void ContentList::ContentAppended(nsIContent* aFirstNewContent,
     // The new stuff is somewhere in the middle of our list; check
     // whether we need to invalidate
     for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
-      if (MatchSelf(cur)) {
+      if (MatchSelf<MatchSelfMode::Insertion>(cur)) {
         // Uh-oh.  We're gonna have to add elements into the middle
         // of our list. That's not worth the effort.
         SetDirty();
@@ -690,7 +691,7 @@ void ContentList::ContentInserted(nsIContent* aChild,
   if (mState != State::Dirty &&
       MayContainRelevantNodes(aChild->GetParentNode()) &&
       nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
-      MatchSelf(aChild)) {
+      MatchSelf<MatchSelfMode::Insertion>(aChild)) {
     SetDirty();
   }
 
@@ -702,7 +703,7 @@ void ContentList::ContentWillBeRemoved(nsIContent* aChild,
   if (mState != State::Dirty &&
       MayContainRelevantNodes(aChild->GetParentNode()) &&
       nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
-      MatchSelf(aChild)) {
+      MatchSelf<MatchSelfMode::Removal>(aChild)) {
     SetDirty();
   }
 
@@ -741,6 +742,7 @@ bool ContentList::Match(Element* aElement) {
                    : ni->Equals(mXMLMatchAtom, mMatchNameSpaceId);
 }
 
+template <ContentList::MatchSelfMode Mode>
 bool ContentList::MatchSelf(nsIContent* aContent) {
   MOZ_ASSERT(aContent, "Can't match null stuff, you know");
   MOZ_ASSERT(mDeep || aContent->GetParentNode() == mRootNode,
@@ -750,13 +752,26 @@ bool ContentList::MatchSelf(nsIContent* aContent) {
     return false;
   }
 
-  if (Match(aContent->AsElement())) return true;
+  auto matches = [&](Element* aElement) {
+    if (Match(aElement)) {
+      return true;
+    }
+    if constexpr (Mode == MatchSelfMode::Removal) {
+      return mFunc == nsContentUtils::MatchClassNames &&
+             aElement->IsSVGElement() && aElement->MayHaveClass();
+    }
+    return false;
+  };
+
+  if (matches(aContent->AsElement())) {
+    return true;
+  }
 
   if (!mDeep) return false;
 
   for (nsIContent* cur = aContent->GetFirstChild(); cur;
        cur = cur->GetNextNode(aContent)) {
-    if (cur->IsElement() && Match(cur->AsElement())) {
+    if (cur->IsElement() && matches(cur->AsElement())) {
       return true;
     }
   }
diff --git a/dom/base/ContentList.h b/dom/base/ContentList.h
index 98dd407adc2..cc409aee8d7 100644
--- a/dom/base/ContentList.h
+++ b/dom/base/ContentList.h
@@ -381,6 +381,15 @@ class ContentList : public HTMLCollection, public nsStubMultiMutationObserver {
    * @return whether we match
    */
   bool Match(Element* aElement);
+
+  enum class MatchSelfMode : uint8_t {
+    Insertion,
+    // Also accept SVG elements that may have a class. SMIL can change their
+    // class without notification, so they can be in the list without
+    // matching now. Needed when aContent leaves the subtree.
+    Removal,
+  };
+
   /**
    * See if anything in the subtree rooted at aContent, including
    * aContent itself, matches our criterion.
@@ -388,6 +397,7 @@ class ContentList : public HTMLCollection, public nsStubMultiMutationObserver {
    * @param  aContent the root of the subtree to match against
    * @return whether we match something in the tree rooted at aContent
    */
+  template <MatchSelfMode Mode>
   bool MatchSelf(nsIContent* aContent);
 
   virtual nsINode* GetNextNode(nsINode* aCurrent);
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index d6635bbeb0e..90c81f9b28f 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2597,6 +2597,9 @@ class nsContentUtils {
   static already_AddRefed<mozilla::dom::ContentList> GetElementsByClassName(
       nsINode* aRootNode, const nsAString& aClasses);
 
+  static bool MatchClassNames(mozilla::dom::Element* aElement,
+                              int32_t aNamespaceID, nsAtom* aAtom, void* aData);
+
   /**
    * Returns a presshell for this document, if there is one. This will be
    * aDoc's direct presshell if there is one, otherwise we'll look at all
@@ -3769,8 +3772,6 @@ class nsContentUtils {
 
   static void DropFragmentParsers();
 
-  static bool MatchClassNames(mozilla::dom::Element* aElement,
-                              int32_t aNamespaceID, nsAtom* aAtom, void* aData);
   static void DestroyClassNameArray(void* aData);
   static void* AllocClassMatchingInfo(nsINode* aRootNode,
                                       const nsString* aClasses);
Loading diff…