High firefox Logic Error 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionUse-after-free in the DOM: Bindings (WebIDL) component
ComponentDOM
Bug ClassLogic Error
Tracker2081953
Fix commit5e5161b4fa63 (firefox) +46/-5
CISA KEVNot listed
CreditedOpenAI Codex Security (kylebot)
Disclosed2026-10-27

Changed Functions

FunctionChangeNotes
if
dom/base/nsGlobalWindowInner.cpp
modified
if
dom/base/nsGlobalWindowOuter.cpp
modified

Files Changed

  • dom/base/nsGlobalWindowInner.cpp
  • dom/base/nsGlobalWindowInner.h
  • dom/base/nsGlobalWindowOuter.cpp
diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp
index 62d2e74bb29..095b2f6fd47 100644
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -310,6 +310,8 @@
 #include "nsTLiteralString.h"
 #include "nsTObserverArray.h"
 #include "nsTStringRepr.h"
+#include "nsThread.h"
+#include "nsThreadManager.h"
 #include "nsThreadUtils.h"
 #include "nsWeakReference.h"
 #include "nsWindowMemoryReporter.h"
@@ -3918,8 +3920,13 @@ already_AddRefed<BrowsingContext> nsGlobalWindowInner::GetChildWindow(
 }
 
 void nsGlobalWindowInner::RefreshRealmPrincipal() {
-  JS::SetRealmPrincipals(js::GetNonCCWObjectRealm(GetWrapperPreserveColor()),
-                         nsJSPrincipals::get(mDoc->NodePrincipal()));
+  JS::Realm* realm = js::GetNonCCWObjectRealm(GetWrapperPreserveColor());
+  nsJSPrincipals* existing = nsJSPrincipals::get(JS::GetRealmPrincipals(realm));
+  nsJSPrincipals* jsPrincipals = nsJSPrincipals::get(mDoc->NodePrincipal());
+  if (existing != jsPrincipals) {
+    HoldOldPrincipal(existing);
+  }
+  JS::SetRealmPrincipals(realm, jsPrincipals);
 }
 
 void nsGlobalWindowInner::RefreshReduceTimerPrecisionCallerType() {
@@ -7977,6 +7984,25 @@ void nsGlobalWindowInner::UnnoteMediaSourceURL(const nsACString& aURL) {
   MOZ_ASSERT(found, "MediaSourceURL should have been noted");
 }
 
+void nsGlobalWindowInner::HoldOldPrincipal(nsIPrincipal* aPrincipal) {
+  mOldPrincipals.AppendElement(aPrincipal);
+  Dispatch(NewRunnableMethod(
+      "nsGlobalWindowInner::ClearOldPrincipalsIfNotNestedEventLoop", this,
+      &nsGlobalWindowInner::ClearOldPrincipalsIfNotNestedEventLoop));
+}
+
+void nsGlobalWindowInner::ClearOldPrincipalsIfNotNestedEventLoop() {
+  MOZ_ASSERT(NS_IsMainThread());
+  nsThread* mainThread = nsThreadManager::get().GetCurrentThread();
+  if (mainThread && mainThread->RecursionDepth() > 1) {
+    // Not OK to clear old principals from a nested event loop. In order
+    // to avoid spamming the nested event loop with runnables, just give
+    // up and leave the old principals around.
+    return;
+  }
+  mOldPrincipals.Clear();
+}
+
 void nsPIDOMWindowInner::MaybeSetHasPointerRawUpdateEventListeners() {
   if (HasPointerRawUpdateEventListeners() || !IsSecureContext()) {
     return;
diff --git a/dom/base/nsGlobalWindowInner.h b/dom/base/nsGlobalWindowInner.h
index 635262a0485..e804afe5ba7 100644
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -1321,7 +1321,11 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
     return mClientSource.get();
   }
 
+  void HoldOldPrincipal(nsIPrincipal* aPrincipal);
+
  private:
+  void ClearOldPrincipalsIfNotNestedEventLoop();
+
   RefPtr<mozilla::dom::ContentMediaController> mContentMediaController;
 
   RefPtr<mozilla::dom::WebTaskSchedulerMainThread> mWebTaskScheduler;
@@ -1537,6 +1541,12 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
 
   nsTArray<nsCString> mMediaSourceURLs;
 
+  // Holder for past principals to keep them alive so that we need to refcount
+  // them only when we actually swap principals and we don't need to refcount
+  // them on hot code paths in bindings that use raw pointers and must not
+  // get the principal deleted from underneath them.
+  nsTArray<nsCOMPtr<nsIPrincipal>> mOldPrincipals;
+
   // These fields are used by the inner and outer windows to prevent
   // programatically moving the window while the mouse is down.
   static bool sMouseDown;
diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp
index 9d5ac55c4c5..ddbc2ca41bb 100644
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -2269,14 +2269,19 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
     // currentInner->RefreshRealmPrincipals() here because we haven't yet set
     // its mDoc to aDocument.
     JS::Realm* realm = js::GetNonCCWObjectRealm(newInnerGlobal);
+    nsJSPrincipals* existing =
+        nsJSPrincipals::get(JS::GetRealmPrincipals(realm));
+    nsJSPrincipals* jsPrincipals =
+        nsJSPrincipals::get(aDocument->NodePrincipal());
+    if (existing != jsPrincipals) {
+      newInnerWindow->HoldOldPrincipal(existing);
+    }
 #ifdef DEBUG
     bool sameOrigin = false;
-    nsIPrincipal* existing = nsJSPrincipals::get(JS::GetRealmPrincipals(realm));
     aDocument->NodePrincipal()->Equals(existing, &sameOrigin);
     MOZ_ASSERT(sameOrigin);
 #endif
-    JS::SetRealmPrincipals(realm,
-                           nsJSPrincipals::get(aDocument->NodePrincipal()));
+    JS::SetRealmPrincipals(realm, jsPrincipals);
   } else {
     if (aState) {
       newInnerWindow = wsh->GetInnerWindow();
Loading diff…