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, ¶ms);
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