Firefox · Core
CVE-2026-84143
Memory Corruption in Core
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Files Changed
browser/components/tabbrowser/test/browser/tabs/browser_new_tab_in_privilegedabout_process_pref.jsdocshell/base/nsDocShellLoadState.cppdocshell/test/browser/browser_browsing_context_discarded.js
Patch
diff --git a/browser/components/tabbrowser/test/browser/tabs/browser_new_tab_in_privilegedabout_process_pref.js b/browser/components/tabbrowser/test/browser/tabs/browser_new_tab_in_privilegedabout_process_pref.js
index 75b83782e79..9e8b2f6a710 100644
--- a/browser/components/tabbrowser/test/browser/tabs/browser_new_tab_in_privilegedabout_process_pref.js
+++ b/browser/components/tabbrowser/test/browser/tabs/browser_new_tab_in_privilegedabout_process_pref.js
@@ -216,13 +216,6 @@ add_task(async function process_switching_through_navigation_features() {
BrowserTestUtils.startLoadingURIString(browser, TEST_HTTP);
await BrowserTestUtils.browserLoaded(browser, false, TEST_HTTP);
checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
-
- // Check that location change causes a change in process type as well.
- await SpecialPowers.spawn(browser, [ABOUT_NEWTAB], uri => {
- content.location = uri;
- });
- await BrowserTestUtils.browserLoaded(browser, false, ABOUT_NEWTAB);
- assertIsPrivilegedProcess(browser, "about:newtab after location change");
}
);
diff --git a/docshell/base/nsDocShellLoadState.cpp b/docshell/base/nsDocShellLoadState.cpp
index a82a99ee505..df093780f21 100644
--- a/docshell/base/nsDocShellLoadState.cpp
+++ b/docshell/base/nsDocShellLoadState.cpp
@@ -4,6 +4,7 @@
#include "nsDocShellLoadState.h"
#include "nsIDocShell.h"
+#include "nsContentSecurityUtils.h"
#include "nsDocShell.h"
#include "nsILoadInfo.h"
#include "nsIProtocolHandler.h"
@@ -15,6 +16,7 @@
#include "nsNetUtil.h"
#include "nsQueryObject.h"
#include "ReferrerInfo.h"
+#include "xpcpublic.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Components.h"
@@ -25,6 +27,7 @@
#include "mozilla/dom/LoadURIOptionsBinding.h"
#include "mozilla/dom/Navigation.h"
#include "mozilla/dom/NavigationUtils.h"
+#include "mozilla/dom/ProcessIsolation.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "mozilla/dom/nsHTTPSOnlyUtils.h"
#include "mozilla/net/DocumentLoadListener.h"
@@ -44,6 +47,66 @@ using namespace mozilla::dom;
// Global reference to the URI fixup service.
static mozilla::StaticRefPtr<nsIURIFixup> sURIFixup;
+static bool ContentTriggeredURILoadIsAllowed(
+ nsIURI* aURI, const nsACString& aEffectiveRemoteType) {
+ MOZ_ASSERT(aEffectiveRemoteType != NOT_REMOTE_TYPE);
+ MOZ_ASSERT(!aURI->SchemeIs("javascript"), "Should have been blocked already");
+
+ // view-source: URIs are not linkable from web content, but the "View Page
+ // Source" context menu has the content process itself load them,
+ // so decide based on the inner URI instead.
+ if (aURI->SchemeIs("view-source")) {
+ nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
+ MOZ_ASSERT(nestedURI);
+
+ nsCOMPtr<nsIURI> innerURI;
+ return NS_SUCCEEDED(nestedURI->GetInnerURI(getter_AddRefs(innerURI))) &&
+ ContentTriggeredURILoadIsAllowed(innerURI, aEffectiveRemoteType);
+ }
+
+ // A null principal is the least privileged principal there is, so any URI it
+ // is allowed to link to may be loaded from any content process.
+ nsCOMPtr<nsIPrincipal> genericNullPrincipal = NullPrincipal::Create({});
+
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+
+ if (NS_SUCCEEDED(secMan->CheckLoadURIWithPrincipal(
+ genericNullPrincipal, aURI,
+ nsIScriptSecurityManager::DISALLOW_SCRIPT |
+ nsIScriptSecurityManager::DONT_REPORT_ERRORS,
+ 0))) {
+ return true;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal =
+ BasePrincipal::CreateContentPrincipal(aURI, {});
+ if (principal->GetIsNullPrincipal()) {
+ // Only allow null principals from URIs that have the
+ // URI_LOADABLE_BY_SUBSUMERS (i.e. blob:) flag. Other null principals likely
+ // correspond to internal, unsafe-to-load in content, resources.
+ bool loadableBySubsumers = false;
+ if (NS_FAILED(NS_URIChainHasFlags(
+ aURI, nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
+ &loadableBySubsumers))) {
+ return false;
+ }
+ return loadableBySubsumers;
+ }
+
+ // Automation-Only: Allow loading of chrome://reftest/* URLs.
+ if (aURI->SchemeIs("chrome") && xpc::IsInAutomation()) {
+ nsAutoCString host;
+ if (NS_SUCCEEDED(aURI->GetHost(host)) && host.EqualsLiteral("reftest")) {
+ return true;
+ }
+ }
+
+ return ValidatePrincipalCouldPotentiallyBeLoadedBy(
+ principal, aEffectiveRemoteType,
+ {ValidatePrincipalOptions::AllowNotLoadedOrigin});
+}
+
nsDocShellLoadState::nsDocShellLoadState(nsIURI* aURI)
: nsDocShellLoadState(aURI, nsContentUtils::GenerateLoadIdentifier()) {}
@@ -174,10 +237,29 @@ nsDocShellLoadState::nsDocShellLoadState(
}
}
+ const nsCString& effectiveRemoteType = GetEffectiveTriggeringRemoteType();
+ if (effectiveRemoteType != NOT_REMOTE_TYPE &&
+ !ContentTriggeredURILoadIsAllowed(mURI, effectiveRemoteType)) {
+ nsAutoCString aboutModuleOrScheme;
+ if (mURI->SchemeIs("about")) {
+ (void)NS_GetAboutModuleName(mURI, aboutModuleOrScheme);
+ aboutModuleOrScheme.InsertLiteral("about:", 0);
+ } else {
+ mURI->GetScheme(aboutModuleOrScheme);
+ aboutModuleOrScheme.AppendLiteral(":");
+ }
+ nsCString remotePrefix(RemoteTypePrefix(effectiveRemoteType));
+ aActor->FatalError(
+ nsPrintfCString("Illegal load attempt of %s URL from %s",
+ aboutModuleOrScheme.get(), remotePrefix.get())
+ .get());
+ return;
+ }
+
// NOTE: Eventually this should probably be called on a LoadedOriginSet, but
// we don't track this on the load state yet.
if (!ValidatePrincipalCouldPotentiallyBeLoadedBy(
- mTriggeringPrincipal, GetEffectiveTriggeringRemoteType(),
+ mTriggeringPrincipal, effectiveRemoteType,
{ValidatePrincipalOptions::AllowExpanded,
ValidatePrincipalOptions::AlwaysAllowSystem,
ValidatePrincipalOptions::AllowNotLoadedOrigin})) {
@@ -186,7 +268,7 @@ nsDocShellLoadState::nsDocShellLoadState(
return;
}
if (!ValidatePrincipalCouldPotentiallyBeLoadedBy(
- mPrincipalToInherit, GetEffectiveTriggeringRemoteType(),
+ mPrincipalToInherit, effectiveRemoteType,
{ValidatePrincipalOptions::AllowNullPtr,
ValidatePrincipalOptions::AllowNotLoadedOrigin})) {
aActor->FatalError("nsDocShellLoadState with invalid principalToInherit");
diff --git a/docshell/test/browser/browser_browsing_context_discarded.js b/docshell/test/browser/browser_browsing_context_discarded.js
index 3dc5265a977..6f1b5be55b8 100644
--- a/docshell/test/browser/browser_browsing_context_discarded.js
+++ b/docshell/test/browser/browser_browsing_context_discarded.js
@@ -115,7 +115,7 @@ add_task(async function replaceToplevel() {
const discarded = await observeDiscarded([...expected.keys()], async () => {
await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
- content.location = "about:newtab";
+ content.location = "about:blocked";
});
});
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/browser/components/tabbrowser/test/browser/tabs/browser_new_tab_in_privilegedabout_process_pref.js b/browser/components/tabbrowser/test/browser/tabs/browser_new_tab_in_privilegedabout_process_pref.js
index 75b83782e79..9e8b2f6a710 100644
--- a/browser/components/tabbrowser/test/browser/tabs/browser_new_tab_in_privilegedabout_process_pref.js
+++ b/browser/components/tabbrowser/test/browser/tabs/browser_new_tab_in_privilegedabout_process_pref.js
@@ -216,13 +216,6 @@ add_task(async function process_switching_through_navigation_features() {
BrowserTestUtils.startLoadingURIString(browser, TEST_HTTP);
await BrowserTestUtils.browserLoaded(browser, false, TEST_HTTP);
checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
-
- // Check that location change causes a change in process type as well.
- await SpecialPowers.spawn(browser, [ABOUT_NEWTAB], uri => {
- content.location = uri;
- });
- await BrowserTestUtils.browserLoaded(browser, false, ABOUT_NEWTAB);
- assertIsPrivilegedProcess(browser, "about:newtab after location change");
}
);
diff --git a/docshell/test/browser/browser_browsing_context_discarded.js b/docshell/test/browser/browser_browsing_context_discarded.js
index 3dc5265a977..6f1b5be55b8 100644
--- a/docshell/test/browser/browser_browsing_context_discarded.js
+++ b/docshell/test/browser/browser_browsing_context_discarded.js
@@ -115,7 +115,7 @@ add_task(async function replaceToplevel() {
const discarded = await observeDiscarded([...expected.keys()], async () => {
await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
- content.location = "about:newtab";
+ content.location = "about:blocked";
});
});
Loading diff…
References
On This Page