CVE-2026-84332
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
IsJitDisabledForSiteTestchrome/browser/chrome_content_browser_client_unittest.cc |
modified | |
TEST_Fchrome/browser/chrome_content_browser_client_unittest.cc |
modified | |
ChromeContentBrowserClientPreferredColorSchemeAndroidTestchrome/browser/chrome_content_browser_client_unittest.cc |
modified |
Files Changed
chrome/browser/chrome_content_browser_client.ccchrome/browser/chrome_content_browser_client_unittest.cc
Patch
From ffb88da0146b5ba583e5285b1bd1fad24c7d2e34 Mon Sep 17 00:00:00 2001 From: Javier Castro <[email protected]> Date: Mon, 27 Jul 2026 14:37:23 -0700 Subject: [PATCH] Apply JAVASCRIPT_JIT setting to all web-safe site URLs ChromeContentBrowserClient::IsJitDisabledForSite() only consulted the JAVASCRIPT_JIT content setting for http/https site URLs. SiteInfo::Create() can however supply non-http(s) agent-cluster URLs (e.g. blob:null/<guid> for a sandboxed iframe that navigates to a Blob), so the early return left JIT enabled for the renderer hosting that frame regardless of the configured content setting. Match the existing AreV8OptimizationsEnabledForSite() and gate the early return on ChildProcessSecurityPolicy::IsWebSafeScheme() so that blob:, filesystem: and data: site URLs also honour the content setting, while WebUI and other non-web schemes still keep JIT enabled. Bug: 514489238 Internal CL: https://chrome-internal-review.googlesource.com/c/chrome/experimental/chromium/src/+/9599485 TAG=agy CONV=f98b2970-8cc8-47b6-8a92-3821abb33f5d Change-Id: I02f6fe0c57d442219026cb4e8ea58e34b5ce0be9 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8155983 Reviewed-by: Alex Moshchuk <[email protected]> Commit-Queue: Javier Castro <[email protected]> Cr-Commit-Position: refs/heads/main@{#1668994} --- diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 398e316a..a5579e31 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -8134,8 +8134,10 @@ nullptr) == CONTENT_SETTING_BLOCK; } - // Only disable JIT for web schemes. - if (!site_url.SchemeIsHTTPOrHTTPS()) { + // Only disable JIT for schemes that might actually load web content. This + // enables JIT for schemes such as chrome:// and chrome-untrusted://. + auto* policy = ChildProcessSecurityPolicy::GetInstance(); + if (!policy->IsWebSafeScheme(site_url.GetScheme())) { return false; } diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc index 727a0dd..80ff6f1e8 100644 --- a/chrome/browser/chrome_content_browser_client_unittest.cc +++ b/chrome/browser/chrome_content_browser_client_unittest.cc @@ -2154,6 +2154,44 @@ EXPECT_FALSE(IsOriginIsolatedByUser(url)); } +class IsJitDisabledForSiteTest : public ChromeContentBrowserClientTest { + protected: + bool IsJitDisabledForSite(const GURL& site_url) { + return browser_client_.IsJitDisabledForSite(&profile_, site_url); + } + + ChromeContentBrowserClient browser_client_; +}; + +TEST_F(IsJitDisabledForSiteTest, DefaultContentSettingAppliesToWebSafeSchemes) { + auto* map = HostContentSettingsMapFactory::GetForProfile(&profile_); + map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_JIT, + ContentSetting::CONTENT_SETTING_BLOCK); + + EXPECT_TRUE(IsJitDisabledForSite(GURL())); + EXPECT_TRUE(IsJitDisabledForSite(GURL("http://example.test"))); + EXPECT_TRUE(IsJitDisabledForSite(GURL("https://example.test"))); + + // The default content setting also covers web-safe schemes other than + // http(s), since those can host web-controlled script as well. + EXPECT_TRUE(IsJitDisabledForSite(GURL("blob:https://example.test/guid"))); + EXPECT_TRUE(IsJitDisabledForSite(GURL("blob:null/guid"))); + EXPECT_TRUE(IsJitDisabledForSite(GURL("filesystem:http://example.test/f"))); + EXPECT_TRUE(IsJitDisabledForSite(GURL("data:text/html,hello"))); + + // Schemes that are not web safe, such as WebUI schemes, are unaffected. + EXPECT_FALSE(IsJitDisabledForSite(GURL("chrome://settings"))); + EXPECT_FALSE(IsJitDisabledForSite(GURL("chrome-untrusted://foo"))); + EXPECT_FALSE(IsJitDisabledForSite(GURL("file:///tmp/foo.html"))); +} + +TEST_F(IsJitDisabledForSiteTest, AllowedByDefault) { + EXPECT_FALSE(IsJitDisabledForSite(GURL())); + EXPECT_FALSE(IsJitDisabledForSite(GURL("https://example.test"))); + EXPECT_FALSE(IsJitDisabledForSite(GURL("blob:null/guid"))); + EXPECT_FALSE(IsJitDisabledForSite(GURL("chrome://settings"))); +} + #if BUILDFLAG(IS_ANDROID) class ChromeContentBrowserClientPreferredColorSchemeAndroidTest
Regression Test / PoC
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index 727a0dd..80ff6f1e8 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -2154,6 +2154,44 @@
EXPECT_FALSE(IsOriginIsolatedByUser(url));
}
+class IsJitDisabledForSiteTest : public ChromeContentBrowserClientTest {
+ protected:
+ bool IsJitDisabledForSite(const GURL& site_url) {
+ return browser_client_.IsJitDisabledForSite(&profile_, site_url);
+ }
+
+ ChromeContentBrowserClient browser_client_;
+};
+
+TEST_F(IsJitDisabledForSiteTest, DefaultContentSettingAppliesToWebSafeSchemes) {
+ auto* map = HostContentSettingsMapFactory::GetForProfile(&profile_);
+ map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_JIT,
+ ContentSetting::CONTENT_SETTING_BLOCK);
+
+ EXPECT_TRUE(IsJitDisabledForSite(GURL()));
+ EXPECT_TRUE(IsJitDisabledForSite(GURL("http://example.test")));
+ EXPECT_TRUE(IsJitDisabledForSite(GURL("https://example.test")));
+
+ // The default content setting also covers web-safe schemes other than
+ // http(s), since those can host web-controlled script as well.
+ EXPECT_TRUE(IsJitDisabledForSite(GURL("blob:https://example.test/guid")));
+ EXPECT_TRUE(IsJitDisabledForSite(GURL("blob:null/guid")));
+ EXPECT_TRUE(IsJitDisabledForSite(GURL("filesystem:http://example.test/f")));
+ EXPECT_TRUE(IsJitDisabledForSite(GURL("data:text/html,hello")));
+
+ // Schemes that are not web safe, such as WebUI schemes, are unaffected.
+ EXPECT_FALSE(IsJitDisabledForSite(GURL("chrome://settings")));
+ EXPECT_FALSE(IsJitDisabledForSite(GURL("chrome-untrusted://foo")));
+ EXPECT_FALSE(IsJitDisabledForSite(GURL("file:///tmp/foo.html")));
+}
+
+TEST_F(IsJitDisabledForSiteTest, AllowedByDefault) {
+ EXPECT_FALSE(IsJitDisabledForSite(GURL()));
+ EXPECT_FALSE(IsJitDisabledForSite(GURL("https://example.test")));
+ EXPECT_FALSE(IsJitDisabledForSite(GURL("blob:null/guid")));
+ EXPECT_FALSE(IsJitDisabledForSite(GURL("chrome://settings")));
+}
+
#if BUILDFLAG(IS_ANDROID)
class ChromeContentBrowserClientPreferredColorSchemeAndroidTest
Original Bug Report
Bypass of JITless policy via non-HTTP/HTTPS schemes (e.g., blob: URLs)
Flapjack, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A logic flaw in ChromeContentBrowserClient::IsJitDisabledForSite potentially allows web content to bypass the JavaScript JIT content setting. By executing code within a sandboxed frame using a blob: URL, an attacker can force the browser to enable JIT optimizations in that process, circumventing defense-in-depth policies.
Affected files:
chrome/browser/chrome_content_browser_client.cc
Estimated timestamp from git blame: 2021-06-16
Summary
The ChromeContentBrowserClient::IsJitDisabledForSite function contains a logic flaw that potentially allows sites to bypass the “JavaScript JIT” content setting restriction. This setting is intended as a defense-in-depth mitigation to prevent JIT-related vulnerabilities from being exploited. Because the function explicitly enables JIT for any URL that does not strictly use the HTTP or HTTPS schemes, it fails to enforce the policy for other web-safe schemes like blob:, data:, and filesystem:.
Vulnerability Details
In chrome/browser/chrome_content_browser_client.cc, the IsJitDisabledForSite function evaluates whether JIT should be disabled for a given site_url. The implementation has the following check:
// Only disable JIT for web schemes.
if (!site_url.SchemeIsHTTPOrHTTPS()) {
return false;
}
The check !site_url.SchemeIsHTTPOrHTTPS() returns false (meaning JIT is enabled) for any URL that is not http: or https:.
When a site creates a sandboxed iframe and navigates it to an opaque-origin blob: URL, content::SiteInfo::Create determines that the frame requires a dedicated process (RequiresDedicatedProcessInternal returns true because the frame is sandboxed). As a result, the agent_cluster_url_or_default passed to IsJitDisabledForSite resolves to the literal blob: URL (e.g., blob:https://example.com/guid).
Since GURL::SchemeIsHTTPOrHTTPS() returns false for blob schemes, the function immediately returns false, enabling JIT for that execution context regardless of global user settings or enterprise policies blocking JIT. This behavior is inconsistent with AreV8OptimizationsEnabledForSite in the same file, which correctly uses ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(site_url.GetScheme()) to restrict optimizations across all relevant web schemes.
Potential Attacker Steps
Note: These are suggested/potential steps based on static analysis. Our tooling agent does not have the ability to run code to confirm a working Proof of Concept.
- The victim’s browser is configured to disable JavaScript JIT globally via the
JAVASCRIPT_JITcontent setting or enterprise policy. - An attacker hosts a malicious website that creates a Blob containing a V8 JIT exploit payload and calls
URL.createObjectURL(blob). - The attacker dynamically creates an iframe with the attribute
sandbox="allow-scripts"to force an opaque origin and dedicated process allocation, setting itssrcto the generatedblob:URL. - When the browser allocates the new renderer process for the sandboxed iframe,
IsJitDisabledForSiteevaluates theblob:URL, hits the early return, and enables JIT. - The attacker’s payload executes with JIT optimizations enabled, bypassing the policy and allowing the underlying V8 JIT vulnerability to be exploited.
Suggested Fix
Update ChromeContentBrowserClient::IsJitDisabledForSite to use a more inclusive check for web-safe schemes. Instead of restricting the check to SchemeIsHTTPOrHTTPS(), use ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(site_url.GetScheme()), similar to the approach taken in AreV8OptimizationsEnabledForSite. This ensures that JIT policies are accurately enforced for all schemes that can load and execute web content.
Evaluated with Chrome root at commit: b7d0c4d810da1b31400f198c70d9720fc8f0e5a0
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.