Bug 1650089 - Part 1: Add a remoteTypeOverride option for about:blank loads triggered by chrome, r=annyG,kmag

After the changes in this bug, about:blank loads triggered by chrome will
finish in a "web" content process, as they have an untrusted null principal
without a precursor. In a few places throughout the codebase, however, we
perform about:blank loads with the explicit expectation that they do not change
processes. This new remoteTypeOverride option allows the intended final process
to be explicitly specified in this situation.

For security & simplicity reasons, this new attribute is limited to only be
usable on system-principal triggered loads of about:blank in toplevel browsing
contexts.

Differential Revision: https://phabricator.services.mozilla.com/D120671
This commit is contained in:
Nika Layzell 2021-08-03 15:39:33 +00:00
parent 4650289452
commit 78129583eb
11 changed files with 84 additions and 3 deletions

View File

@ -1482,8 +1482,14 @@ function _loadURI(browser, uri, params = {}) {
uri = "about:blank";
}
let { triggeringPrincipal, referrerInfo, postData, userContextId, csp } =
params || {};
let {
triggeringPrincipal,
referrerInfo,
postData,
userContextId,
csp,
remoteTypeOverride,
} = params || {};
let loadFlags =
params.loadFlags || params.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
let hasValidUserGestureActivation =
@ -1528,6 +1534,7 @@ function _loadURI(browser, uri, params = {}) {
referrerInfo,
postData,
hasValidUserGestureActivation,
remoteTypeOverride,
};
try {
browser.webNavigation.loadURI(uri, loadURIOptions);

View File

@ -218,6 +218,10 @@ ContentRestoreInternal.prototype = {
let loadURIOptions = {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
// Specify an override to force the load to finish in the current
// process, as tests rely on this behaviour for non-fission session
// restore.
remoteTypeOverride: Services.appinfo.remoteType,
};
webNavigation.loadURI("about:blank", loadURIOptions);
}

View File

@ -3657,6 +3657,7 @@ var SessionStoreInternal = {
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({
userContextId: aTab.userContextId,
}),
remoteTypeOverride: E10SUtils.NOT_REMOTE,
});
let data = TabState.collect(aTab, TAB_CUSTOM_VALUES.get(aTab));

View File

@ -88,6 +88,7 @@ nsDocShellLoadState::nsDocShellLoadState(
aLoadState.loadingSessionHistoryInfo().ref());
}
mUnstrippedURI = aLoadState.UnstrippedURI();
mRemoteTypeOverride = aLoadState.RemoteTypeOverride();
}
nsDocShellLoadState::nsDocShellLoadState(const nsDocShellLoadState& aOther)
@ -133,7 +134,8 @@ nsDocShellLoadState::nsDocShellLoadState(const nsDocShellLoadState& aOther)
mLoadIdentifier(aOther.mLoadIdentifier),
mChannelInitialized(aOther.mChannelInitialized),
mIsMetaRefresh(aOther.mIsMetaRefresh),
mUnstrippedURI(aOther.mUnstrippedURI) {
mUnstrippedURI(aOther.mUnstrippedURI),
mRemoteTypeOverride(aOther.mRemoteTypeOverride) {
if (aOther.mLoadingSessionHistoryInfo) {
mLoadingSessionHistoryInfo = MakeUnique<LoadingSessionHistoryInfo>(
*aOther.mLoadingSessionHistoryInfo);
@ -369,6 +371,11 @@ nsresult nsDocShellLoadState::CreateFromLoadURIOptions(
nsDocShell::MaybeNotifyKeywordSearchLoading(searchProvider, keyword);
}
if (aLoadURIOptions.mRemoteTypeOverride.WasPassed()) {
loadState->SetRemoteTypeOverride(
aLoadURIOptions.mRemoteTypeOverride.Value());
}
loadState.forget(aResult);
return NS_OK;
}
@ -1046,6 +1053,7 @@ DocShellLoadStateInit nsDocShellLoadState::Serialize() {
loadState.loadingSessionHistoryInfo().emplace(*mLoadingSessionHistoryInfo);
}
loadState.UnstrippedURI() = mUnstrippedURI;
loadState.RemoteTypeOverride() = mRemoteTypeOverride;
return loadState;
}

View File

@ -304,6 +304,14 @@ class nsDocShellLoadState final {
bool IsMetaRefresh() const { return mIsMetaRefresh; }
const mozilla::Maybe<nsCString>& GetRemoteTypeOverride() const {
return mRemoteTypeOverride;
}
void SetRemoteTypeOverride(const nsCString& aRemoteTypeOverride) {
mRemoteTypeOverride = mozilla::Some(aRemoteTypeOverride);
}
// When loading a document through nsDocShell::LoadURI(), a special set of
// flags needs to be set based on other values in nsDocShellLoadState. This
// function calculates those flags, before the LoadState is passed to
@ -524,6 +532,9 @@ class nsDocShellLoadState final {
// The original URI before query stripping happened. If it's present, it shows
// the query stripping happened. Otherwise, it will be a nullptr.
nsCOMPtr<nsIURI> mUnstrippedURI;
// If set, the remote type which the load should be completed within.
mozilla::Maybe<nsCString> mRemoteTypeOverride;
};
#endif /* nsDocShellLoadState_h__ */

View File

@ -719,6 +719,16 @@ nsresult nsFrameLoader::ReallyStartLoadingInternal() {
loadState->SetLoadFlags(flags);
loadState->SetFirstParty(false);
// If we're loading the default about:blank document in a <browser> element,
// prevent the load from causing a process switch by explicitly overriding
// remote type selection.
if (mPendingBrowsingContext->IsTopContent() &&
mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
NS_IsAboutBlank(mURIToLoad) &&
loadState->TriggeringPrincipal()->IsSystemPrincipal()) {
loadState->SetRemoteTypeOverride(mRemoteType);
}
}
if (IsRemoteFrame()) {

View File

@ -303,6 +303,8 @@ struct DocShellLoadStateInit
bool IsMetaRefresh;
nsIURI UnstrippedURI;
nsCString? RemoteTypeOverride;
};
struct TimedChannelInfo

View File

@ -79,4 +79,14 @@ dictionary LoadURIOptions {
* when initiating the load.
*/
long cancelContentJSEpoch = 0;
/**
* If this is passed, it will control which remote type is used to finish this
* load. Ignored for non-`about:` loads.
*
* NOTE: This is _NOT_ defaulted to `null`, as `null` is the value for
* `NOT_REMOTE_TYPE`, and we need to determine the difference between no
* `remoteTypeOverride` and a `remoteTypeOverride` of `NOT_REMOTE_TYPE`.
*/
UTF8String? remoteTypeOverride;
};

View File

@ -538,6 +538,21 @@ auto DocumentLoadListener::Open(nsDocShellLoadState* aLoadState,
}
}
if (aLoadState->GetRemoteTypeOverride()) {
if (!mIsDocumentLoad || !NS_IsAboutBlank(aLoadState->URI()) ||
!loadingContext->IsTopContent()) {
LOG(
("DocumentLoadListener::Open with invalid remoteTypeOverride "
"[this=%p]",
this));
*aRv = NS_ERROR_DOM_SECURITY_ERR;
mParentChannelListener = nullptr;
return nullptr;
}
mRemoteTypeOverride = aLoadState->GetRemoteTypeOverride();
}
if (!nsDocShell::CreateAndConfigureRealChannelForLoadState(
loadingContext, aLoadState, aLoadInfo, mParentChannelListener,
nullptr, attrs, aLoadFlags, aCacheKey, *aRv,
@ -1566,6 +1581,11 @@ bool DocumentLoadListener::MaybeTriggerProcessSwitch(
nsAutoCString preferredRemoteType(currentRemoteType);
RemotenessChangeOptions options;
// If there is a remote type override, default to it.
if (mRemoteTypeOverride) {
preferredRemoteType = *mRemoteTypeOverride;
}
// Update the preferred final process for our load based on the
// Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers.
{
@ -2512,6 +2532,10 @@ DocumentLoadListener::AsyncOnChannelRedirect(
// the new URI and set these again.
mIParentChannelFunctions.Clear();
// If we had a remote type override, ensure it's been cleared after a
// redirect, as it can't apply anymore.
mRemoteTypeOverride.reset();
#ifdef ANDROID
nsCOMPtr<nsIURI> uriBeingLoaded =
AntiTrackingUtils::MaybeGetDocumentURIBeingLoaded(mChannel);

View File

@ -544,6 +544,8 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
bool mSupportsRedirectToRealChannel = true;
Maybe<nsCString> mRemoteTypeOverride;
// The process id of the content process that we are being called from
// or 0 initiated from a parent process load.
base::ProcessId mOtherPid = 0;

View File

@ -814,6 +814,7 @@
postData,
headers,
csp,
remoteTypeOverride,
} = aParams;
let loadFlags =
aParams.loadFlags ||
@ -826,6 +827,7 @@
loadFlags,
postData,
headers,
remoteTypeOverride,
};
this._wrapURIChangeCall(() =>
this.webNavigation.loadURI(aURI, loadURIOptions)