• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/location.h"
6 #ifdef UNSAFE_BUFFERS_BUILD
7 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
8 #pragma allow_unsafe_buffers
9 #endif
10 
11 #include <stddef.h>
12 
13 #include <algorithm>
14 #include <string>
15 #include <string_view>
16 #include <tuple>
17 
18 #include "base/base_switches.h"
19 #include "base/check_is_test.h"
20 #include "base/containers/contains.h"
21 #include "base/containers/span.h"
22 #include "base/debug/crash_logging.h"
23 #include "base/debug/dump_without_crashing.h"
24 #include "base/feature_list.h"
25 #include "base/feature_visitor.h"
26 #include "base/logging.h"
27 #include "base/memory/ptr_util.h"
28 #include "base/memory/raw_ptr.h"
29 #include "base/metrics/field_trial.h"
30 #include "base/metrics/field_trial_param_associator.h"
31 #include "base/metrics/field_trial_params.h"
32 #include "base/metrics/persistent_memory_allocator.h"
33 #include "base/no_destructor.h"
34 #include "base/notreached.h"
35 #include "base/pickle.h"
36 #include "base/rand_util.h"
37 #include "base/strings/string_split.h"
38 #include "base/strings/string_util.h"
39 #include "base/strings/stringprintf.h"
40 #include "build/build_config.h"
41 #include "build/chromeos_buildflags.h"
42 
43 namespace base {
44 
45 namespace {
46 
47 // Pointer to the FeatureList instance singleton that was set via
48 // FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
49 // have more control over initialization timing. Leaky.
50 FeatureList* g_feature_list_instance = nullptr;
51 
52 // Tracks access to Feature state before FeatureList registration.
53 class EarlyFeatureAccessTracker {
54  public:
GetInstance()55   static EarlyFeatureAccessTracker* GetInstance() {
56     static NoDestructor<EarlyFeatureAccessTracker> instance;
57     return instance.get();
58   }
59 
60   // Invoked when `feature` is accessed before FeatureList registration.
AccessedFeature(const Feature & feature,bool with_feature_allow_list=false)61   void AccessedFeature(const Feature& feature,
62                        bool with_feature_allow_list = false) {
63     AutoLock lock(lock_);
64     if (fail_instantly_) {
65       Fail(&feature, with_feature_allow_list);
66     } else if (!feature_) {
67       feature_ = &feature;
68       feature_had_feature_allow_list_ = with_feature_allow_list;
69     }
70   }
71 
72   // Asserts that no feature was accessed before FeatureList registration.
AssertNoAccess()73   void AssertNoAccess() {
74     AutoLock lock(lock_);
75     if (feature_) {
76       Fail(feature_, feature_had_feature_allow_list_);
77     }
78   }
79 
80   // Makes calls to AccessedFeature() fail instantly.
FailOnFeatureAccessWithoutFeatureList()81   void FailOnFeatureAccessWithoutFeatureList() {
82     AutoLock lock(lock_);
83     if (feature_) {
84       Fail(feature_, feature_had_feature_allow_list_);
85     }
86     fail_instantly_ = true;
87   }
88 
89   // Resets the state of this tracker.
Reset()90   void Reset() {
91     AutoLock lock(lock_);
92     feature_ = nullptr;
93     fail_instantly_ = false;
94   }
95 
GetFeature()96   const Feature* GetFeature() {
97     AutoLock lock(lock_);
98     return feature_.get();
99   }
100 
101  private:
Fail(const Feature * feature,bool with_feature_allow_list)102   void Fail(const Feature* feature, bool with_feature_allow_list) {
103     // TODO(crbug.com/40237050): Enable this check on all platforms.
104 #if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
105 #if !BUILDFLAG(IS_NACL)
106     // Create a crash key with the name of the feature accessed too early, to
107     // facilitate crash triage.
108     SCOPED_CRASH_KEY_STRING256("FeatureList", "feature-accessed-too-early",
109                                feature->name);
110     SCOPED_CRASH_KEY_BOOL("FeatureList", "early-access-allow-list",
111                           with_feature_allow_list);
112 #endif  // !BUILDFLAG(IS_NACL)
113     CHECK(!feature) << "Accessed feature " << feature->name
114                     << (with_feature_allow_list
115                             ? " which is not on the allow list passed to "
116                               "SetEarlyAccessInstance()."
117                             : " before FeatureList registration.");
118 #endif  // !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) &&
119         // !BUILDFLAG(IS_CHROMEOS)
120   }
121 
122   friend class NoDestructor<EarlyFeatureAccessTracker>;
123 
124   EarlyFeatureAccessTracker() = default;
125   ~EarlyFeatureAccessTracker() = default;
126 
127   Lock lock_;
128 
129   // First feature to be accessed before FeatureList registration.
130   raw_ptr<const Feature> feature_ GUARDED_BY(lock_) = nullptr;
131   bool feature_had_feature_allow_list_ GUARDED_BY(lock_) = false;
132 
133   // Whether AccessedFeature() should fail instantly.
134   bool fail_instantly_ GUARDED_BY(lock_) = false;
135 };
136 
137 #if DCHECK_IS_ON()
138 const char* g_reason_overrides_disallowed = nullptr;
139 
DCheckOverridesAllowed()140 void DCheckOverridesAllowed() {
141   const bool feature_overrides_allowed = !g_reason_overrides_disallowed;
142   DCHECK(feature_overrides_allowed) << g_reason_overrides_disallowed;
143 }
144 #else
DCheckOverridesAllowed()145 void DCheckOverridesAllowed() {}
146 #endif
147 
148 // An allocator entry for a feature in shared memory. The FeatureEntry is
149 // followed by a base::Pickle object that contains the feature and trial name.
150 struct FeatureEntry {
151   // SHA1(FeatureEntry): Increment this if structure changes!
152   static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 2;
153 
154   // Expected size for 32/64-bit check.
155   static constexpr size_t kExpectedInstanceSize = 16;
156 
157   // Specifies whether a feature override enables or disables the feature. Same
158   // values as the OverrideState enum in feature_list.h
159   uint32_t override_state;
160 
161   // On e.g. x86, alignof(uint64_t) is 4.  Ensure consistent size and alignment
162   // of `pickle_size` across platforms.
163   uint32_t padding;
164 
165   // Size of the pickled structure, NOT the total size of this entry.
166   uint64_t pickle_size;
167 
168   // Return a pointer to the pickled data area immediately following the entry.
GetPickledDataPtrbase::__anon667eca6c0111::FeatureEntry169   uint8_t* GetPickledDataPtr() { return reinterpret_cast<uint8_t*>(this + 1); }
GetPickledDataPtrbase::__anon667eca6c0111::FeatureEntry170   const uint8_t* GetPickledDataPtr() const {
171     return reinterpret_cast<const uint8_t*>(this + 1);
172   }
173 
174   // Reads the feature and trial name from the pickle. Calling this is only
175   // valid on an initialized entry that's in shared memory.
GetFeatureAndTrialNamebase::__anon667eca6c0111::FeatureEntry176   bool GetFeatureAndTrialName(std::string_view* feature_name,
177                               std::string_view* trial_name) const {
178     Pickle pickle = Pickle::WithUnownedBuffer(
179         span(GetPickledDataPtr(), checked_cast<size_t>(pickle_size)));
180     PickleIterator pickle_iter(pickle);
181     if (!pickle_iter.ReadStringPiece(feature_name)) {
182       return false;
183     }
184     // Return true because we are not guaranteed to have a trial name anyways.
185     std::ignore = pickle_iter.ReadStringPiece(trial_name);
186     return true;
187   }
188 };
189 
190 // Splits |text| into two parts by the |separator| where the first part will be
191 // returned updated in |first| and the second part will be returned as |second|.
192 // This function returns false if there is more than one |separator| in |first|.
193 // If there is no |separator| presented in |first|, this function will not
194 // modify |first| and |second|. It's used for splitting the |enable_features|
195 // flag into feature name, field trial name and feature parameters.
SplitIntoTwo(std::string_view text,std::string_view separator,std::string_view * first,std::string * second)196 bool SplitIntoTwo(std::string_view text,
197                   std::string_view separator,
198                   std::string_view* first,
199                   std::string* second) {
200   std::vector<std::string_view> parts =
201       SplitStringPiece(text, separator, TRIM_WHITESPACE, SPLIT_WANT_ALL);
202   if (parts.size() == 2) {
203     *second = std::string(parts[1]);
204   } else if (parts.size() > 2) {
205     DLOG(ERROR) << "Only one '" << separator
206                 << "' is allowed but got: " << text;
207     return false;
208   }
209   *first = parts[0];
210   return true;
211 }
212 
213 // Checks and parses the |enable_features| flag and sets
214 // |parsed_enable_features| to be a comma-separated list of features,
215 // |force_fieldtrials| to be a comma-separated list of field trials that each
216 // feature want to associate with and |force_fieldtrial_params| to be the field
217 // trial parameters for each field trial.
218 // Returns true if |enable_features| is parsable, otherwise false.
ParseEnableFeatures(const std::string & enable_features,std::string * parsed_enable_features,std::string * force_fieldtrials,std::string * force_fieldtrial_params)219 bool ParseEnableFeatures(const std::string& enable_features,
220                          std::string* parsed_enable_features,
221                          std::string* force_fieldtrials,
222                          std::string* force_fieldtrial_params) {
223   std::vector<std::string> enable_features_list;
224   std::vector<std::string> force_fieldtrials_list;
225   std::vector<std::string> force_fieldtrial_params_list;
226   for (const auto& enable_feature :
227        FeatureList::SplitFeatureListString(enable_features)) {
228     std::string feature_name;
229     std::string study;
230     std::string group;
231     std::string feature_params;
232     if (!FeatureList::ParseEnableFeatureString(
233             enable_feature, &feature_name, &study, &group, &feature_params)) {
234       return false;
235     }
236 
237     // If feature params were set but group and study weren't, associate the
238     // feature and its feature params to a synthetic field trial as the
239     // feature params only make sense when it's combined with a field trial.
240     if (!feature_params.empty()) {
241       force_fieldtrials_list.push_back(study + "/" + group);
242       force_fieldtrial_params_list.push_back(study + "." + group + ":" +
243                                              feature_params);
244     }
245     enable_features_list.push_back(study.empty() ? std::move(feature_name)
246                                                  : feature_name + "<" + study);
247   }
248 
249   *parsed_enable_features = JoinString(enable_features_list, ",");
250   // Field trial separator is currently a slash. See
251   // |kPersistentStringSeparator| in base/metrics/field_trial.cc.
252   *force_fieldtrials = JoinString(force_fieldtrials_list, "/");
253   *force_fieldtrial_params = JoinString(force_fieldtrial_params_list, ",");
254   return true;
255 }
256 
UnpackFeatureCache(uint32_t packed_cache_value)257 std::pair<FeatureList::OverrideState, uint16_t> UnpackFeatureCache(
258     uint32_t packed_cache_value) {
259   return std::make_pair(
260       static_cast<FeatureList::OverrideState>(packed_cache_value >> 24),
261       packed_cache_value & 0xFFFF);
262 }
263 
PackFeatureCache(FeatureList::OverrideState override_state,uint32_t caching_context)264 uint32_t PackFeatureCache(FeatureList::OverrideState override_state,
265                           uint32_t caching_context) {
266   return (static_cast<uint32_t>(override_state) << 24) |
267          (caching_context & 0xFFFF);
268 }
269 
270 // A monotonically increasing id, passed to `FeatureList`s as they are created
271 // to invalidate the cache member of `base::Feature` objects that were queried
272 // with a different `FeatureList` installed.
273 uint16_t g_current_caching_context = 1;
274 
275 }  // namespace
276 
277 #if BUILDFLAG(DCHECK_IS_CONFIGURABLE)
278 BASE_FEATURE(kDCheckIsFatalFeature,
279              "DcheckIsFatal",
280              FEATURE_DISABLED_BY_DEFAULT);
281 #endif  // BUILDFLAG(DCHECK_IS_CONFIGURABLE)
282 
FeatureList()283 FeatureList::FeatureList() : caching_context_(g_current_caching_context++) {}
284 
285 FeatureList::~FeatureList() = default;
286 
ScopedDisallowOverrides(const char * reason)287 FeatureList::ScopedDisallowOverrides::ScopedDisallowOverrides(
288     const char* reason)
289 #if DCHECK_IS_ON()
290     : previous_reason_(g_reason_overrides_disallowed) {
291   g_reason_overrides_disallowed = reason;
292 }
293 #else
294 {
295 }
296 #endif
297 
~ScopedDisallowOverrides()298 FeatureList::ScopedDisallowOverrides::~ScopedDisallowOverrides() {
299 #if DCHECK_IS_ON()
300   g_reason_overrides_disallowed = previous_reason_;
301 #endif
302 }
303 
InitFromCommandLine(const std::string & enable_features,const std::string & disable_features)304 void FeatureList::InitFromCommandLine(const std::string& enable_features,
305                                       const std::string& disable_features) {
306   DCHECK(!initialized_);
307 
308   std::string parsed_enable_features;
309   std::string force_fieldtrials;
310   std::string force_fieldtrial_params;
311   bool parse_enable_features_result =
312       ParseEnableFeatures(enable_features, &parsed_enable_features,
313                           &force_fieldtrials, &force_fieldtrial_params);
314   DCHECK(parse_enable_features_result) << StringPrintf(
315       "The --%s list is unparsable or invalid, please check the format.",
316       ::switches::kEnableFeatures);
317 
318   // Only create field trials when field_trial_list is available. Some tests
319   // don't have field trial list available.
320   if (FieldTrialList::GetInstance()) {
321     bool associate_params_result = AssociateFieldTrialParamsFromString(
322         force_fieldtrial_params, &UnescapeValue);
323     DCHECK(associate_params_result) << StringPrintf(
324         "The field trial parameters part of the --%s list is invalid. Make "
325         "sure "
326         "you %%-encode the following characters in param values: %%:/.,",
327         ::switches::kEnableFeatures);
328 
329     bool create_trials_result =
330         FieldTrialList::CreateTrialsFromString(force_fieldtrials);
331     DCHECK(create_trials_result)
332         << StringPrintf("Invalid field trials are specified in --%s.",
333                         ::switches::kEnableFeatures);
334   }
335 
336   // Process disabled features first, so that disabled ones take precedence over
337   // enabled ones (since RegisterOverride() uses emplace()).
338   RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE);
339   RegisterOverridesFromCommandLine(parsed_enable_features,
340                                    OVERRIDE_ENABLE_FEATURE);
341 
342   initialized_from_command_line_ = true;
343 }
344 
InitFromSharedMemory(PersistentMemoryAllocator * allocator)345 void FeatureList::InitFromSharedMemory(PersistentMemoryAllocator* allocator) {
346   DCHECK(!initialized_);
347 
348   PersistentMemoryAllocator::Iterator iter(allocator);
349   const FeatureEntry* entry;
350   while ((entry = iter.GetNextOfObject<FeatureEntry>()) != nullptr) {
351     OverrideState override_state =
352         static_cast<OverrideState>(entry->override_state);
353 
354     std::string_view feature_name;
355     std::string_view trial_name;
356     if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name))
357       continue;
358 
359     FieldTrial* trial = FieldTrialList::Find(trial_name);
360     RegisterOverride(feature_name, override_state, trial);
361   }
362 }
363 
IsFeatureOverridden(const std::string & feature_name) const364 bool FeatureList::IsFeatureOverridden(const std::string& feature_name) const {
365   return GetOverrideEntryByFeatureName(feature_name);
366 }
367 
IsFeatureOverriddenFromCommandLine(const std::string & feature_name) const368 bool FeatureList::IsFeatureOverriddenFromCommandLine(
369     const std::string& feature_name) const {
370   const OverrideEntry* entry = GetOverrideEntryByFeatureName(feature_name);
371   return entry && !entry->overridden_by_field_trial;
372 }
373 
IsFeatureOverriddenFromCommandLine(const std::string & feature_name,OverrideState state) const374 bool FeatureList::IsFeatureOverriddenFromCommandLine(
375     const std::string& feature_name,
376     OverrideState state) const {
377   const OverrideEntry* entry = GetOverrideEntryByFeatureName(feature_name);
378   return entry && !entry->overridden_by_field_trial &&
379          entry->overridden_state == state;
380 }
381 
AssociateReportingFieldTrial(const std::string & feature_name,OverrideState for_overridden_state,FieldTrial * field_trial)382 void FeatureList::AssociateReportingFieldTrial(
383     const std::string& feature_name,
384     OverrideState for_overridden_state,
385     FieldTrial* field_trial) {
386   DCHECK(
387       IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state));
388 
389   // Only one associated field trial is supported per feature. This is generally
390   // enforced server-side.
391   OverrideEntry* entry = &overrides_.find(feature_name)->second;
392   if (entry->field_trial) {
393     NOTREACHED() << "Feature " << feature_name
394                  << " already has trial: " << entry->field_trial->trial_name()
395                  << ", associating trial: " << field_trial->trial_name();
396   }
397 
398   entry->field_trial = field_trial;
399 }
400 
RegisterFieldTrialOverride(const std::string & feature_name,OverrideState override_state,FieldTrial * field_trial)401 void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name,
402                                              OverrideState override_state,
403                                              FieldTrial* field_trial) {
404   DCHECK(field_trial);
405   DCHECK(!HasAssociatedFieldTrialByFeatureName(feature_name))
406       << "Feature " << feature_name << " is overriden multiple times in these "
407       << "trials: "
408       << overrides_.find(feature_name)->second.field_trial->trial_name()
409       << " and " << field_trial->trial_name() << ". "
410       << "Check the trial (study) in (1) the server config, "
411       << "(2) fieldtrial_testing_config.json, (3) about_flags.cc, and "
412       << "(4) client-side field trials.";
413 
414   RegisterOverride(feature_name, override_state, field_trial);
415 }
416 
RegisterExtraFeatureOverrides(const std::vector<FeatureOverrideInfo> & extra_overrides,bool replace_use_default_overrides)417 void FeatureList::RegisterExtraFeatureOverrides(
418     const std::vector<FeatureOverrideInfo>& extra_overrides,
419     bool replace_use_default_overrides) {
420   for (const FeatureOverrideInfo& override_info : extra_overrides) {
421     RegisterOverride(override_info.first.get().name, override_info.second,
422                      /*field_trial=*/nullptr, replace_use_default_overrides);
423   }
424 }
425 
AddFeaturesToAllocator(PersistentMemoryAllocator * allocator)426 void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) {
427   DCHECK(initialized_);
428 
429   for (const auto& override : overrides_) {
430     Pickle pickle;
431     pickle.WriteString(override.first);
432     if (override.second.field_trial)
433       pickle.WriteString(override.second.field_trial->trial_name());
434 
435     size_t total_size = sizeof(FeatureEntry) + pickle.size();
436     FeatureEntry* entry = allocator->New<FeatureEntry>(total_size);
437     if (!entry)
438       return;
439 
440     entry->override_state = override.second.overridden_state;
441     entry->pickle_size = pickle.size();
442     memcpy(entry->GetPickledDataPtr(), pickle.data(), pickle.size());
443 
444     allocator->MakeIterable(entry);
445   }
446 }
447 
GetFeatureOverrides(std::string * enable_overrides,std::string * disable_overrides,bool include_group_name) const448 void FeatureList::GetFeatureOverrides(std::string* enable_overrides,
449                                       std::string* disable_overrides,
450                                       bool include_group_name) const {
451   GetFeatureOverridesImpl(enable_overrides, disable_overrides, false,
452                           include_group_name);
453 }
454 
GetCommandLineFeatureOverrides(std::string * enable_overrides,std::string * disable_overrides) const455 void FeatureList::GetCommandLineFeatureOverrides(
456     std::string* enable_overrides,
457     std::string* disable_overrides) const {
458   GetFeatureOverridesImpl(enable_overrides, disable_overrides, true);
459 }
460 
461 // static
IsEnabled(const Feature & feature)462 bool FeatureList::IsEnabled(const Feature& feature) {
463   if (!g_feature_list_instance ||
464       !g_feature_list_instance->AllowFeatureAccess(feature)) {
465     EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(
466         feature, g_feature_list_instance &&
467                      g_feature_list_instance->IsEarlyAccessInstance());
468     return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
469   }
470   return g_feature_list_instance->IsFeatureEnabled(feature);
471 }
472 
473 // static
IsValidFeatureOrFieldTrialName(std::string_view name)474 bool FeatureList::IsValidFeatureOrFieldTrialName(std::string_view name) {
475   return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos;
476 }
477 
478 // static
GetStateIfOverridden(const Feature & feature)479 std::optional<bool> FeatureList::GetStateIfOverridden(const Feature& feature) {
480   if (!g_feature_list_instance ||
481       !g_feature_list_instance->AllowFeatureAccess(feature)) {
482     EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(
483         feature, g_feature_list_instance &&
484                      g_feature_list_instance->IsEarlyAccessInstance());
485     // If there is no feature list, there can be no overrides.
486     return std::nullopt;
487   }
488   return g_feature_list_instance->IsFeatureEnabledIfOverridden(feature);
489 }
490 
491 // static
GetFieldTrial(const Feature & feature)492 FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) {
493   if (!g_feature_list_instance ||
494       !g_feature_list_instance->AllowFeatureAccess(feature)) {
495     EarlyFeatureAccessTracker::GetInstance()->AccessedFeature(
496         feature, g_feature_list_instance &&
497                      g_feature_list_instance->IsEarlyAccessInstance());
498     return nullptr;
499   }
500   return g_feature_list_instance->GetAssociatedFieldTrial(feature);
501 }
502 
503 // static
SplitFeatureListString(std::string_view input)504 std::vector<std::string_view> FeatureList::SplitFeatureListString(
505     std::string_view input) {
506   return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
507 }
508 
509 // static
ParseEnableFeatureString(std::string_view enable_feature,std::string * feature_name,std::string * study_name,std::string * group_name,std::string * params)510 bool FeatureList::ParseEnableFeatureString(std::string_view enable_feature,
511                                            std::string* feature_name,
512                                            std::string* study_name,
513                                            std::string* group_name,
514                                            std::string* params) {
515   std::string_view first;
516   // First, check whether ":" is present. If true, feature parameters were
517   // set for this feature.
518   std::string feature_params;
519   if (!SplitIntoTwo(enable_feature, ":", &first, &feature_params))
520     return false;
521   // Then, check whether "." is present. If true, a group was specified for
522   // this feature.
523   std::string group;
524   if (!SplitIntoTwo(first, ".", &first, &group))
525     return false;
526   // Finally, check whether "<" is present. If true, a study was specified for
527   // this feature.
528   std::string study;
529   if (!SplitIntoTwo(first, "<", &first, &study))
530     return false;
531 
532   std::string enable_feature_name(first);
533   // If feature params were set but group and study weren't, associate the
534   // feature and its feature params to a synthetic field trial as the
535   // feature params only make sense when it's combined with a field trial.
536   if (!feature_params.empty()) {
537     study = study.empty() ? "Study" + enable_feature_name : std::move(study);
538     group = group.empty() ? "Group" + enable_feature_name : std::move(group);
539   }
540 
541   feature_name->swap(enable_feature_name);
542   study_name->swap(study);
543   group_name->swap(group);
544   params->swap(feature_params);
545   return true;
546 }
547 
548 // static
InitInstance(const std::string & enable_features,const std::string & disable_features)549 bool FeatureList::InitInstance(const std::string& enable_features,
550                                const std::string& disable_features) {
551   return InitInstance(enable_features, disable_features,
552                       std::vector<FeatureOverrideInfo>());
553 }
554 
555 // static
InitInstance(const std::string & enable_features,const std::string & disable_features,const std::vector<FeatureOverrideInfo> & extra_overrides)556 bool FeatureList::InitInstance(
557     const std::string& enable_features,
558     const std::string& disable_features,
559     const std::vector<FeatureOverrideInfo>& extra_overrides) {
560   // We want to initialize a new instance here to support command-line features
561   // in testing better. For example, we initialize a dummy instance in
562   // base/test/test_suite.cc, and override it in content/browser/
563   // browser_main_loop.cc.
564   // On the other hand, we want to avoid re-initialization from command line.
565   // For example, we initialize an instance in chrome/browser/
566   // chrome_browser_main.cc and do not override it in content/browser/
567   // browser_main_loop.cc.
568   // If the singleton was previously initialized from within an accessor, we
569   // want to prevent callers from reinitializing the singleton and masking the
570   // accessor call(s) which likely returned incorrect information.
571   EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess();
572   bool instance_existed_before = false;
573   if (g_feature_list_instance) {
574     if (g_feature_list_instance->initialized_from_command_line_)
575       return false;
576 
577     delete g_feature_list_instance;
578     g_feature_list_instance = nullptr;
579     instance_existed_before = true;
580   }
581 
582   std::unique_ptr<FeatureList> feature_list(new FeatureList);
583   feature_list->InitFromCommandLine(enable_features, disable_features);
584   feature_list->RegisterExtraFeatureOverrides(extra_overrides);
585   FeatureList::SetInstance(std::move(feature_list));
586   return !instance_existed_before;
587 }
588 
589 // static
GetInstance()590 FeatureList* FeatureList::GetInstance() {
591   return g_feature_list_instance;
592 }
593 
594 // static
SetInstance(std::unique_ptr<FeatureList> instance)595 void FeatureList::SetInstance(std::unique_ptr<FeatureList> instance) {
596   DCHECK(!g_feature_list_instance ||
597          g_feature_list_instance->IsEarlyAccessInstance());
598   // If there is an existing early-access instance, release it.
599   if (g_feature_list_instance) {
600     std::unique_ptr<FeatureList> old_instance =
601         WrapUnique(g_feature_list_instance);
602     g_feature_list_instance = nullptr;
603   }
604   instance->FinalizeInitialization();
605 
606   // Note: Intentional leak of global singleton.
607   g_feature_list_instance = instance.release();
608 
609   EarlyFeatureAccessTracker::GetInstance()->AssertNoAccess();
610 
611   // Don't configure random bytes field trials for a possibly early access
612   // FeatureList instance, as the state of the involved Features might change
613   // with the final FeatureList for this process.
614   if (!g_feature_list_instance->IsEarlyAccessInstance()) {
615 #if !BUILDFLAG(IS_NACL)
616     // Configured first because it takes precedence over the getrandom() trial.
617     internal::ConfigureBoringSSLBackedRandBytesFieldTrial();
618 #endif
619   }
620 
621 #if BUILDFLAG(DCHECK_IS_CONFIGURABLE)
622   // Update the behaviour of LOGGING_DCHECK to match the Feature configuration.
623   // DCHECK is also forced to be FATAL if we are running a death-test.
624   // TODO(crbug.com/1057995#c11): --gtest_internal_run_death_test doesn't
625   // currently run through this codepath, mitigated in
626   // base::TestSuite::Initialize() for now.
627   // TODO(asvitkine): If we find other use-cases that need integrating here
628   // then define a proper API/hook for the purpose.
629   if (FeatureList::IsEnabled(kDCheckIsFatalFeature) ||
630       CommandLine::ForCurrentProcess()->HasSwitch(
631           "gtest_internal_run_death_test")) {
632     logging::LOGGING_DCHECK = logging::LOGGING_FATAL;
633   } else {
634     logging::LOGGING_DCHECK = logging::LOGGING_ERROR;
635   }
636 #endif  // BUILDFLAG(DCHECK_IS_CONFIGURABLE)
637 }
638 
639 // static
SetEarlyAccessInstance(std::unique_ptr<FeatureList> instance,base::flat_set<std::string> allowed_feature_names)640 void FeatureList::SetEarlyAccessInstance(
641     std::unique_ptr<FeatureList> instance,
642     base::flat_set<std::string> allowed_feature_names) {
643   CHECK(!g_feature_list_instance);
644   CHECK(!allowed_feature_names.empty());
645   instance->allowed_feature_names_ = std::move(allowed_feature_names);
646   SetInstance(std::move(instance));
647 }
648 
649 // static
ClearInstanceForTesting()650 std::unique_ptr<FeatureList> FeatureList::ClearInstanceForTesting() {
651   FeatureList* old_instance = g_feature_list_instance;
652   g_feature_list_instance = nullptr;
653   EarlyFeatureAccessTracker::GetInstance()->Reset();
654   return WrapUnique(old_instance);
655 }
656 
657 // static
RestoreInstanceForTesting(std::unique_ptr<FeatureList> instance)658 void FeatureList::RestoreInstanceForTesting(
659     std::unique_ptr<FeatureList> instance) {
660   DCHECK(!g_feature_list_instance);
661   // Note: Intentional leak of global singleton.
662   g_feature_list_instance = instance.release();
663 }
664 
665 // static
FailOnFeatureAccessWithoutFeatureList()666 void FeatureList::FailOnFeatureAccessWithoutFeatureList() {
667   EarlyFeatureAccessTracker::GetInstance()
668       ->FailOnFeatureAccessWithoutFeatureList();
669 }
670 
671 // static
GetEarlyAccessedFeatureForTesting()672 const Feature* FeatureList::GetEarlyAccessedFeatureForTesting() {
673   return EarlyFeatureAccessTracker::GetInstance()->GetFeature();
674 }
675 
676 // static
ResetEarlyFeatureAccessTrackerForTesting()677 void FeatureList::ResetEarlyFeatureAccessTrackerForTesting() {
678   EarlyFeatureAccessTracker::GetInstance()->Reset();
679 }
680 
AddEarlyAllowedFeatureForTesting(std::string feature_name)681 void FeatureList::AddEarlyAllowedFeatureForTesting(std::string feature_name) {
682   CHECK(IsEarlyAccessInstance());
683   allowed_feature_names_.insert(std::move(feature_name));
684 }
685 
686 // static
VisitFeaturesAndParams(FeatureVisitor & visitor,std::string_view filter_prefix)687 void FeatureList::VisitFeaturesAndParams(FeatureVisitor& visitor,
688                                          std::string_view filter_prefix) {
689   // If there is no feature list, there are no overrides. This should only happen
690   // in tests.
691   // TODO(leszeks): Add a CHECK_IS_TEST() to verify the above.
692   if (!g_feature_list_instance) {
693     return;
694   }
695 
696   FieldTrialParamAssociator* params_associator =
697       FieldTrialParamAssociator::GetInstance();
698 
699   using FeatureOverride = std::pair<std::string, OverrideEntry>;
700   base::span<FeatureOverride> filtered_overrides(
701       g_feature_list_instance->overrides_);
702   if (!filter_prefix.empty()) {
703     // If there is a filter prefix, then change the begin/end range to be the
704     // range where the values are prefixed with the given prefix (overrides are
705     // lexically sorted, so this will be a continuous range). This is
706     // implemented as a binary search of the upper and lower bounds of the
707     // override iterator, projecting each iterator value to just the
708     // key, trimmed to the length of the prefix.
709     DCHECK(std::ranges::is_sorted(
710         filtered_overrides, std::less<>(),
711         [](const FeatureOverride& entry) { return entry.first; }));
712     filtered_overrides = std::ranges::equal_range(
713         filtered_overrides, filter_prefix, std::less<>(),
714         [filter_prefix](const FeatureOverride& entry) {
715           return std::string_view(entry.first).substr(0, filter_prefix.size());
716         });
717   }
718   for (const FeatureOverride& feature_override : filtered_overrides) {
719     FieldTrial* field_trial = feature_override.second.field_trial;
720 
721     std::string trial_name;
722     std::string group_name;
723     FieldTrialParams params;
724     if (field_trial) {
725       trial_name = field_trial->trial_name();
726       group_name = field_trial->group_name();
727       params_associator->GetFieldTrialParamsWithoutFallback(
728           trial_name, group_name, &params);
729     }
730 
731     visitor.Visit(feature_override.first,
732                   feature_override.second.overridden_state, params, trial_name,
733                   group_name);
734   }
735 }
736 
FinalizeInitialization()737 void FeatureList::FinalizeInitialization() {
738   DCHECK(!initialized_);
739   // Store the field trial list pointer for DCHECKing.
740   field_trial_list_ = FieldTrialList::GetInstance();
741   initialized_ = true;
742 }
743 
IsFeatureEnabled(const Feature & feature) const744 bool FeatureList::IsFeatureEnabled(const Feature& feature) const {
745   OverrideState overridden_state = GetOverrideState(feature);
746 
747   // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below.
748   if (overridden_state != OVERRIDE_USE_DEFAULT)
749     return overridden_state == OVERRIDE_ENABLE_FEATURE;
750 
751   return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
752 }
753 
IsFeatureEnabledIfOverridden(const Feature & feature) const754 std::optional<bool> FeatureList::IsFeatureEnabledIfOverridden(
755     const Feature& feature) const {
756   OverrideState overridden_state = GetOverrideState(feature);
757 
758   // If marked as OVERRIDE_USE_DEFAULT, fall through to returning empty.
759   if (overridden_state != OVERRIDE_USE_DEFAULT)
760     return overridden_state == OVERRIDE_ENABLE_FEATURE;
761 
762   return std::nullopt;
763 }
764 
GetOverrideState(const Feature & feature) const765 FeatureList::OverrideState FeatureList::GetOverrideState(
766     const Feature& feature) const {
767   DCHECK(initialized_);
768   DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name;
769   DCHECK(CheckFeatureIdentity(feature))
770       << feature.name
771       << " has multiple definitions. Either it is defined more than once in "
772          "code or (for component builds) the code is built into multiple "
773          "components (shared libraries) without a corresponding export "
774          "statement";
775 
776   uint32_t current_cache_value =
777       feature.cached_value.load(std::memory_order_relaxed);
778 
779   auto unpacked = UnpackFeatureCache(current_cache_value);
780 
781   if (unpacked.second == caching_context_)
782     return unpacked.first;
783 
784   OverrideState state = GetOverrideStateByFeatureName(feature.name);
785   uint32_t new_cache_value = PackFeatureCache(state, caching_context_);
786 
787   // Update the cache with the new value.
788   // In non-test code, this value can be in one of 2 states: either it's unset,
789   // or another thread has updated it to the same value we're about to write.
790   // Because of this, a plain `store` yields the correct result in all cases.
791   // In test code, it's possible for a different thread to have installed a new
792   // `ScopedFeatureList` and written a value that's different than the one we're
793   // about to write, although that would be a thread safety violation already
794   // and such tests should be fixed.
795   feature.cached_value.store(new_cache_value, std::memory_order_relaxed);
796 
797   return state;
798 }
799 
GetOverrideStateByFeatureName(std::string_view feature_name) const800 FeatureList::OverrideState FeatureList::GetOverrideStateByFeatureName(
801     std::string_view feature_name) const {
802   DCHECK(initialized_);
803   DCHECK(IsValidFeatureOrFieldTrialName(feature_name)) << feature_name;
804 
805   if (const OverrideEntry* entry =
806           GetOverrideEntryByFeatureName(feature_name)) {
807     // Activate the corresponding field trial, if necessary.
808     if (entry->field_trial) {
809       entry->field_trial->Activate();
810     }
811 
812     // TODO(asvitkine) Expand this section as more support is added.
813 
814     return entry->overridden_state;
815   }
816   // Otherwise, report that we want to use the default state.
817   return OVERRIDE_USE_DEFAULT;
818 }
819 
GetAssociatedFieldTrial(const Feature & feature) const820 FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) const {
821   DCHECK(initialized_);
822   DCHECK(CheckFeatureIdentity(feature)) << feature.name;
823 
824   return GetAssociatedFieldTrialByFeatureName(feature.name);
825 }
826 
827 const base::FeatureList::OverrideEntry*
GetOverrideEntryByFeatureName(std::string_view name) const828 FeatureList::GetOverrideEntryByFeatureName(std::string_view name) const {
829   DCHECK(IsValidFeatureOrFieldTrialName(name)) << name;
830 
831   auto it = overrides_.find(name);
832   if (it != overrides_.end()) {
833     const OverrideEntry& entry = it->second;
834     return &entry;
835   }
836   return nullptr;
837 }
838 
GetAssociatedFieldTrialByFeatureName(std::string_view name) const839 FieldTrial* FeatureList::GetAssociatedFieldTrialByFeatureName(
840     std::string_view name) const {
841   DCHECK(initialized_);
842 
843   if (const OverrideEntry* entry = GetOverrideEntryByFeatureName(name)) {
844     return entry->field_trial;
845   }
846   return nullptr;
847 }
848 
HasAssociatedFieldTrialByFeatureName(std::string_view name) const849 bool FeatureList::HasAssociatedFieldTrialByFeatureName(
850     std::string_view name) const {
851   DCHECK(!initialized_);
852 
853   const OverrideEntry* entry = GetOverrideEntryByFeatureName(name);
854   return entry && entry->field_trial;
855 }
856 
GetEnabledFieldTrialByFeatureName(std::string_view name) const857 FieldTrial* FeatureList::GetEnabledFieldTrialByFeatureName(
858     std::string_view name) const {
859   DCHECK(initialized_);
860 
861   const base::FeatureList::OverrideEntry* entry =
862       GetOverrideEntryByFeatureName(name);
863   if (entry &&
864       entry->overridden_state == base::FeatureList::OVERRIDE_ENABLE_FEATURE) {
865     return entry->field_trial;
866   }
867   return nullptr;
868 }
869 
ConstructAccessor()870 std::unique_ptr<FeatureList::Accessor> FeatureList::ConstructAccessor() {
871   if (initialized_) {
872     // This function shouldn't be called after initialization.
873     NOTREACHED();
874   }
875   // Use new and WrapUnique because we want to restrict access to the Accessor's
876   // constructor.
877   return base::WrapUnique(new Accessor(this));
878 }
879 
RegisterOverridesFromCommandLine(const std::string & feature_list,OverrideState overridden_state)880 void FeatureList::RegisterOverridesFromCommandLine(
881     const std::string& feature_list,
882     OverrideState overridden_state) {
883   for (const auto& value : SplitFeatureListString(feature_list)) {
884     std::string_view feature_name = value;
885     FieldTrial* trial = nullptr;
886 
887     // The entry may be of the form FeatureName<FieldTrialName - in which case,
888     // this splits off the field trial name and associates it with the override.
889     std::string::size_type pos = feature_name.find('<');
890     if (pos != std::string::npos) {
891       feature_name = std::string_view(value.data(), pos);
892       trial = FieldTrialList::Find(value.substr(pos + 1));
893 #if !BUILDFLAG(IS_NACL)
894       // If the below DCHECK fires, it means a non-existent trial name was
895       // specified via the "Feature<Trial" command-line syntax.
896       DCHECK(trial) << "trial='" << value.substr(pos + 1) << "' does not exist";
897 #endif  // !BUILDFLAG(IS_NACL)
898     }
899 
900     RegisterOverride(feature_name, overridden_state, trial);
901   }
902 }
903 
RegisterOverride(std::string_view feature_name,OverrideState overridden_state,FieldTrial * field_trial,bool replace_use_default_overrides)904 void FeatureList::RegisterOverride(std::string_view feature_name,
905                                    OverrideState overridden_state,
906                                    FieldTrial* field_trial,
907                                    bool replace_use_default_overrides) {
908   DCHECK(!initialized_);
909   DCheckOverridesAllowed();
910   if (field_trial) {
911     DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
912         << field_trial->trial_name();
913   }
914   if (StartsWith(feature_name, "*")) {
915     feature_name = feature_name.substr(1);
916     overridden_state = OVERRIDE_USE_DEFAULT;
917   }
918 
919   // When `replace_use_default_overrides` is true, if an `OVERRIDE_USE_DEFAULT`
920   // entry exists, it should be replaced.
921   const std::string feature_name_str(feature_name);
922   if (replace_use_default_overrides) {
923     auto found = overrides_.find(feature_name_str);
924     if (found != overrides_.end() &&
925         found->second.overridden_state == OVERRIDE_USE_DEFAULT) {
926       // Also, keep the existing trial if a null trial was passed.
927       auto* trial = field_trial ? field_trial : &*found->second.field_trial;
928       found->second = OverrideEntry(overridden_state, trial);
929       return;
930     }
931   }
932 
933   // Note: The semantics of emplace() is that it does not overwrite the entry if
934   // one already exists for the key. Thus, only the first override for a given
935   // feature name takes effect.
936   overrides_.emplace(feature_name_str,
937                      OverrideEntry(overridden_state, field_trial));
938 }
939 
GetFeatureOverridesImpl(std::string * enable_overrides,std::string * disable_overrides,bool command_line_only,bool include_group_name) const940 void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides,
941                                           std::string* disable_overrides,
942                                           bool command_line_only,
943                                           bool include_group_name) const {
944   DCHECK(initialized_);
945 
946   // Check that the FieldTrialList this is associated with, if any, is the
947   // active one. If not, it likely indicates that this FeatureList has override
948   // entries from a freed FieldTrial, which may be caused by an incorrect test
949   // set up.
950   if (field_trial_list_)
951     DCHECK_EQ(field_trial_list_, FieldTrialList::GetInstance());
952 
953   enable_overrides->clear();
954   disable_overrides->clear();
955 
956   // Note: Since |overrides_| is a std::map, iteration will be in alphabetical
957   // order. This is not guaranteed to users of this function, but is useful for
958   // tests to assume the order.
959   for (const auto& entry : overrides_) {
960     if (command_line_only &&
961         (entry.second.field_trial != nullptr ||
962          entry.second.overridden_state == OVERRIDE_USE_DEFAULT)) {
963       continue;
964     }
965 
966     std::string* target_list = nullptr;
967     switch (entry.second.overridden_state) {
968       case OVERRIDE_USE_DEFAULT:
969       case OVERRIDE_ENABLE_FEATURE:
970         target_list = enable_overrides;
971         break;
972       case OVERRIDE_DISABLE_FEATURE:
973         target_list = disable_overrides;
974         break;
975     }
976 
977     if (!target_list->empty())
978       target_list->push_back(',');
979     if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT)
980       target_list->push_back('*');
981     target_list->append(entry.first);
982     if (entry.second.field_trial) {
983       auto* const field_trial = entry.second.field_trial.get();
984       target_list->push_back('<');
985       target_list->append(field_trial->trial_name());
986       if (include_group_name) {
987         target_list->push_back('.');
988         target_list->append(field_trial->GetGroupNameWithoutActivation());
989       }
990     }
991   }
992 }
993 
CheckFeatureIdentity(const Feature & feature) const994 bool FeatureList::CheckFeatureIdentity(const Feature& feature) const {
995   AutoLock auto_lock(feature_identity_tracker_lock_);
996 
997   auto it = feature_identity_tracker_.find(feature.name);
998   if (it == feature_identity_tracker_.end()) {
999     // If it's not tracked yet, register it.
1000     feature_identity_tracker_[feature.name] = &feature;
1001     return true;
1002   }
1003   // Compare address of |feature| to the existing tracked entry.
1004   return it->second == &feature;
1005 }
1006 
IsEarlyAccessInstance() const1007 bool FeatureList::IsEarlyAccessInstance() const {
1008   return !allowed_feature_names_.empty();
1009 }
1010 
AllowFeatureAccess(const Feature & feature) const1011 bool FeatureList::AllowFeatureAccess(const Feature& feature) const {
1012   DCHECK(initialized_);
1013   // If this isn't an instance set with SetEarlyAccessInstance all features are
1014   // allowed to be checked.
1015   if (!IsEarlyAccessInstance()) {
1016     return true;
1017   }
1018   return base::Contains(allowed_feature_names_, feature.name);
1019 }
1020 
OverrideEntry(OverrideState overridden_state,FieldTrial * field_trial)1021 FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state,
1022                                           FieldTrial* field_trial)
1023     : overridden_state(overridden_state),
1024       field_trial(field_trial),
1025       overridden_by_field_trial(field_trial != nullptr) {}
1026 
Accessor(FeatureList * feature_list)1027 FeatureList::Accessor::Accessor(FeatureList* feature_list)
1028     : feature_list_(feature_list) {}
1029 
GetOverrideStateByFeatureName(std::string_view feature_name)1030 FeatureList::OverrideState FeatureList::Accessor::GetOverrideStateByFeatureName(
1031     std::string_view feature_name) {
1032   return feature_list_->GetOverrideStateByFeatureName(feature_name);
1033 }
1034 
GetParamsByFeatureName(std::string_view feature_name,std::map<std::string,std::string> * params)1035 bool FeatureList::Accessor::GetParamsByFeatureName(
1036     std::string_view feature_name,
1037     std::map<std::string, std::string>* params) {
1038   base::FieldTrial* trial =
1039       feature_list_->GetAssociatedFieldTrialByFeatureName(feature_name);
1040   return FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial,
1041                                                                        params);
1042 }
1043 
1044 }  // namespace base
1045