Medium firefox Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionSite isolation issue in the Networking component
ComponentDOM
Bug ClassLogic Error
Tracker2043200
Fix commit0379190125a3 (firefox) +89/-31
CISA KEVNot listed
CreditedAtsushi Sada
Disclosed2026-07-21

Changed Functions

FunctionChangeNotes
RegisterChannel
netwerk/base/RedirectChannelRegistrar.cpp
modified

Files Changed

  • docshell/base/nsDocShell.cpp
  • dom/ipc/ContentParent.cpp
  • mobile/android/components/geckoview/GeckoViewContentChannelParent.cpp
  • netwerk/base/RedirectChannelRegistrar.cpp
  • netwerk/base/RedirectChannelRegistrar.h
  • netwerk/base/nsIRedirectChannelRegistrar.idl
  • netwerk/base/nsNetUtil.cpp
  • netwerk/base/nsNetUtil.h
  • netwerk/ipc/DocumentLoadListener.cpp
  • netwerk/ipc/NeckoParent.cpp
  • netwerk/ipc/ParentChannelWrapper.cpp
  • netwerk/ipc/ParentChannelWrapper.h
  • netwerk/ipc/ParentProcessDocumentChannel.cpp
  • netwerk/protocol/http/HttpChannelParent.cpp
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index f1b0b1f7b6c..9b78ed2be44 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10433,7 +10433,9 @@ nsresult nsDocShell::OpenRedirectedChannel(nsDocShellLoadState* aLoadState) {
     // that forwards functionality as needed, and then we register
     // it under the provided identifier.
     RefPtr wrapper = MakeRefPtr<ParentChannelWrapper>(channel, loader);
-    wrapper->Register(aLoadState->GetPendingRedirectChannelRegistrarId());
+    // We're in the parent process, so the redirect is owned by the parent
+    // process (ContentParentId 0).
+    wrapper->Register(aLoadState->GetPendingRedirectChannelRegistrarId(), 0);
 
     mLoadGroup->AddRequest(channel, nullptr);
   } else if (nsCOMPtr<nsIChildChannel> childChannel =
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
index 517aa43bd05..a35f7e4e082 100644
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4846,14 +4846,16 @@ mozilla::ipc::IPCResult ContentParent::RecvExtProtocolChannelConnectParent(
 
   // First get the real channel created before redirect on the parent.
   nsCOMPtr<nsIChannel> channel;
-  rv = NS_LinkRedirectChannels(registrarId, nullptr, getter_AddRefs(channel));
+  rv = NS_LinkRedirectChannels(registrarId, ChildID(), nullptr,
+                               getter_AddRefs(channel));
   NS_ENSURE_SUCCESS(rv, IPC_OK());
 
   nsCOMPtr<nsIParentChannel> parent = do_QueryInterface(channel, &rv);
   NS_ENSURE_SUCCESS(rv, IPC_OK());
 
   // The channel itself is its own (faked) parent, link it.
-  rv = NS_LinkRedirectChannels(registrarId, parent, getter_AddRefs(channel));
+  rv = NS_LinkRedirectChannels(registrarId, ChildID(), parent,
+                               getter_AddRefs(channel));
   NS_ENSURE_SUCCESS(rv, IPC_OK());
 
   // Signal the parent channel that it's a redirect-to parent.  This will
diff --git a/mobile/android/components/geckoview/GeckoViewContentChannelParent.cpp b/mobile/android/components/geckoview/GeckoViewContentChannelParent.cpp
index aa72b5131ca..01d3225f2bc 100644
--- a/mobile/android/components/geckoview/GeckoViewContentChannelParent.cpp
+++ b/mobile/android/components/geckoview/GeckoViewContentChannelParent.cpp
@@ -197,8 +197,10 @@ bool GeckoViewContentChannelParent::Init(
 bool GeckoViewContentChannelParent::Init(
     const GeckoViewContentChannelConnectArgs& aArgs) {
   nsCOMPtr<nsIChannel> channel;
-  nsresult rv =
-      NS_LinkRedirectChannels(aArgs.channelId(), this, getter_AddRefs(channel));
+  dom::ContentParentId cpId =
+      static_cast<dom::ContentParent*>(Manager()->Manager())->ChildID();
+  nsresult rv = NS_LinkRedirectChannels(aArgs.channelId(), cpId, this,
+                                        getter_AddRefs(channel));
   if (NS_SUCCEEDED(rv)) {
     mChannel = channel;
   }
diff --git a/netwerk/base/RedirectChannelRegistrar.cpp b/netwerk/base/RedirectChannelRegistrar.cpp
index 9aa687ed88d..8fbc36e4faa 100644
--- a/netwerk/base/RedirectChannelRegistrar.cpp
+++ b/netwerk/base/RedirectChannelRegistrar.cpp
@@ -34,10 +34,12 @@ RedirectChannelRegistrar::GetOrCreate() {
 }
 
 NS_IMETHODIMP
-RedirectChannelRegistrar::RegisterChannel(nsIChannel* channel, uint64_t id) {
+RedirectChannelRegistrar::RegisterChannel(nsIChannel* channel, uint64_t id,
+                                          uint64_t aContentParentId) {
   MutexAutoLock lock(mLock);
 
   mRealChannels.InsertOrUpdate(id, channel);
+  mChannelOwners.InsertOrUpdate(id, aContentParentId);
 
   return NS_OK;
 }
@@ -53,10 +55,19 @@ RedirectChannelRegistrar::GetRegisteredChannel(uint64_t id,
 }
 
 NS_IMETHODIMP
-RedirectChannelRegistrar::LinkChannels(uint64_t id, nsIParentChannel* channel,
+RedirectChannelRegistrar::LinkChannels(uint64_t id, uint64_t aContentParentId,
+                                       nsIParentChannel* channel,
                                        nsIChannel** _retval) {
   MutexAutoLock lock(mLock);
 
+  // Only hand back the registered channel if it was registered for the
+  // requesting content process, since the id is supplied by the content
+  // process.
+  uint64_t owner;
+  if (!mChannelOwners.Get(id, &owner) || owner != aContentParentId) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   if (!mRealChannels.Get(id, _retval)) return NS_ERROR_NOT_AVAILABLE;
 
   mParentChannels.InsertOrUpdate(id, channel);
@@ -79,6 +90,7 @@ RedirectChannelRegistrar::DeregisterChannels(uint64_t id) {
 
   mRealChannels.Remove(id);
   mParentChannels.Remove(id);
+  mChannelOwners.Remove(id);
   return NS_OK;
 }
 
diff --git a/netwerk/base/RedirectChannelRegistrar.h b/netwerk/base/RedirectChannelRegistrar.h
index 1d5b14a4d71..48acea2c606 100644
--- a/netwerk/base/RedirectChannelRegistrar.h
+++ b/netwerk/base/RedirectChannelRegistrar.h
@@ -10,6 +10,7 @@
 #include "nsIChannel.h"
 #include "nsIParentChannel.h"
 #include "nsInterfaceHashtable.h"
+#include "nsTHashMap.h"
 #include "mozilla/Mutex.h"
 
 namespace mozilla {
@@ -35,6 +36,13 @@ class RedirectChannelRegistrar final : public nsIRedirectChannelRegistrar {
 
   ChannelHashtable mRealChannels MOZ_GUARDED_BY(mLock);
   ParentChannelHashtable mParentChannels MOZ_GUARDED_BY(mLock);
+
+  // Maps a registered channel id to the ContentParentId (as a raw uint64_t,
+  // 0 for the parent process) of the process the redirect is destined for.
+  // linkChannels refuses to pair a real channel with a parent actor coming
+  // from any other process.
+  nsTHashMap<nsUint64HashKey, uint64_t> mChannelOwners MOZ_GUARDED_BY(mLock);
+
   Mutex mLock;
 
   static StaticRefPtr<RedirectChannelRegistrar> gSingleton;
diff --git a/netwerk/base/nsIRedirectChannelRegistrar.idl b/netwerk/base/nsIRedirectChannelRegistrar.idl
index 26dfdf3c6f1..15e522bd883 100644
--- a/netwerk/base/nsIRedirectChannelRegistrar.idl
+++ b/netwerk/base/nsIRedirectChannelRegistrar.idl
@@ -15,30 +15,37 @@ interface nsIParentChannel;
  * See also nsIChildChannel and nsIParentChannel.
  */
 
-[scriptable, uuid (efa36ea2-5b07-46fc-9534-a5acb8b77b72)]
+[scriptable, uuid (1f2c5e0a-9b3d-4d8e-8c4a-6d7e0b1a2c3d)]
 interface nsIRedirectChannelRegistrar : nsISupports
 {
   /**
    * Register the redirect target channel. The passed id needs to be a
    * unique ID for that channel (see `nsContentUtils::GenerateLoadIdentifier`).
+   * aContentParentId identifies the content process the redirect is destined
+   * for (0 for the parent process); linkChannels will only pair the channel
+   * with a parent actor coming from that same process.
    *
    * Primarily used in ParentChannelListener::AsyncOnChannelRedirect to get
    * a channel id sent to the HttpChannelChild being redirected.
    */
-  void registerChannel(in nsIChannel channel, in uint64_t id);
+  void registerChannel(in nsIChannel channel, in uint64_t id,
+                       in uint64_t aContentParentId);
 
   /**
-   * First, search for the channel registered under the id.  If found return
-   * it.  Then, register under the same id the parent side of IPC protocol
-   * to let it be later grabbed back by the originator of the redirect and
-   * notifications from the real channel could be forwarded to this parent
-   * channel.
+   * First, search for the channel registered under the id.  If found, and the
+   * registered channel is owned by aContentParentId, return it. Then, register
+   * under the same id the parent side of IPC protocol to let it be later
+   * grabbed back by the originator of the redirect and notifications from the
+   * real channel could be forwarded to this parent channel. Fails with
+   * NS_ERROR_NOT_AVAILABLE if no channel is registered under the id for the
+   * given content process.
    *
    * Primarily used in parent side of an IPC protocol implementation
    * in reaction to nsIChildChannel.connectParent(id) called from the child
-   * process.
+   * process. aContentParentId is the process that made that request.
    */
-  nsIChannel linkChannels(in uint64_t id, in nsIParentChannel channel);
+  nsIChannel linkChannels(in uint64_t id, in uint64_t aContentParentId,
+                          in nsIParentChannel channel);
 
   /**
    * Returns back the channel previously registered under the ID with
diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp
index 9941a85331b..5b27e9190c2 100644
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2637,14 +2637,15 @@ bool NS_ShouldRemoveAuthHeaderOnRedirect(nsIChannel* aOldChannel,
   return NS_FAILED(rv);
 }
 
-nsresult NS_LinkRedirectChannels(uint64_t channelId,
+nsresult NS_LinkRedirectChannels(uint64_t channelId, uint64_t aContentParentId,
                                  nsIParentChannel* parentChannel,
                                  nsIChannel** _result) {
   nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
       RedirectChannelRegistrar::GetOrCreate();
   MOZ_ASSERT(registrar);
 
-  return registrar->LinkChannels(channelId, parentChannel, _result);
+  return registrar->LinkChannels(channelId, aContentParentId, parentChannel,
+                                 _result);
 }
 
Loading diff…