site_isolation_policy.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/site_isolation/site_isolation_policy.h"
  5. #include "base/containers/contains.h"
  6. #include "base/feature_list.h"
  7. #include "base/json/values_util.h"
  8. #include "base/metrics/field_trial_params.h"
  9. #include "base/metrics/histogram_functions.h"
  10. #include "base/system/sys_info.h"
  11. #include "build/build_config.h"
  12. #include "components/prefs/pref_service.h"
  13. #include "components/prefs/scoped_user_pref_update.h"
  14. #include "components/site_isolation/features.h"
  15. #include "components/site_isolation/pref_names.h"
  16. #include "components/user_prefs/user_prefs.h"
  17. #include "content/public/browser/browser_context.h"
  18. #include "content/public/browser/site_instance.h"
  19. #include "content/public/browser/site_isolation_mode.h"
  20. #include "content/public/browser/site_isolation_policy.h"
  21. #include "content/public/common/content_features.h"
  22. namespace site_isolation {
  23. namespace {
  24. using IsolatedOriginSource =
  25. content::ChildProcessSecurityPolicy::IsolatedOriginSource;
  26. bool g_disallow_memory_threshold_caching = false;
  27. bool ShouldCacheMemoryThresholdDecision() {
  28. return base::FeatureList::IsEnabled(
  29. features::kCacheSiteIsolationMemoryThreshold);
  30. }
  31. struct IsolationDisableDecisions {
  32. bool should_disable_strict;
  33. bool should_disable_partial;
  34. };
  35. bool ShouldDisableSiteIsolationDueToMemorySlow(
  36. content::SiteIsolationMode site_isolation_mode) {
  37. // The memory threshold behavior differs for desktop and Android:
  38. // - Android uses a 1900MB default threshold for partial site isolation modes
  39. // and a 3200MB default threshold for strict site isolation. See docs in
  40. // https://crbug.com/849815. The thresholds roughly correspond to 2GB+ and
  41. // 4GB+ devices and are lower to account for memory carveouts, which
  42. // reduce the amount of memory seen by AmountOfPhysicalMemoryMB(). Both
  43. // partial and strict site isolation thresholds can be overridden via
  44. // params defined in a kSiteIsolationMemoryThresholds field trial.
  45. // - Desktop does not enforce a default memory threshold, but for now we
  46. // still support a threshold defined via a kSiteIsolationMemoryThresholds
  47. // field trial. The trial typically carries the threshold in a param; if
  48. // it doesn't, use a default that's slightly higher than 1GB (see
  49. // https://crbug.com/844118).
  50. int default_memory_threshold_mb;
  51. #if BUILDFLAG(IS_ANDROID)
  52. if (site_isolation_mode == content::SiteIsolationMode::kStrictSiteIsolation) {
  53. default_memory_threshold_mb = 3200;
  54. } else {
  55. default_memory_threshold_mb = 1900;
  56. }
  57. #else
  58. default_memory_threshold_mb = 1077;
  59. #endif
  60. if (base::FeatureList::IsEnabled(features::kSiteIsolationMemoryThresholds)) {
  61. std::string param_name;
  62. switch (site_isolation_mode) {
  63. case content::SiteIsolationMode::kStrictSiteIsolation:
  64. param_name = features::kStrictSiteIsolationMemoryThresholdParamName;
  65. break;
  66. case content::SiteIsolationMode::kPartialSiteIsolation:
  67. param_name = features::kPartialSiteIsolationMemoryThresholdParamName;
  68. break;
  69. }
  70. int memory_threshold_mb = base::GetFieldTrialParamByFeatureAsInt(
  71. features::kSiteIsolationMemoryThresholds, param_name,
  72. default_memory_threshold_mb);
  73. return base::SysInfo::AmountOfPhysicalMemoryMB() <= memory_threshold_mb;
  74. }
  75. #if BUILDFLAG(IS_ANDROID)
  76. if (base::SysInfo::AmountOfPhysicalMemoryMB() <=
  77. default_memory_threshold_mb) {
  78. return true;
  79. }
  80. #endif
  81. return false;
  82. }
  83. IsolationDisableDecisions MakeBothDecisions() {
  84. IsolationDisableDecisions result{
  85. .should_disable_strict = ShouldDisableSiteIsolationDueToMemorySlow(
  86. content::SiteIsolationMode::kStrictSiteIsolation),
  87. .should_disable_partial = ShouldDisableSiteIsolationDueToMemorySlow(
  88. content::SiteIsolationMode::kPartialSiteIsolation)};
  89. return result;
  90. }
  91. bool CachedDisableSiteIsolation(
  92. content::SiteIsolationMode site_isolation_mode) {
  93. static const IsolationDisableDecisions decisions = MakeBothDecisions();
  94. if (site_isolation_mode == content::SiteIsolationMode::kStrictSiteIsolation) {
  95. return decisions.should_disable_strict;
  96. }
  97. return decisions.should_disable_partial;
  98. }
  99. } // namespace
  100. // static
  101. bool SiteIsolationPolicy::IsIsolationForPasswordSitesEnabled() {
  102. // If the user has explicitly enabled site isolation for password sites from
  103. // chrome://flags or from the command line, honor this regardless of policies
  104. // that may disable site isolation. In particular, this means that the
  105. // chrome://flags switch for this feature takes precedence over any memory
  106. // threshold restrictions and over a switch for disabling site isolation.
  107. if (base::FeatureList::GetInstance()->IsFeatureOverriddenFromCommandLine(
  108. features::kSiteIsolationForPasswordSites.name,
  109. base::FeatureList::OVERRIDE_ENABLE_FEATURE)) {
  110. return true;
  111. }
  112. // Don't isolate anything when site isolation is turned off by the user or
  113. // policy. This includes things like the switches::kDisableSiteIsolation
  114. // command-line switch, the corresponding "Disable site isolation" entry in
  115. // chrome://flags, enterprise policy controlled via
  116. // switches::kDisableSiteIsolationForPolicy, and memory threshold checks in
  117. // ShouldDisableSiteIsolationDueToMemoryThreshold().
  118. if (!content::SiteIsolationPolicy::AreDynamicIsolatedOriginsEnabled())
  119. return false;
  120. // The feature needs to be checked last, because checking the feature
  121. // activates the field trial and assigns the client either to a control or an
  122. // experiment group - such assignment should be final.
  123. return base::FeatureList::IsEnabled(features::kSiteIsolationForPasswordSites);
  124. }
  125. // static
  126. bool SiteIsolationPolicy::IsIsolationForOAuthSitesEnabled() {
  127. // If the user has explicitly enabled site isolation for OAuth sites from the
  128. // command line, honor this regardless of policies that may disable site
  129. // isolation.
  130. if (base::FeatureList::GetInstance()->IsFeatureOverriddenFromCommandLine(
  131. features::kSiteIsolationForOAuthSites.name,
  132. base::FeatureList::OVERRIDE_ENABLE_FEATURE)) {
  133. return true;
  134. }
  135. // Don't isolate anything when site isolation is turned off by the user or
  136. // policy. This includes things like the switches::kDisableSiteIsolation
  137. // command-line switch, the corresponding "Disable site isolation" entry in
  138. // chrome://flags, enterprise policy controlled via
  139. // switches::kDisableSiteIsolationForPolicy, and memory threshold checks in
  140. // ShouldDisableSiteIsolationDueToMemoryThreshold().
  141. if (!content::SiteIsolationPolicy::AreDynamicIsolatedOriginsEnabled())
  142. return false;
  143. // The feature needs to be checked last, because checking the feature
  144. // activates the field trial and assigns the client either to a control or an
  145. // experiment group - such assignment should be final.
  146. return base::FeatureList::IsEnabled(features::kSiteIsolationForOAuthSites);
  147. }
  148. // static
  149. bool SiteIsolationPolicy::IsEnterprisePolicyApplicable() {
  150. #if BUILDFLAG(IS_ANDROID)
  151. // https://crbug.com/844118: Limiting policy to devices with > 1GB RAM.
  152. // Using 1077 rather than 1024 because 1) it helps ensure that devices with
  153. // exactly 1GB of RAM won't get included because of inaccuracies or off-by-one
  154. // errors and 2) this is the bucket boundary in Memory.Stats.Win.TotalPhys2.
  155. bool have_enough_memory = base::SysInfo::AmountOfPhysicalMemoryMB() > 1077;
  156. return have_enough_memory;
  157. #else
  158. return true;
  159. #endif
  160. }
  161. // static
  162. bool SiteIsolationPolicy::ShouldDisableSiteIsolationDueToMemoryThreshold(
  163. content::SiteIsolationMode site_isolation_mode) {
  164. static const bool cache_memory_threshold_decision =
  165. ShouldCacheMemoryThresholdDecision();
  166. if (!g_disallow_memory_threshold_caching && cache_memory_threshold_decision) {
  167. return CachedDisableSiteIsolation(site_isolation_mode);
  168. }
  169. return ShouldDisableSiteIsolationDueToMemorySlow(site_isolation_mode);
  170. }
  171. // static
  172. void SiteIsolationPolicy::PersistIsolatedOrigin(
  173. content::BrowserContext* context,
  174. const url::Origin& origin,
  175. IsolatedOriginSource source) {
  176. DCHECK(context);
  177. DCHECK(!context->IsOffTheRecord());
  178. DCHECK(!origin.opaque());
  179. // This function currently supports two sources for persistence, for
  180. // user-triggered and web-triggered isolated origins.
  181. if (source == IsolatedOriginSource::USER_TRIGGERED) {
  182. PersistUserTriggeredIsolatedOrigin(context, origin);
  183. } else if (source == IsolatedOriginSource::WEB_TRIGGERED) {
  184. PersistWebTriggeredIsolatedOrigin(context, origin);
  185. } else {
  186. NOTREACHED();
  187. }
  188. }
  189. // static
  190. void SiteIsolationPolicy::PersistUserTriggeredIsolatedOrigin(
  191. content::BrowserContext* context,
  192. const url::Origin& origin) {
  193. // User-triggered isolated origins are currently stored in a simple list of
  194. // unlimited size.
  195. // TODO(alexmos): Cap the maximum number of entries and evict older entries.
  196. // See https://crbug.com/1172407.
  197. ListPrefUpdate update(user_prefs::UserPrefs::Get(context),
  198. site_isolation::prefs::kUserTriggeredIsolatedOrigins);
  199. base::Value* list = update.Get();
  200. base::Value value(origin.Serialize());
  201. if (!base::Contains(list->GetListDeprecated(), value))
  202. list->Append(std::move(value));
  203. }
  204. // static
  205. void SiteIsolationPolicy::PersistWebTriggeredIsolatedOrigin(
  206. content::BrowserContext* context,
  207. const url::Origin& origin) {
  208. // Web-triggered isolated origins are stored in a dictionary of (origin,
  209. // timestamp) pairs. The number of entries is capped by a field trial param,
  210. // and older entries are evicted.
  211. DictionaryPrefUpdate update(
  212. user_prefs::UserPrefs::Get(context),
  213. site_isolation::prefs::kWebTriggeredIsolatedOrigins);
  214. base::Value* dict = update.Get();
  215. // Add the origin. If it already exists, this will just update the
  216. // timestamp.
  217. dict->SetKey(origin.Serialize(), base::TimeToValue(base::Time::Now()));
  218. // Check whether the maximum number of stored sites was exceeded and remove
  219. // one or more entries, starting with the oldest timestamp. Note that more
  220. // than one entry may need to be removed, since the maximum number of entries
  221. // could change over time (via a change in the field trial param).
  222. size_t max_size =
  223. ::features::kSiteIsolationForCrossOriginOpenerPolicyMaxSitesParam.Get();
  224. while (dict->DictSize() > max_size) {
  225. auto items = dict->DictItems();
  226. auto oldest_site_time_pair = std::min_element(
  227. items.begin(), items.end(), [](auto pair_a, auto pair_b) {
  228. absl::optional<base::Time> time_a = base::ValueToTime(pair_a.second);
  229. absl::optional<base::Time> time_b = base::ValueToTime(pair_b.second);
  230. // has_value() should always be true unless the prefs were corrupted.
  231. // In that case, prioritize the corrupted entry for removal.
  232. return (time_a.has_value() ? time_a.value() : base::Time::Min()) <
  233. (time_b.has_value() ? time_b.value() : base::Time::Min());
  234. });
  235. dict->RemoveKey(oldest_site_time_pair->first);
  236. }
  237. }
  238. // static
  239. void SiteIsolationPolicy::ApplyPersistedIsolatedOrigins(
  240. content::BrowserContext* browser_context) {
  241. auto* policy = content::ChildProcessSecurityPolicy::GetInstance();
  242. // If the user turned off password-triggered isolation, don't apply any
  243. // stored isolated origins, but also don't clear them from prefs, so that
  244. // they can be used if password-triggered isolation is re-enabled later.
  245. if (IsIsolationForPasswordSitesEnabled()) {
  246. std::vector<url::Origin> origins;
  247. for (const auto& value :
  248. user_prefs::UserPrefs::Get(browser_context)
  249. ->GetValueList(prefs::kUserTriggeredIsolatedOrigins)) {
  250. origins.push_back(url::Origin::Create(GURL(value.GetString())));
  251. }
  252. if (!origins.empty()) {
  253. policy->AddFutureIsolatedOrigins(
  254. origins, IsolatedOriginSource::USER_TRIGGERED, browser_context);
  255. }
  256. base::UmaHistogramCounts1000(
  257. "SiteIsolation.SavedUserTriggeredIsolatedOrigins.Size", origins.size());
  258. }
  259. // Similarly, load saved web-triggered isolated origins only if isolation of
  260. // COOP sites (currently the only source of these origins) is enabled with
  261. // persistence, but don't remove them from prefs otherwise.
  262. if (content::SiteIsolationPolicy::ShouldPersistIsolatedCOOPSites()) {
  263. std::vector<url::Origin> origins;
  264. std::vector<std::string> expired_entries;
  265. auto* pref_service = user_prefs::UserPrefs::Get(browser_context);
  266. const auto& dict =
  267. pref_service->GetValueDict(prefs::kWebTriggeredIsolatedOrigins);
  268. for (auto site_time_pair : dict) {
  269. // Only isolate origins that haven't expired.
  270. absl::optional<base::Time> timestamp =
  271. base::ValueToTime(site_time_pair.second);
  272. base::TimeDelta expiration_timeout =
  273. ::features::
  274. kSiteIsolationForCrossOriginOpenerPolicyExpirationTimeoutParam
  275. .Get();
  276. if (timestamp.has_value() &&
  277. base::Time::Now() - timestamp.value() <= expiration_timeout) {
  278. origins.push_back(url::Origin::Create(GURL(site_time_pair.first)));
  279. } else {
  280. expired_entries.push_back(site_time_pair.first);
  281. }
  282. }
  283. // Remove expired entries (as well as those with an invalid timestamp).
  284. if (!expired_entries.empty()) {
  285. DictionaryPrefUpdate update(pref_service,
  286. prefs::kWebTriggeredIsolatedOrigins);
  287. base::Value* updated_dict = update.Get();
  288. for (const auto& entry : expired_entries)
  289. updated_dict->RemoveKey(entry);
  290. }
  291. if (!origins.empty()) {
  292. policy->AddFutureIsolatedOrigins(
  293. origins, IsolatedOriginSource::WEB_TRIGGERED, browser_context);
  294. }
  295. base::UmaHistogramCounts100(
  296. "SiteIsolation.SavedWebTriggeredIsolatedOrigins.Size", origins.size());
  297. }
  298. }
  299. // static
  300. void SiteIsolationPolicy::IsolateStoredOAuthSites(
  301. content::BrowserContext* browser_context,
  302. const std::vector<url::Origin>& logged_in_sites) {
  303. // Only isolate logged-in sites if the corresponding feature is enabled and
  304. // other isolation requirements (such as memory threshold) are satisfied.
  305. // Note that we don't clear logged-in sites from prefs if site isolation is
  306. // disabled so that they can be used if isolation is re-enabled later.
  307. if (!IsIsolationForOAuthSitesEnabled())
  308. return;
  309. auto* policy = content::ChildProcessSecurityPolicy::GetInstance();
  310. policy->AddFutureIsolatedOrigins(
  311. logged_in_sites,
  312. content::ChildProcessSecurityPolicy::IsolatedOriginSource::USER_TRIGGERED,
  313. browser_context);
  314. // Note that the max count matches
  315. // login_detection::GetOauthLoggedInSitesMaxSize().
  316. base::UmaHistogramCounts100("SiteIsolation.SavedOAuthSites.Size",
  317. logged_in_sites.size());
  318. }
  319. // static
  320. void SiteIsolationPolicy::IsolateNewOAuthURL(
  321. content::BrowserContext* browser_context,
  322. const GURL& signed_in_url) {
  323. if (!IsIsolationForOAuthSitesEnabled())
  324. return;
  325. // OAuth information is currently persisted and restored by other layers. See
  326. // login_detection::prefs::SaveSiteToOAuthSignedInList().
  327. constexpr bool kShouldPersist = false;
  328. content::SiteInstance::StartIsolatingSite(
  329. browser_context, signed_in_url,
  330. content::ChildProcessSecurityPolicy::IsolatedOriginSource::USER_TRIGGERED,
  331. kShouldPersist);
  332. }
  333. // static
  334. bool SiteIsolationPolicy::ShouldPdfCompositorBeEnabledForOopifs() {
  335. #if BUILDFLAG(IS_ANDROID)
  336. // TODO(crbug.com/1022917): Always enable on Android, at which point, this
  337. // method should go away.
  338. //
  339. // Only use the PDF compositor when one of the site isolation modes that
  340. // forces OOPIFs is on. This includes:
  341. // - Full site isolation, which may be forced on.
  342. // - Password-triggered site isolation for high-memory devices
  343. // - Isolated origins specified via command line, enterprise policy, or field
  344. // trials.
  345. return content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites() ||
  346. IsIsolationForPasswordSitesEnabled() ||
  347. content::SiteIsolationPolicy::AreIsolatedOriginsEnabled();
  348. #else
  349. // Always use the PDF compositor on non-mobile platforms.
  350. return true;
  351. #endif
  352. }
  353. // static
  354. void SiteIsolationPolicy::SetDisallowMemoryThresholdCachingForTesting(
  355. bool disallow_caching) {
  356. g_disallow_memory_threshold_caching = disallow_caching;
  357. }
  358. } // namespace site_isolation