Chrome · Network
CVE-2026-17907
Logic Error in Network
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
NetworkContextConfigurationManagedProxySettingsBrowserTestchrome/browser/net/network_context_configuration_browsertest.cc |
modified | |
ifchrome/browser/net/system_network_context_manager.cc |
modified |
Files Changed
chrome/browser/net/network_context_configuration_browsertest.ccchrome/browser/net/system_network_context_manager.ccchrome/browser/policy/configuration_policy_handler_list_factory.ccchrome/common/pref_names.hcomponents/policy/resources/templates/policies.yamlcomponents/policy/resources/templates/policy_definitions/Miscellaneous/AllowSocketPoolSizeRandomizationForProxies.yamlcomponents/policy/resources/templates/policy_definitions/Miscellaneous/MaxConnectionsPerProxy.yamlcomponents/policy/resources/templates/policy_definitions/Miscellaneous/MaxConnectionsPerProxyForWebSocket.yamlcomponents/policy/resources/templates/policy_definitions/Miscellaneous/policy_atomic_groups.yaml
Patch
From e904ef795270f669226db7cb5522d2973dceeb7c Mon Sep 17 00:00:00 2001 From: Ari Chivukula <[email protected]> Date: Tue, 05 May 2026 15:28:17 -0700 Subject: [PATCH] [TCP] Add policy to gate proxy pool randomization This allows enterprises to gate randomization of size for proxy pools specifically. I don't see a reason to have a per-pool-type setting. For context see: https://xsleaks.dev/docs/defenses/secure-defaults/randomized-capacity/ Bug: 497837927 Change-Id: I2c45705ec42e25cb510cc1205faa85c7961ebd64 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7816739 Commit-Queue: Dominic Farolino <[email protected]> Reviewed-by: Igor Ruvinov <[email protected]> Auto-Submit: Ari Chivukula <[email protected]> Reviewed-by: mmenke <[email protected]> Reviewed-by: Dominic Farolino <[email protected]> Cr-Commit-Position: refs/heads/main@{#1625761} --- diff --git a/chrome/browser/net/network_context_configuration_browsertest.cc b/chrome/browser/net/network_context_configuration_browsertest.cc index 2bb49c3..33ecdd2 100644 --- a/chrome/browser/net/network_context_configuration_browsertest.cc +++ b/chrome/browser/net/network_context_configuration_browsertest.cc @@ -2097,7 +2097,12 @@ public: const size_t kTestMaxConnectionsPerProxy = 16; - NetworkContextConfigurationManagedProxySettingsBrowserTest() = default; + NetworkContextConfigurationManagedProxySettingsBrowserTest() { + // The test still works as this is overridden by the policy + // kPermitSocketPoolSizeRandomizationForProxies below. + scoped_feature_list_.InitAndEnableFeature( + net::features::kTcpSocketPoolLimitRandomization); + } NetworkContextConfigurationManagedProxySettingsBrowserTest( const NetworkContextConfigurationManagedProxySettingsBrowserTest&) = @@ -2123,6 +2128,10 @@ policy::POLICY_SOURCE_CLOUD, base::Value(static_cast<int>(kTestMaxConnectionsPerProxy)), /*external_data_fetcher=*/nullptr); + policies.Set(policy::key::kAllowSocketPoolSizeRandomizationForProxies, + policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE, + policy::POLICY_SOURCE_CLOUD, base::Value(false), + /*external_data_fetcher=*/nullptr); UpdateChromePolicy(policies); } @@ -2133,6 +2142,9 @@ size_t GetExpectedMaxConnectionsPerProxyForWebSocket() const override { return kTestMaxConnectionsPerProxy; } + + private: + base::test::ScopedFeatureList scoped_feature_list_; }; IN_PROC_BROWSER_TEST_P( diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc index 089a007..c91cce0 100644 --- a/chrome/browser/net/system_network_context_manager.cc +++ b/chrome/browser/net/system_network_context_manager.cc @@ -760,6 +760,9 @@ registry->RegisterIntegerPref(prefs::kMaxConnectionsPerProxyForWebSocket, -1); + registry->RegisterBooleanPref( + prefs::kAllowSocketPoolSizeRandomizationForProxies, true); + registry->RegisterListPref(prefs::kExplicitlyAllowedNetworkPorts); #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) @@ -852,10 +855,10 @@ ? std::optional<uint32_t>( base::saturated_cast<uint32_t>(max_connections_websocket)) : std::nullopt; - if (max_connections_normal_clamp || max_connections_websocket_clamp) { - network_service->SetMaxConnectionsPerProxyChain( - max_connections_normal_clamp, max_connections_websocket_clamp); - } + network_service->SetMaxConnectionsPerProxyChain( + max_connections_normal_clamp, max_connections_websocket_clamp, + local_state_->GetBoolean( + prefs::kAllowSocketPoolSizeRandomizationForProxies)); network_service_network_context_.reset(); content::CreateNetworkContextInNetworkService( diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index ae5dbd5..772de728 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc @@ -486,6 +486,9 @@ { key::kMaxConnectionsPerProxyForWebSocket, prefs::kMaxConnectionsPerProxyForWebSocket, base::Value::Type::INTEGER }, + { key::kAllowSocketPoolSizeRandomizationForProxies, + prefs::kAllowSocketPoolSizeRandomizationForProxies, + base::Value::Type::BOOLEAN }, // Policies for all platforms - End #if BUILDFLAG(IS_ANDROID) { key::kAccessibilityPerformanceFilteringAllowed, diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index f815914..ed07378 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -2490,6 +2490,8 @@ "net.max_connections_per_proxy"; inline constexpr char kMaxConnectionsPerProxyForWebSocket[] = "net.max_connections_per_proxy_for_websocket"; +inline constexpr char kAllowSocketPoolSizeRandomizationForProxies[] = + "net.allow_socket_pool_size_randomization_for_proxies"; #if BUILDFLAG(IS_MAC) // A boolean that tracks whether to show a notification when trying to quit diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml index 2d0c498d..e9a95403 100644 --- a/components/policy/resources/templates/policies.yaml +++ b/components/policy/resources/templates/policies.yaml @@ -1445,6 +1445,7 @@ 1444: DataUrlInWebWorkerOpaqueOriginEnabled 1445: KioskPinchToZoomAllowed 1446: SecuritySignalsClientCertificatesSelectors + 1447: AllowSocketPoolSizeRandomizationForProxies atomic_groups: 1: Homepage diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/AllowSocketPoolSizeRandomizationForProxies.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/AllowSocketPoolSizeRandomizationForProxies.yaml new file mode 100644 index 0000000..47fef96 --- /dev/null +++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/AllowSocketPoolSizeRandomizationForProxies.yaml @@ -0,0 +1,31 @@ +caption: Allow socket pool size randomization for proxies +default: true +desc: |- + Socket pool size randomization is a security mechanism that prevents attackers from exploiting deterministic capacity limits to learn cross-site information. + If the capacity for a pool is normally 128 sockets, this mechanism randomly caps the pool between 128 and 256. + This can allow up to 2x as many connections to the proxy, but in practice the expected value is more like 1.2x. + + This impacts the settings from <ph name="MAX_CONNECTIONS_PER_PROXY_POLICY_NAME">MaxConnectionsPerProxy</ph> and <ph name="MAX_CONNECTIONS_PER_PROXY_FOR_WEBSOCKET_POLICY_NAME">MaxConnectionsPerProxyForWebSocket</ph>. + Instead of them defining the upper limit, the upper limit is 2x their values (though again, the expected value in practice is more like 1.2x them). + + This is enabled by default for all pools, but this policy allows the feature to be disabled for proxy pools specifically. +example_value: true +features: + dynamic_refresh: false + per_profile: false +items: +- caption: Allow socket pool randomization for proxies + value: true +- caption: Prevent socket pool randomization for proxies + value: false +owners: +- file://components/policy/OWNERS +- [email protected] +schema: + type: boolean +supported_on: +- android:150- +- chrome.*:150- +- chrome_os:150- +tags: [] +type: main diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/MaxConnectionsPerProxy.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/MaxConnectionsPerProxy.yaml index 7a5d4af..5175327 100644 --- a/components/policy/resources/templates/policy_definitions/Miscellaneous/MaxConnectionsPerProxy.yaml +++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/MaxConnectionsPerProxy.yaml @@ -17,6 +17,8 @@ The value should be equal to or lower than 256 (99 in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> 147 and earlier). Setting a value above that limit will cause 256 (99 in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> 147 and earlier) to be used. Raise above the default (128) at your own risk. + + Please note that the enforced limits are impacted by <ph name="ALLOW_SOCKET_POOL_SIZE_RANDOMIZATION_FOR_PROXIES_POLICY_NAME">AllowSocketPoolSizeRandomizationForProxies</ph>. example_value: 128 features: dynamic_refresh: false diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/MaxConnectionsPerProxyForWebSocket.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/MaxConnectionsPerProxyForWebSocket.yaml index 784926d9..8365cd5 100644 --- a/components/policy/resources/templates/policy_definitions/Miscellaneous/MaxConnectionsPerProxyForWebSocket.yaml +++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/MaxConnectionsPerProxyForWebSocket.yaml @@ -17,6 +17,8 @@ The value should be equal to or lower than 256. Setting a value above that limit will cause 256 to be used. Raise above the default (128) at your own risk. + + Please note that the enforced limits are impacted by <ph name="ALLOW_SOCKET_POOL_SIZE_RANDOMIZATION_FOR_PROXIES_POLICY_NAME">AllowSocketPoolSizeRandomizationForProxies</ph>. example_value: 128 features: dynamic_refresh: false diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/policy_atomic_groups.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/policy_atomic_groups.yaml index fb41ff4..10190d7 100644 --- a/components/policy/resources/templates/policy_definitions/Miscellaneous/policy_atomic_groups.yaml +++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/policy_atomic_groups.yaml @@ -3,3 +3,4 @@ policies: - MaxConnectionsPerProxy - MaxConnectionsPerProxyForWebSocket
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/net/network_context_configuration_browsertest.cc b/chrome/browser/net/network_context_configuration_browsertest.cc
index 2bb49c3..33ecdd2 100644
--- a/chrome/browser/net/network_context_configuration_browsertest.cc
+++ b/chrome/browser/net/network_context_configuration_browsertest.cc
@@ -2097,7 +2097,12 @@
public:
const size_t kTestMaxConnectionsPerProxy = 16;
- NetworkContextConfigurationManagedProxySettingsBrowserTest() = default;
+ NetworkContextConfigurationManagedProxySettingsBrowserTest() {
+ // The test still works as this is overridden by the policy
+ // kPermitSocketPoolSizeRandomizationForProxies below.
+ scoped_feature_list_.InitAndEnableFeature(
+ net::features::kTcpSocketPoolLimitRandomization);
+ }
NetworkContextConfigurationManagedProxySettingsBrowserTest(
const NetworkContextConfigurationManagedProxySettingsBrowserTest&) =
@@ -2123,6 +2128,10 @@
policy::POLICY_SOURCE_CLOUD,
base::Value(static_cast<int>(kTestMaxConnectionsPerProxy)),
/*external_data_fetcher=*/nullptr);
+ policies.Set(policy::key::kAllowSocketPoolSizeRandomizationForProxies,
+ policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
+ policy::POLICY_SOURCE_CLOUD, base::Value(false),
+ /*external_data_fetcher=*/nullptr);
UpdateChromePolicy(policies);
}
@@ -2133,6 +2142,9 @@
size_t GetExpectedMaxConnectionsPerProxyForWebSocket() const override {
return kTestMaxConnectionsPerProxy;
}
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_P(
diff --git a/components/policy/test/data/pref_mapping/AllowSocketPoolSizeRandomizationForProxies.json b/components/policy/test/data/pref_mapping/AllowSocketPoolSizeRandomizationForProxies.json
new file mode 100644
index 0000000..8879fb8
--- /dev/null
+++ b/components/policy/test/data/pref_mapping/AllowSocketPoolSizeRandomizationForProxies.json
@@ -0,0 +1,20 @@
+[
+ {
+ "os": [
+ "chromeos",
+ "win",
+ "linux",
+ "mac",
+ "android"
+ ],
+ "simple_policy_pref_mapping_test": {
+ "default_value": true,
+ "pref_location": "local_state",
+ "pref_name": "net.allow_socket_pool_size_randomization_for_proxies",
+ "values_to_test": [
+ true,
+ false
+ ]
+ }
+ }
+]
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 4695b4db..5b1413cc 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -28434,39 +28434,50 @@
SocketPoolAdditionalCapacity real_poll_128 =
SocketPoolAdditionalCapacity::CreateForTest(
/*base=*/0.1, /*capacity=*/128, /*minimum=*/0.3, /*noise=*/0.4);
- for (bool proxy_pool_randomization : {true, false}) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitWithFeatureState(
- features::kTcpSocketPoolLimitRandomizationForProxy,
- proxy_pool_randomization);
- std::unique_ptr<HttpNetworkSession> session = CreateSession(&session_deps_);
- EXPECT_EQ(session
- ->GetSocketPool(HttpNetworkSession::SocketPoolType::kNormal,
- ProxyChain::Direct())
- ->AdditionalCapacityForTest(),
- real_poll_256);
- EXPECT_EQ(
- session
- ->GetSocketPool(HttpNetworkSession::SocketPoolType::kWebSocket,
- ProxyChain::Direct())
- ->AdditionalCapacityForTest(),
- real_poll_256);
- EXPECT_EQ(session
- ->GetSocketPool(
- HttpNetworkSession::SocketPoolType::kNormal,
- ProxyChain(ProxyServer::SCHEME_HTTPS,
- SameProxyWithDifferentSchemesProxyResolver::
- ProxyHostPortPair()))
- ->AdditionalCapacityForTest(),
- proxy_pool_randomization ? real_poll_128 : empty_pool);
- EXPECT_EQ(session
- ->GetSocketPool(
- HttpNetworkSession::SocketPoolType::kWebSocket,
- ProxyChain(ProxyServer::SCHEME_HTTPS,
- SameProxyWithDifferentSchemesProxyResolver::
- ProxyHostPortPair()))
- ->AdditionalCapacityForTest(),
- proxy_pool_randomization ? real_poll_128 : empty_pool);
+ for (bool allow_proxy_pool_randomization : {true, false}) {
+ ClientSocketPoolManager::set_allow_size_randomization_for_proxy(
+ allow_proxy_pool_randomization);
+ for (bool proxy_pool_randomization_feature : {true, false}) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitWithFeatureState(
+ features::kTcpSocketPoolLimitRandomizationForProxy,
+ proxy_pool_randomization_feature);
+ std::unique_ptr<HttpNetworkSession> session =
+ CreateSession(&session_deps_);
+ EXPECT_EQ(session
+ ->GetSocketPool(HttpNetworkSession::SocketPoolType::kNormal,
+ ProxyChain::Direct())
+ ->AdditionalCapacityForTest(),
+ real_poll_256);
+ EXPECT_EQ(
+ session
+ ->GetSocketPool(HttpNetworkSession::SocketPoolType::kWebSocket,
+ ProxyChain::Direct())
+ ->AdditionalCapacityForTest(),
+ real_poll_256);
+ EXPECT_EQ(
+ session
+ ->GetSocketPool(
+ HttpNetworkSession::SocketPoolType::kNormal,
+ ProxyChain(ProxyServer::SCHEME_HTTPS,
+ SameProxyWithDifferentSchemesProxyResolver::
+ ProxyHostPortPair()))
+ ->AdditionalCapacityForTest(),
+ allow_proxy_pool_randomization && proxy_pool_randomization_feature
+ ? real_poll_128
+ : empty_pool);
+ EXPECT_EQ(
+ session
+ ->GetSocketPool(
+ HttpNetworkSession::SocketPoolType::kWebSocket,
+ ProxyChain(ProxyServer::SCHEME_HTTPS,
+ SameProxyWithDifferentSchemesProxyResolver::
+ ProxyHostPortPair()))
+ ->AdditionalCapacityForTest(),
+ allow_proxy_pool_randomization && proxy_pool_randomization_feature
+ ? real_poll_128
+ : empty_pool);
+ }
}
}
diff --git a/services/network/network_service_unittest.cc b/services/network/network_service_unittest.cc
index f5454563..a915e189 100644
--- a/services/network/network_service_unittest.cc
+++ b/services/network/network_service_unittest.cc
@@ -1212,6 +1212,10 @@
net::HttpNetworkSession::SocketPoolType::kWebSocket);
}
+static bool GetGlobalAllowSizeRandomizationForProxy() {
+ return net::ClientSocketPoolManager::allow_size_randomization_for_proxy();
+}
+
// Tests that NetworkService::SetMaxConnectionsPerProxyChain() (1) modifies
// globals in net::ClientSocketPoolManager (2) saturates out of bound values.
TEST_F(NetworkServiceTest, SetMaxConnectionsPerProxyChain) {
@@ -1222,44 +1226,52 @@
// Starts off at default value.
EXPECT_EQ(kDefault, GetGlobalMaxConnectionsPerProxyChain());
EXPECT_EQ(kDefault, GetGlobalMaxConnectionsPerProxyChainForWebSocket());
+ EXPECT_EQ(true, GetGlobalAllowSizeRandomizationForProxy());
// Anything less than kMin saturates to kMin.
- service()->SetMaxConnectionsPerProxyChain(kMin - 1, kMin - 1);
+ service()->SetMaxConnectionsPerProxyChain(kMin - 1, kMin - 1, false);
EXPECT_EQ(kMin, GetGlobalMaxConnectionsPerProxyChain());
EXPECT_EQ(kMin, GetGlobalMaxConnectionsPerProxyChainForWebSocket());
+ EXPECT_EQ(false, GetGlobalAllowSizeRandomizationForProxy());
// Anything larger than kMax saturates to kMax
- service()->SetMaxConnectionsPerProxyChain(kMax + 1, kMax + 1);
+ service()->SetMaxConnectionsPerProxyChain(kMax + 1, kMax + 1, true);
EXPECT_EQ(kMax, GetGlobalMaxConnectionsPerProxyChain());
EXPECT_EQ(kMax, GetGlobalMaxConnectionsPerProxyChainForWebSocket());
+ EXPECT_EQ(true, GetGlobalAllowSizeRandomizationForProxy());
// Anything in between kMin and kMax should be set exactly.
- service()->SetMaxConnectionsPerProxyChain(58, 58);
+ service()->SetMaxConnectionsPerProxyChain(58, 58, false);
EXPECT_EQ(58u, GetGlobalMaxConnectionsPerProxyChain());
EXPECT_EQ(58u, GetGlobalMaxConnectionsPerProxyChainForWebSocket());
+ EXPECT_EQ(false, GetGlobalAllowSizeRandomizationForProxy());
// It's possible to update neither if that's you're thing.
- service()->SetMaxConnectionsPerProxyChain(std::nullopt, std::nullopt);
+ service()->SetMaxConnectionsPerProxyChain(std::nullopt, std::nullopt, true);
EXPECT_EQ(58u, GetGlobalMaxConnectionsPerProxyChain());
EXPECT_EQ(58u, GetGlobalMaxConnectionsPerProxyChainForWebSocket());
+ EXPECT_EQ(true, GetGlobalAllowSizeRandomizationForProxy());
// It's possible to update just one or the other.
- service()->SetMaxConnectionsPerProxyChain(56, std::nullopt);
+ service()->SetMaxConnectionsPerProxyChain(56, std::nullopt, false);
EXPECT_EQ(56u, GetGlobalMaxConnectionsPerProxyChain());
EXPECT_EQ(58u, GetGlobalMaxConnectionsPerProxyChainForWebSocket());
+ EXPECT_EQ(false, GetGlobalAllowSizeRandomizationForProxy());
// It's possible to update just one or the other.
- service()->SetMaxConnectionsPerProxyChain(std::nullopt, 60);
+ service()->SetMaxConnectionsPerProxyChain(std::nullopt, 60, true);
EXPECT_EQ(56u, GetGlobalMaxConnectionsPerProxyChain());
EXPECT_EQ(60u, GetGlobalMaxConnectionsPerProxyChainForWebSocket());
+ EXPECT_EQ(true, GetGlobalAllowSizeRandomizationForProxy());
// It's possible to update both to different values.
- service()->SetMaxConnectionsPerProxyChain(57, 59);
+ service()->SetMaxConnectionsPerProxyChain(57, 59, false);
EXPECT_EQ(57u, GetGlobalMaxConnectionsPerProxyChain());
EXPECT_EQ(59u, GetGlobalMaxConnectionsPerProxyChainForWebSocket());
+ EXPECT_EQ(false, GetGlobalAllowSizeRandomizationForProxy());
// Restore the default value to minize sideffects.
- service()->SetMaxConnectionsPerProxyChain(kDefault, kDefault);
+ service()->SetMaxConnectionsPerProxyChain(kDefault, kDefault, true);
}
#if BUILDFLAG(IS_CT_SUPPORTED)
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page