CVE-2026-18809
Overview
Background
- GeckoView
- the Android embedding layer that renders web content for Firefox for Android and Firefox Focus.
- `content://` scheme
- an Android URI scheme that exposes files and data shared through a
ContentProvider, resolved by GeckoView’s dedicated content channel. - `GeckoViewContentChannelParent`
- the parent-process (privileged) IPC actor that services channel requests originating from a child content process.
- `ipc::DeserializeURI`
- the helper that reconstructs an
nsIURIfrom data sent across the IPC boundary by a less-trusted child process.
Root Cause Analysis
GeckoViewContentChannelParent::Init reconstructs a URI from IPC arguments via ipc::DeserializeURI(aArgs.uri()) and then proceeds to open a channel for it in the privileged parent process, without ever verifying that the URI’s scheme is actually content. The invariant that a content channel actor must only ever operate on content:// URIs was never enforced, so a compromised or malicious child process could hand this parent-only actor a URI of any scheme (for example file://) and have the parent open it on the child’s behalf.
The fix adds an early guard, if (!uri || !uri->SchemeIs("content")), that rejects a null URI or any non-content scheme, sets rv = NS_ERROR_UNKNOWN_PROTOCOL, and returns false to fail actor initialization. This works because the scheme is now validated at the trust boundary — inside the parent process, on data received from the child — before any channel is created or resource is touched. The check closes the gap between what the actor is meant to handle and what an attacker can make it handle.
Attack Path
- Compromise a content process The attacker gains code execution or IPC-message control in a sandboxed child content process, e.g. via a malicious page.
- Forge a channel request
The child constructs the IPC
Initarguments with a URI whose scheme is notcontent(such asfile://), pointing at a target the sandbox should not reach. - Cross the trust boundary
The parent’s
GeckoViewContentChannelParent::Initdeserializes the attacker URI and, lacking a scheme check, accepts it as valid. - Open with parent privilege The privileged parent opens a channel for the arbitrary-scheme URI and returns its contents back over IPC to the child.
- Exfiltrate data The child reads back the disclosed resource, leaking information it could not otherwise access from within the sandbox.
Impact Assessment
content:// scheme and reading their contents, yielding information disclosure of local resources on Firefox for Android and Firefox Focus. The disclosure is realized in the parent process on behalf of the sandboxed child, effectively bypassing the child’s scheme/origin restrictions. The precondition is that the attacker already influences the child-to-parent IPC (a compromised or malicious content process), consistent with the classified LogicError and high impact.Files Changed
mobile/android/components/geckoview/GeckoViewContentChannelParent.cpp
Audit Directions
- IPC-deserialized URI trustAudit every parent-side actor that calls
ipc::DeserializeURI(or similar deserialization) and confirm the resultingnsIURIscheme/origin is validated before a channel is opened or a resource accessed. - Scheme-specific actorsFor any parent actor tied to one scheme (
content,file,blob, etc.), verify it enforcesSchemeIson child-supplied URIs rather than assuming the child sent a well-formed request. - Null-URI handlingCheck that
DeserializeURIresults are tested for null before dereference, since a crafted or failed deserialization can yield a nullnsIURIat the trust boundary.
Patch
diff --git a/mobile/android/components/geckoview/GeckoViewContentChannelParent.cpp b/mobile/android/components/geckoview/GeckoViewContentChannelParent.cpp
index 01d3225f2bc..b6a30d4f3c2 100644
--- a/mobile/android/components/geckoview/GeckoViewContentChannelParent.cpp
+++ b/mobile/android/components/geckoview/GeckoViewContentChannelParent.cpp
@@ -159,6 +159,11 @@ bool GeckoViewContentChannelParent::Init(
nsCOMPtr<nsIURI> uri = ipc::DeserializeURI(aArgs.uri());
+ if (!uri || !uri->SchemeIs("content")) {
+ rv = NS_ERROR_UNKNOWN_PROTOCOL;
+ return false;
+ }
+
nsAutoCString remoteType;
rv = GetRemoteType(remoteType);
if (MOZ_UNLIKELY(NS_FAILED(rv))) {