CVE-2026-16383
Overview
Background
- WebTransport
- A DOM networking API that opens an HTTP/3-based bidirectional transport to a server from a content process.
- `ClientInfo` / `IPCClientInfo`
- A structure identifying the originating client (window or worker) whose IPC form (
IPCClientInfo) is sent from the content process to the parent to associate the connection with a security context. - `mozilla::Maybe<T>`
- Gecko’s optional wrapper that either holds a value (
Some) or explicitly holds nothing (Nothing), distinguishing genuine absence from a fabricated default. - `WebTransportParent`
- The parent-process (
PBackground) actor that actually performs the privilegedAsyncConnectWithClienton behalf of a content-process request.
Root Cause Analysis
On the content side, WebTransport::Init refused to proceed when mGlobal->GetClientInfo() was Nothing, throwing NS_ERROR_DOM_INVALID_STATE_ERR, so the honest path only ever transmitted a populated IPCClientInfo. However, the IPDL message CreateWebTransportParent declared aClientInfo as a mandatory, non-nullable IPCClientInfo, meaning the parent could never observe “no client” and instead received a value that a compromised or malicious content process could freely fabricate. In WebTransportParent::Create, the parent unconditionally wrapped this attacker-influenced field as Some(ClientInfo{aClientInfo}) and fed it into AsyncConnectWithClient, so any client-info-based security or partitioning decision downstream operated on unvalidated, spoofable data — the violated invariant being that a client identity presented to the parent must reflect a real, verified client.
The fix changes the IPDL field to nullable (IPCClientInfo?) and threads mozilla::Maybe<IPCClientInfo> end-to-end, so WebTransport::Init now sends Nothing when there is no client rather than throwing, and the parent forwards the honest Maybe (including Nothing) into AsyncConnectWithClient instead of manufacturing a Some. This works because the parent no longer coerces a possibly-bogus value into a valid-looking client identity, letting the connection code correctly handle genuine absence.
Some, which let the parent treat a spoofable or absent value as a legitimate present one; the fix restores the optionality (Maybe<IPCClientInfo> / IPCClientInfo?) so absence is represented truthfully across the trust boundary.Attack Path
- Compromise or drive a content process
An attacker running script in (or having compromised) a content process controls the arguments marshalled into the
CreateWebTransportParentIPC message. - Supply a forged mandatory client info
Because
aClientInfowas non-nullable, the sender can populate it with an arbitrary or defaultIPCClientInforather than a genuine, verified client identity. - Parent accepts and wraps the value
BackgroundParentImpl::RecvCreateWebTransportParentforwards the field intoWebTransportParent::Create, which unconditionally buildsSome(ClientInfo{aClientInfo}). - Privileged connect uses the spoofed identity
The parent passes the fabricated client info into
AsyncConnectWithClient, so any mitigation keyed on client identity is evaluated against attacker-controlled data.
Impact Assessment
PBackground) process during WebTransport setup; the precondition is the ability to send the CreateWebTransportParent IPC message (script in a content process, or a compromised content process). Consistent with the metadata, this is a moderate/medium logic error rather than direct memory corruption.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifdom/webtransport/api/WebTransport.cpp |
modified |
Files Changed
dom/webtransport/api/WebTransport.cppdom/webtransport/parent/WebTransportParent.cppdom/webtransport/parent/WebTransportParent.hipc/glue/BackgroundParentImpl.cppipc/glue/BackgroundParentImpl.hipc/glue/PBackground.ipdl
Audit Directions
- Non-nullable IPC fields that model optional dataReview IPDL messages where a logically optional identity or context (client info, principals, tokens) is declared mandatory, since senders can then supply defaults the receiver cannot distinguish from real values.
- Parent-side wrapping of untrusted values in `Some`Flag parent-process code that unconditionally converts an IPC-received value into
Some(...)or a “valid” wrapper, which hides absence and forces downstream security checks onto unvalidated data. - Client-info / identity consumers in privileged connect pathsTrace all callers of
AsyncConnectWithClientand similar routines to confirm they validate and correctly handleNothingrather than trusting a caller-provided identity.
Patch
diff --git a/dom/webtransport/api/WebTransport.cpp b/dom/webtransport/api/WebTransport.cpp
index 332dba455d1..23525bc5884 100644
--- a/dom/webtransport/api/WebTransport.cpp
+++ b/dom/webtransport/api/WebTransport.cpp
@@ -287,13 +287,12 @@ void WebTransport::Init(const GlobalObject& aGlobal, const nsAString& aURL,
return;
}
- if (mGlobal->GetClientInfo().isNothing()) {
- aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
- return;
- }
- IPCClientInfo ipcClientInfo = mGlobal->GetClientInfo().ref().ToIPC();
-
nsCOMPtr<nsIPrincipal> principal = mGlobal->PrincipalOrNull();
+ mozilla::Maybe<IPCClientInfo> ipcClientInfo;
+
+ if (mGlobal->GetClientInfo().isSome()) {
+ ipcClientInfo = mozilla::Some(mGlobal->GetClientInfo().ref().ToIPC());
+ }
nsPIDOMWindowInner* window = mGlobal->GetAsInnerWindow();
if (window) {
diff --git a/dom/webtransport/parent/WebTransportParent.cpp b/dom/webtransport/parent/WebTransportParent.cpp
index ff25539eb8f..2a56118b34d 100644
--- a/dom/webtransport/parent/WebTransportParent.cpp
+++ b/dom/webtransport/parent/WebTransportParent.cpp
@@ -33,9 +33,9 @@ WebTransportParent::~WebTransportParent() {
void WebTransportParent::Create(
const nsAString& aURL, nsIPrincipal* aPrincipal,
- const uint64_t& aBrowsingContextID, const IPCClientInfo& aClientInfo,
- const bool& aDedicated, const bool& aRequireUnreliable,
- const uint32_t& aCongestionControl,
+ const uint64_t& aBrowsingContextID,
+ const mozilla::Maybe<IPCClientInfo>& aClientInfo, const bool& aDedicated,
+ const bool& aRequireUnreliable, const uint32_t& aCongestionControl,
nsTArray<WebTransportHash>&& aServerCertHashes,
Endpoint<PWebTransportParent>&& aParentEndpoint,
std::function<void(std::tuple<const nsresult&, const uint8_t&>)>&&
@@ -93,11 +93,11 @@ void WebTransportParent::Create(
nsServerCertHashes = std::move(nsServerCertHashes),
principal = RefPtr{aPrincipal}, browsingContextID = aBrowsingContextID,
flags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
- clientInfo = ClientInfo{aClientInfo}] {
+ clientInfo = aClientInfo] {
LOG(("WebTransport %p AsyncConnect", self.get()));
if (NS_FAILED(self->mWebTransport->AsyncConnectWithClient(
uri, dedicated, std::move(nsServerCertHashes), principal,
- browsingContextID, flags, self, Some(clientInfo),
+ browsingContextID, flags, self, clientInfo,
nsIWebTransport::HTTPVersion::h3))) {
LOG(("AsyncConnect failure; we should get OnSessionClosed"));
}
diff --git a/dom/webtransport/parent/WebTransportParent.h b/dom/webtransport/parent/WebTransportParent.h
index 766026317dd..1215e99fbf5 100644
--- a/dom/webtransport/parent/WebTransportParent.h
+++ b/dom/webtransport/parent/WebTransportParent.h
@@ -33,8 +33,8 @@ class WebTransportParent : public PWebTransportParent,
void Create(const nsAString& aURL, nsIPrincipal* aPrincipal,
const uint64_t& aBrowsingContextID,
- const IPCClientInfo& aClientInfo, const bool& aDedicated,
- const bool& aRequireUnreliable,
+ const mozilla::Maybe<IPCClientInfo>& aClientInfo,
+ const bool& aDedicated, const bool& aRequireUnreliable,
const uint32_t& aCongestionControl,
nsTArray<WebTransportHash>&& aServerCertHashes,
Endpoint<PWebTransportParent>&& aParentEndpoint,
diff --git a/ipc/glue/BackgroundParentImpl.cpp b/ipc/glue/BackgroundParentImpl.cpp
index 65e6c92d41f..95dd1148a1a 100644
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -463,9 +463,9 @@ mozilla::ipc::IPCResult BackgroundParentImpl::RecvCreateFileSystemManagerParent(
mozilla::ipc::IPCResult BackgroundParentImpl::RecvCreateWebTransportParent(
const nsAString& aURL, nsIPrincipal* aPrincipal,
- const uint64_t& aBrowsingContextID, const IPCClientInfo& aClientInfo,
- const bool& aDedicated, const bool& aRequireUnreliable,
- const uint32_t& aCongestionControl,
+ const uint64_t& aBrowsingContextID,
+ const mozilla::Maybe<IPCClientInfo>& aClientInfo, const bool& aDedicated,
+ const bool& aRequireUnreliable, const uint32_t& aCongestionControl,
nsTArray<WebTransportHash>&& aServerCertHashes,
Endpoint<PWebTransportParent>&& aParentEndpoint,
CreateWebTransportParentResolver&& aResolver) {
diff --git a/ipc/glue/BackgroundParentImpl.h b/ipc/glue/BackgroundParentImpl.h
index 8a6f056be76..429fd9b3f57 100644
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -121,9 +121,9 @@ class BackgroundParentImpl : public PBackgroundParent {
mozilla::ipc::IPCResult RecvCreateWebTransportParent(
const nsAString& aURL, nsIPrincipal* aPrincipal,
- const uint64_t& aBrowsingContextID, const IPCClientInfo& aClientInfo,
- const bool& aDedicated, const bool& aRequireUnreliable,
- const uint32_t& aCongestionControl,
+ const uint64_t& aBrowsingContextID,
+ const mozilla::Maybe<IPCClientInfo>& aClientInfo, const bool& aDedicated,
+ const bool& aRequireUnreliable, const uint32_t& aCongestionControl,
nsTArray<WebTransportHash>&& aServerCertHashes,
Endpoint<PWebTransportParent>&& aParentEndpoint,
CreateWebTransportParentResolver&& aResolver) override;
diff --git a/ipc/glue/PBackground.ipdl b/ipc/glue/PBackground.ipdl
index bfc523c53e0..3575bc03208 100644
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -178,7 +178,7 @@ parent:
nsString aURL,
nullable nsIPrincipal aPrincipal,
uint64_t aBrowsingContextID,
- IPCClientInfo aClientInfo,
+ IPCClientInfo? aClientInfo,
bool aDedicated,
bool aRequireUnreliable,
uint32_t aCongestionControl,