Medium firefox Logic Error 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionSite isolation issue in the Networking: HTTP component
ComponentNetworking
Bug ClassLogic Error
Tracker2032140
Fix commit2ebc5198bee4 (firefox) +39/-5
CISA KEVNot listed
Creditedpakhunov.anton.n
Disclosed2026-07-21

Background

`HttpChannelParent`
the parent-process IPC actor that services an HTTP channel opened by a content process over the PHttpChannel protocol.
`HttpBackgroundChannelParent`
a companion parent-process actor on the PBackground protocol that delivers asynchronous channel events back to the same content process.
`BackgroundChannelRegistrar`
the parent-process registry that pairs an HttpChannelParent with its matching HttpBackgroundChannelParent using a content-supplied channel Id as the key.
`ContentParentId`
a dom::ContentParentId (child ID) that uniquely identifies which content process a parent-side actor belongs to.

Root Cause Analysis

When a content process opened an HTTP channel, BackgroundChannelRegistrar::NotifyChannelLinked paired the HttpChannelParent with an HttpBackgroundChannelParent solely by matching a uint64_t channel Id, and that channel Id is chosen and supplied entirely by the content process. Because the registrar never checked that the two actors originated from the same content process, a compromised or malicious content process could supply a channel Id that collides with one belonging to a different content process, causing the registrar to cross-link the two actors and violating the invariant that a channel and its background channel must stay within one process boundary (site isolation).

The fix threads the owning process identity through both actors: HttpBackgroundChannelParent::Init now takes and stores a dom::ContentParentId captured via BackgroundParent::GetChildID, HttpChannelParent::GetContentParentId derives its owner from its IPC manager chain, and NotifyChannelLinked refuses to link the pair when GetContentParentId() differs between them. This restores the invariant by making process identity — which the parent derives itself and the content process cannot forge — a mandatory part of the linking decision rather than trusting only the attacker-controlled key.

Key insight
The core mistake was using an attacker-supplied channel Id as the sole key for pairing two parent-side IPC actors without binding that key to the originating content process. The fix records each actor’s ContentParentId from trusted parent-side sources and rejects any link whose two halves come from different processes.

Attack Path

  1. Compromise a content process An attacker gains code execution in one content process (for example via a renderer bug) so it can drive PHttpChannel and PBackground IPC directly.
  2. Predict or observe a victim channel Id The attacker arranges for a channel Id that collides with the uint64_t channel Id of a channel belonging to a different content process.
  3. Open a crafted background channel The malicious process constructs an HttpBackgroundChannelParent and calls Init with the colliding channel Id, registering it in BackgroundChannelRegistrar.
  4. Trigger cross-process linking NotifyChannelLinked matches the two actors purely by channel Id and pairs the attacker’s background channel with the victim’s HttpChannelParent.
  5. Receive another origin's channel events The attacker’s process now receives background channel events destined for the victim process, breaching the site-isolation boundary.

Impact Assessment

An attacker in one content process could cause an HTTP channel’s background events to be delivered across the process boundary, letting a compromised or malicious renderer interfere with or observe another origin’s channel activity and defeating site isolation. The consequence is confined to the parent-process networking IPC linkage between content processes; the metadata rates it a moderate, medium-severity logic error. The precondition is the ability to drive HTTP and PBackground IPC with a chosen channel Id, i.e. control of at least one content process alongside a colliding channel Id.

Files Changed

  • ipc/glue/BackgroundParentImpl.cpp
  • netwerk/protocol/http/BackgroundChannelRegistrar.cpp
  • netwerk/protocol/http/HttpBackgroundChannelParent.cpp
  • netwerk/protocol/http/HttpBackgroundChannelParent.h
  • netwerk/protocol/http/HttpChannelParent.cpp
  • netwerk/protocol/http/HttpChannelParent.h

Audit Directions

  • Content-supplied keys as identity
    Audit any parent-process registry or map keyed on values (IDs, handles, tokens) chosen by a content process and confirm each lookup also verifies the requesting process’s identity.
  • Cross-actor linking across IPC protocols
    Wherever two actors from different IPC protocols (PHttpChannel and PBackground here) are paired, verify both halves are bound to and checked against the same ContentParentId before linking.
  • Trusted vs. untrusted origin derivation
    Prefer deriving process identity from parent-side facilities like BackgroundParent::GetChildID or the actor manager chain rather than trusting fields sent in the IPC payload.
diff --git a/ipc/glue/BackgroundParentImpl.cpp b/ipc/glue/BackgroundParentImpl.cpp
index 292e261ee48..a401403b92c 100644
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -1073,7 +1073,12 @@ BackgroundParentImpl::RecvPHttpBackgroundChannelConstructor(
   net::HttpBackgroundChannelParent* aParent =
       static_cast<net::HttpBackgroundChannelParent*>(aActor);
 
-  if (NS_WARN_IF(NS_FAILED(aParent->Init(aChannelId)))) {
+  // Record the content process that owns this PBackground actor, so the
+  // background channel can only ever be paired with a HttpChannelParent
+  // from the same process.
+  dom::ContentParentId cpId(BackgroundParent::GetChildID(this));
+
+  if (NS_WARN_IF(NS_FAILED(aParent->Init(cpId, aChannelId)))) {
     return IPC_FAIL_NO_REASON(this);
   }
 
diff --git a/netwerk/protocol/http/BackgroundChannelRegistrar.cpp b/netwerk/protocol/http/BackgroundChannelRegistrar.cpp
index 960ce95715a..b397c9235de 100644
--- a/netwerk/protocol/http/BackgroundChannelRegistrar.cpp
+++ b/netwerk/protocol/http/BackgroundChannelRegistrar.cpp
@@ -48,6 +48,13 @@ void BackgroundChannelRegistrar::NotifyChannelLinked(
   MOZ_ASSERT(aChannelParent);
   MOZ_ASSERT(aBgParent);
 
+  // Only link the two actors when they originate from the same content
+  // process, since the channel Id used as the key is supplied by
+  // the content process.
+  if (aChannelParent->GetContentParentId() != aBgParent->GetContentParentId()) {
+    return;
+  }
+
   aBgParent->LinkToChannel(aChannelParent);
   aChannelParent->OnBackgroundParentReady(aBgParent);
 }
diff --git a/netwerk/protocol/http/HttpBackgroundChannelParent.cpp b/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
index 22c60ffedc0..5b198b8380d 100644
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.cpp
@@ -79,12 +79,15 @@ HttpBackgroundChannelParent::~HttpBackgroundChannelParent() {
   MOZ_ASSERT(!mIPCOpened);
 }
 
-nsresult HttpBackgroundChannelParent::Init(const uint64_t& aChannelId) {
+nsresult HttpBackgroundChannelParent::Init(const dom::ContentParentId& aCpId,
+                                           const uint64_t& aChannelId) {
   LOG(("HttpBackgroundChannelParent::Init [this=%p channelId=%" PRIu64 "]\n",
        this, aChannelId));
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
+  mContentParentId = aCpId;
+
   RefPtr<ContinueAsyncOpenRunnable> runnable =
       new ContinueAsyncOpenRunnable(this, aChannelId);
 
diff --git a/netwerk/protocol/http/HttpBackgroundChannelParent.h b/netwerk/protocol/http/HttpBackgroundChannelParent.h
index db89d0f240e..31aa564d18b 100644
--- a/netwerk/protocol/http/HttpBackgroundChannelParent.h
+++ b/netwerk/protocol/http/HttpBackgroundChannelParent.h
@@ -8,6 +8,7 @@
 #include "mozilla/net/PHttpBackgroundChannelParent.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/dom/ipc/IdType.h"
 #include "nsID.h"
 #include "nsISupportsImpl.h"
 
@@ -24,9 +25,14 @@ class HttpBackgroundChannelParent final : public PHttpBackgroundChannelParent {
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HttpBackgroundChannelParent, final)
 
-  // Try to find associated HttpChannelParent with the same
-  // channel Id.
-  nsresult Init(const uint64_t& aChannelId);
+  // Try to find associated HttpChannelParent with the same content process
+  // and channel Id.
+  nsresult Init(const dom::ContentParentId& aCpId, const uint64_t& aChannelId);
+
+  // The content process that opened this background channel. Used by
+  // BackgroundChannelRegistrar to ensure the channel is only linked to a
+  // HttpChannelParent belonging to the same process.
+  dom::ContentParentId GetContentParentId() const { return mContentParentId; }
 
   // Callbacks for BackgroundChannelRegistrar to notify
   // the associated HttpChannelParent is found.
@@ -112,6 +118,10 @@ class HttpBackgroundChannelParent final : public PHttpBackgroundChannelParent {
   nsCOMPtr<nsISerialEventTarget> mBackgroundThread
       MOZ_GUARDED_BY(mBgThreadMutex);
 
+  // The content process that opened this background channel, set once in Init
+  // before the actor is registered.
+  dom::ContentParentId mContentParentId;
+
   // associated HttpChannelParent for generating the channel events
   RefPtr<HttpChannelParent> mChannelParent;
 };
diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp
index bd284f6345f..f2e8d562495 100644
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -212,6 +212,10 @@ void HttpChannelParent::TryInvokeAsyncOpen(nsresult aRv) {
   InvokeAsyncOpen(aRv);
 }
 
+dom::ContentParentId HttpChannelParent::GetContentParentId() const {
+  return static_cast<ContentParent*>(Manager()->Manager())->ChildID();
+}
+
 void HttpChannelParent::OnBackgroundParentReady(
     HttpBackgroundChannelParent* aBgParent) {
   LOG(("HttpChannelParent::OnBackgroundParentReady [this=%p bgParent=%p]\n",
diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h
index 9f48ab236e2..3fd64fd0c8b 100644
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -102,6 +102,11 @@ class HttpChannelParent final : public nsIInterfaceRequestor,
                                 const nsACString& aPromptAction,
                                 const nsACString& aTopLevelSite);
 
+  // The content process this channel parent belongs to. Used by
+  // BackgroundChannelRegistrar to ensure a background channel is only linked
+  // to a channel from the same process.
+  dom::ContentParentId GetContentParentId() const;
+
   // Callback while background channel is ready.
   void OnBackgroundParentReady(HttpBackgroundChannelParent* aBgParent);
   // Callback while background channel is destroyed.
Loading diff…