CVE-2026-12297
Overview
Background
- `nsStandardURL`
- Gecko’s canonical URL implementation that stores a single spec string plus integer offset/length segments (
mAuthority,mPath,mQuery,mRef) describing each URL component. - `URLSegment`
- a
{mPos, mLen}pair (position and length) that indexes into the URL spec buffer to locate one component without copying it. - `Deserialize`
- the method that rebuilds an
nsStandardURLfromURIParamsreceived over IPC, typically when a child content process hands a URL to the privileged parent process. - `BaseURIMutator` / `Finalize`
- the URI-mutator base and its
Finalizestep, which transfers the builtmURIout to the caller viaforget.
Root Cause Analysis
nsStandardURL::Deserialize reconstructs the URL’s URLSegment offsets directly from attacker-influenceable URIParams and validated only that mQuery and mRef were sub-segments of mPath, without confirming that the path segment actually begins where the authority segment ends. This left the invariant that the path must immediately follow the authority (mPath.mPos == mAuthority.mPos + mAuthority.mLen) unenforced, so a crafted deserialization could produce inconsistent, overlapping, or gapped segment boundaries relative to the spec buffer. Subsequent code that indexes the spec string using these trusted offsets could then read the wrong region, giving an out-of-bounds or logically incorrect view of the URL in the parent process.
The fix adds an explicit NS_ENSURE_TRUE check that, whenever both mAuthority and mPath have non-negative lengths, the path’s start equals the authority’s end, rejecting the malformed URIParams before the object is accepted. The companion Finalize change guards against a null mURI being forgotten out, closing a null-pointer path in the mutator flow.
URLSegment boundaries without verifying that the authority and path segments are contiguous; the fix reinstates that contiguity invariant with an explicit boundary check during Deserialize.Attack Path
- Compromised child process
An attacker who already controls a sandboxed content process crafts a malicious
URIParamsstructure with inconsistentmAuthorityandmPathsegment offsets. - IPC delivery
The crafted params are sent to the more-privileged parent process, which calls
nsStandardURL::Deserializeto rebuild the URL. - Missing invariant check The old validation passes because it never required the path to begin at the end of the authority, so the malformed segments are accepted.
- Boundary violation
Later parent-process code indexes the spec buffer using the corrupt
mPath/mAuthorityoffsets, accessing memory outside or across the intended component bounds. - Sandbox escape The resulting out-of-bounds or logic corruption in the parent process is leveraged to break out of the content sandbox.
Impact Assessment
URIParams over IPC; the flaw is reached through normal URL deserialization, so no user interaction beyond the existing compromise is required.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifnetwerk/base/nsStandardURL.cpp |
modified | |
ifnetwerk/base/nsStandardURL.h |
modified |
Files Changed
netwerk/base/nsStandardURL.cppnetwerk/base/nsStandardURL.h
Audit Directions
- IPC-deserialized offsetsReview every
Deserialize/FromIPCpath that reconstructs objects from{position, length}segments and confirm each pair is validated for contiguity, ordering, and containment against the backing buffer, not just pairwise sub-segment relationships. - Partial segment validationFlag validation blocks that check some component relationships (e.g. query/ref within path) but omit others (authority-to-path), since an incomplete invariant set leaves exploitable gaps.
- Mutator finalize pathsAudit
Finalize/forgetimplementations across URI mutators for missing null checks on the member being transferred out.
Patch
diff --git a/netwerk/base/nsStandardURL.cpp b/netwerk/base/nsStandardURL.cpp
index 23bcb9d713e..f1ce9e203c7 100644
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -3730,6 +3730,10 @@ bool nsStandardURL::Deserialize(const URIParams& aParams) {
NS_ENSURE_TRUE(isSubSegment(mQuery, mPath), false);
NS_ENSURE_TRUE(isSubSegment(mRef, mPath), false);
+ if (mAuthority.mLen >= 0 && mPath.mLen >= 0) {
+ NS_ENSURE_TRUE(mPath.mPos == mAuthority.mPos + mAuthority.mLen, false);
+ }
+
if (!IsValid()) {
return false;
}
diff --git a/netwerk/base/nsStandardURL.h b/netwerk/base/nsStandardURL.h
index 3049cf6dd6f..79760da0268 100644
--- a/netwerk/base/nsStandardURL.h
+++ b/netwerk/base/nsStandardURL.h
@@ -412,6 +412,9 @@ class nsStandardURL : public nsIFileURL,
}
[[nodiscard]] NS_IMETHOD Finalize(nsIURI** aURI) override {
+ if (!BaseURIMutator<T>::mURI) {
+ return NS_ERROR_NULL_POINTER;
+ }
BaseURIMutator<T>::mURI.forget(aURI);
return NS_OK;
}