Race condition and insufficient principal validation in BackgroundParentImpl::RecvPBroadcastChannelConstructor enables cross-origin BroadcastChannel injection
Categories
(Core :: DOM: postMessage, defect)
Tracking
()
People
(Reporter: tranquac0312, Unassigned)
Details
(Keywords: reporter-external, Whiteboard: [client-bounty-form])
Attachments
(2 files)
|
1.75 KB,
text/x-c++src
|
Details | |
|
1.66 KB,
patch
|
Details | Diff | Splinter Review |
Summary
RecvPBroadcastChannelConstructor (BackgroundParentImpl.cpp:789) dispatches an async principal check to the main thread, but the actor is already registered and operational before the check runs. The check itself only compares two attacker-controlled IPC values (PrincipalInfo-derived origin vs aOrigin), not whether the process is authorized for the principal. A compromised content process joins any origin's BroadcastChannel to inject or sniff cross-origin messages.
Root Cause
// BackgroundParentImpl.cpp:713-731 — Alloc: actor registered BEFORE check
PBroadcastChannelParent*
BackgroundParentImpl::AllocPBroadcastChannelParent(
const PrincipalInfo& aPrincipalInfo, const nsACString& aOrigin,
const nsAString& aChannel) {
nsString originChannelKey;
originChannelKey.Assign(aChannel);
originChannelKey.AppendLiteral("|");
originChannelKey.Append(NS_ConvertUTF8toUTF16(aOrigin));
return new BroadcastChannelParent(originChannelKey);
// Actor keyed on attacker-controlled aOrigin — already in the channel
}
// BackgroundParentImpl.cpp:789-812 — Recv: async check, returns IPC_OK() immediately
IPCResult BackgroundParentImpl::RecvPBroadcastChannelConstructor(...) {
RefPtr<CheckPrincipalRunnable> runnable =
new CheckPrincipalRunnable(parent.forget(), aPrincipalInfo, aOrigin);
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
return IPC_OK(); // Actor already active — messages flow before check runs
}
Two flaws:
-
Race condition:
AllocPBroadcastChannelParentregisters the actor withoriginChannelKeyderived from attacker-controlledaOriginBEFORE any validation. Messages can be sent/received in the window between registration and the async check completing. -
Insufficient check:
CheckPrincipalRunnable(line 736) only verifies thatPrincipalInfo.GetOrigin() == aOrigin— both values come from the content process. A compromised process sends consistent forged values and the check always passes. NoValidatePrincipalor process-authorization check exists.
What Gets Bypassed
BroadcastChannel origin isolation. The originChannelKey format is <channelName>|<origin>. By forging aOrigin, the attacker's actor joins the same channel key as the victim origin. This enables:
- Message injection — attacker sends messages that arrive at all BroadcastChannel listeners for the victim origin
- Message sniffing — attacker receives all messages broadcast by the victim origin on that channel
Impact
A compromised content process can:
- Read cross-origin BroadcastChannel messages — inter-tab communication for any origin is exposed
- Inject messages into another origin's BroadcastChannel — victim tabs receive attacker-controlled messages, potentially triggering state changes (logout, navigation, data corruption)
- Bypass site isolation — Fission process boundaries do not protect BroadcastChannel because the parent mediates message routing using only the attacker-controlled origin key
Affected
Firefox Release, Beta, Nightly, ESR (all current). Verified on mozilla-firefox/firefox main HEAD 2026-04-23.
Suggested Fix
See attached diff. Use ThreadsafeContentParentHandle for synchronous remote-type validation before actor registration, or gate the Alloc on a validated principal.
URL
https://searchfox.org/mozilla-central/source/ipc/glue/BackgroundParentImpl.cpp#789
Updated•6 months ago
|
Updated•6 months ago
|
Updated•1 month ago
|
Description
•