High firefox Logic Error 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionSandbox escape in the DOM: Networking component
ComponentDOM
Bug ClassLogic Error
Tracker2059961
Fix commit315f2bc61241 (firefox) +76/-24
CISA KEVNot listed
CreditedYaqoub Aldurayhim
Disclosed2026-10-27

Changed Functions

FunctionChangeNotes
if
dom/webtransport/parent/WebTransportParent.cpp
modified
if
netwerk/protocol/webtransport/WebTransportSessionProxy.cpp
modified
OnDraining
netwerk/protocol/webtransport/WebTransportSessionProxy.cpp
modified

Files Changed

  • dom/webtransport/parent/WebTransportParent.cpp
  • netwerk/protocol/webtransport/WebTransportSessionProxy.cpp
  • netwerk/protocol/webtransport/WebTransportSessionProxy.h
diff --git a/dom/webtransport/parent/WebTransportParent.cpp b/dom/webtransport/parent/WebTransportParent.cpp
index e5aaf4961a1..015d50e4edd 100644
--- a/dom/webtransport/parent/WebTransportParent.cpp
+++ b/dom/webtransport/parent/WebTransportParent.cpp
@@ -981,7 +981,9 @@ WebTransportParent::OnOutgoingDatagramOutCome(
   }
 
   // This assumes the stack is calling us back synchronously!
-  MOZ_ASSERT(mOutgoingDatagramResolver);
+  if (!mOutgoingDatagramResolver) {
+    return NS_OK;
+  }
   mOutgoingDatagramResolver(result);
   mOutgoingDatagramResolver = nullptr;
 
@@ -991,7 +993,9 @@ WebTransportParent::OnOutgoingDatagramOutCome(
 NS_IMETHODIMP WebTransportParent::OnMaxDatagramSize(uint64_t aSize) {
   LOG(("Max datagram size is %" PRIu64, aSize));
   MOZ_ASSERT(mSocketThread->IsOnCurrentThread());
-  MOZ_ASSERT(mMaxDatagramSizeResolver);
+  if (!mMaxDatagramSizeResolver) {
+    return NS_OK;
+  }
   mMaxDatagramSizeResolver(aSize);
   mMaxDatagramSizeResolver = nullptr;
   return NS_OK;
diff --git a/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp b/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp
index c356f01ca2f..f73d5167861 100644
--- a/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp
+++ b/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp
@@ -1280,26 +1280,41 @@ WebTransportSessionProxy::OnSessionClosed(bool aCleanly, uint32_t aStatus,
   return NS_OK;
 }
 
+void WebTransportSessionProxy::OnDrainingInternal() {
+  nsCOMPtr<WebTransportSessionEventListener> listener;
+  {
+    MutexAutoLock lock(mMutex);
+    if (!mTarget->IsOnCurrentThread()) {
+      // mTarget changed (RetargetTo) after this runnable was queued; forward to
+      // the current target so the listener is only ever invoked on one thread.
+      mTarget->Dispatch(NS_NewRunnableFunction(
+          "WebTransportSessionProxy::OnDrainingInternal",
+          [self = RefPtr{this}] { self->OnDrainingInternal(); }));
+      return;
+    }
+    listener = mListener;
+  }
+
+  if (listener) {
+    listener->OnDraining();
+  }
+}
+
 NS_IMETHODIMP
 WebTransportSessionProxy::OnDraining() {
   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
-  MutexAutoLock lock(mMutex);
   LOG(("WebTransportSessionProxy::OnDraining %p", this));
 
-  if (!mTarget->IsOnCurrentThread()) {
-    nsCOMPtr<WebTransportSessionEventListener> listener = mListener;
-    mTarget->Dispatch(NS_NewRunnableFunction(
-        "WebTransportSessionProxy::OnDraining", [listener]() {
-          if (listener) {
-            listener->OnDraining();
-          }
-        }));
-    return NS_OK;
+  {
+    MutexAutoLock lock(mMutex);
+    if (!mTarget->IsOnCurrentThread()) {
+      return mTarget->Dispatch(NS_NewRunnableFunction(
+          "WebTransportSessionProxy::OnDraining",
+          [self = RefPtr{this}] { self->OnDrainingInternal(); }));
+    }
   }
 
-  if (mListener) {
-    mListener->OnDraining();
-  }
+  OnDrainingInternal();
   return NS_OK;
 }
 
@@ -1314,7 +1329,16 @@ void WebTransportSessionProxy::OnStatsAvailableInternal(
   nsCOMPtr<WebTransportSessionEventListener> listener;
   {
     MutexAutoLock lock(mMutex);
-    MOZ_ASSERT(mTarget->IsOnCurrentThread());
+    if (!mTarget->IsOnCurrentThread()) {
+      // mTarget changed (RetargetTo) after this runnable was queued; forward to
+      // the current target so the listener is only ever invoked on one thread.
+      mTarget->Dispatch(NS_NewRunnableFunction(
+          "WebTransportSessionProxy::OnStatsAvailableInternal",
+          [self = RefPtr{this}, stats = aStats] {
+            self->OnStatsAvailableInternal(stats);
+          }));
+      return;
+    }
     listener = mListener;
   }
   if (!listener) {
@@ -1352,10 +1376,6 @@ WebTransportSessionProxy::OnStatsAvailable(
 
   {
     MutexAutoLock lock(mMutex);
-    // RetargetTo() runs synchronously before content can ever reach this
-    // point (its promise chain depends on it), so mTarget should already be
-    // the socket thread here.
-    MOZ_ASSERT(mTarget->IsOnCurrentThread());
     // Cache stats for use after connection is closed (spec requirement).
     if (rawStats) {
       mHasCachedStats = true;
@@ -1499,7 +1519,16 @@ void WebTransportSessionProxy::NotifyDatagramReceived(
   nsCOMPtr<WebTransportSessionEventListener> listener;
   {
     MutexAutoLock lock(mMutex);
-    MOZ_ASSERT(mTarget->IsOnCurrentThread());
+    if (!mTarget->IsOnCurrentThread()) {
+      // mTarget changed (RetargetTo) after this runnable was queued; forward to
+      // the current target so the listener is only ever invoked on one thread.
+      mTarget->Dispatch(NS_NewRunnableFunction(
+          "WebTransportSessionProxy::NotifyDatagramReceived",
+          [self = RefPtr{this}, data{std::move(aData)}]() mutable {
+            self->NotifyDatagramReceived(std::move(data));
+          }));
+      return;
+    }
 
     if (mState != WebTransportSessionProxyState::ACTIVE || !mListener) {
       return;
@@ -1547,8 +1576,6 @@ void WebTransportSessionProxy::OnMaxDatagramSizeInternal(uint64_t aSize) {
   nsCOMPtr<WebTransportSessionEventListener> listener;
   {
     MutexAutoLock lock(mMutex);
-    MOZ_ASSERT(mTarget->IsOnCurrentThread());
-
     if (!mStopRequestCalled) {
       mPendingEvents.AppendElement([self = RefPtr{this}, size(aSize)]() {
         self->OnMaxDatagramSizeInternal(size);
@@ -1556,6 +1583,17 @@ void WebTransportSessionProxy::OnMaxDatagramSizeInternal(uint64_t aSize) {
       return;
     }
 
+    if (!mTarget->IsOnCurrentThread()) {
+      // mTarget changed (RetargetTo) after this runnable was queued; forward to
+      // the current target so the listener is only ever invoked on one thread.
+      mTarget->Dispatch(NS_NewRunnableFunction(
+          "WebTransportSessionProxy::OnMaxDatagramSizeInternal",
+          [self = RefPtr{this}, size(aSize)] {
+            self->OnMaxDatagramSizeInternal(size);
+          }));
+      return;
+    }
+
     if (mState != WebTransportSessionProxyState::ACTIVE || !mListener) {
       return;
     }
@@ -1588,7 +1626,16 @@ void WebTransportSessionProxy::OnOutgoingDatagramOutComeInternal(
   nsCOMPtr<WebTransportSessionEventListener> listener;
   {
     MutexAutoLock lock(mMutex);
-    MOZ_ASSERT(mTarget->IsOnCurrentThread());
+    if (!mTarget->IsOnCurrentThread()) {
+      // mTarget changed (RetargetTo) after this runnable was queued; forward to
+      // the current target so the listener is only ever invoked on one thread.
+      mTarget->Dispatch(NS_NewRunnableFunction(
+          "WebTransportSessionProxy::OnOutgoingDatagramOutComeInternal",
+          [self = RefPtr{this}, id(aId), outcome(aOutCome)] {
+            self->OnOutgoingDatagramOutComeInternal(id, outcome);
+          }));
+      return;
+    }
     if (mState != WebTransportSessionProxyState::ACTIVE || !mListener) {
       return;
     }
diff --git a/netwerk/protocol/webtransport/WebTransportSessionProxy.h b/netwerk/protocol/webtransport/WebTransportSessionProxy.h
index 4ca97f8e9c4..852ac0180dc 100644
--- a/netwerk/protocol/webtransport/WebTransportSessionProxy.h
+++ b/netwerk/protocol/webtransport/WebTransportSessionProxy.h
@@ -181,6 +181,7 @@ class WebTransportSessionProxy final
   void GetMaxDatagramSizeInternal(
       const RefPtr<WebTransportSessionBase>& aSession);
   void OnMaxDatagramSizeInternal(uint64_t aSize);
+  void OnDrainingInternal();
   void GetStatsInternal(const RefPtr<WebTransportSessionBase>& aSession);
   void OnStatsAvailableInternal(
       const Maybe<mozilla::dom::WebTransportStatsData>& aStats);
Loading diff…