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 <arichiv@chromium.org>
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 <dom@chromium.org>
Reviewed-by: Igor Ruvinov <igorruvinov@chromium.org>
Auto-Submit: Ari Chivukula <arichiv@chromium.org>
Reviewed-by: mmenke <mmenke@chromium.org>
Reviewed-by: Dominic Farolino <dom@chromium.org>
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
+- arichiv@chromium.org
+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