pref_hash_filter.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. // Copyright 2013 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 "services/preferences/tracked/pref_hash_filter.h"
  5. #include <stdint.h>
  6. #include <algorithm>
  7. #include <memory>
  8. #include <utility>
  9. #include "base/bind.h"
  10. #include "base/check_op.h"
  11. #include "base/metrics/histogram_macros.h"
  12. #include "base/notreached.h"
  13. #include "base/strings/string_number_conversions.h"
  14. #include "base/time/time.h"
  15. #include "base/values.h"
  16. #include "components/pref_registry/pref_registry_syncable.h"
  17. #include "components/prefs/pref_service.h"
  18. #include "components/prefs/pref_store.h"
  19. #include "services/preferences/public/cpp/tracked/pref_names.h"
  20. #include "services/preferences/tracked/dictionary_hash_store_contents.h"
  21. #include "services/preferences/tracked/pref_hash_store_transaction.h"
  22. #include "services/preferences/tracked/tracked_atomic_preference.h"
  23. #include "services/preferences/tracked/tracked_split_preference.h"
  24. namespace {
  25. void CleanupDeprecatedTrackedPreferences(
  26. base::DictionaryValue* pref_store_contents,
  27. PrefHashStoreTransaction* hash_store_transaction) {
  28. // Add deprecated previously tracked preferences below for them to be cleaned
  29. // up from both the pref files and the hash store.
  30. static const char* const kDeprecatedTrackedPreferences[] = {
  31. // TODO(pmonette): Remove in 2022+.
  32. "module_blacklist_cache_md5_digest"};
  33. for (size_t i = 0; i < std::size(kDeprecatedTrackedPreferences); ++i) {
  34. const char* key = kDeprecatedTrackedPreferences[i];
  35. pref_store_contents->RemovePath(key);
  36. hash_store_transaction->ClearHash(key);
  37. }
  38. }
  39. } // namespace
  40. using PrefTrackingStrategy =
  41. prefs::mojom::TrackedPreferenceMetadata::PrefTrackingStrategy;
  42. PrefHashFilter::PrefHashFilter(
  43. std::unique_ptr<PrefHashStore> pref_hash_store,
  44. StoreContentsPair external_validation_hash_store_pair,
  45. const std::vector<prefs::mojom::TrackedPreferenceMetadataPtr>&
  46. tracked_preferences,
  47. mojo::PendingRemote<prefs::mojom::ResetOnLoadObserver>
  48. reset_on_load_observer,
  49. scoped_refptr<base::RefCountedData<
  50. mojo::Remote<prefs::mojom::TrackedPreferenceValidationDelegate>>>
  51. delegate,
  52. size_t reporting_ids_count)
  53. : pref_hash_store_(std::move(pref_hash_store)),
  54. external_validation_hash_store_pair_(
  55. external_validation_hash_store_pair.first
  56. ? absl::make_optional(
  57. std::move(external_validation_hash_store_pair))
  58. : absl::nullopt),
  59. reset_on_load_observer_(std::move(reset_on_load_observer)),
  60. delegate_(std::move(delegate)) {
  61. DCHECK(pref_hash_store_);
  62. DCHECK_GE(reporting_ids_count, tracked_preferences.size());
  63. // Verify that, if |external_validation_hash_store_pair_| is present, both its
  64. // items are non-null.
  65. DCHECK(!external_validation_hash_store_pair_.has_value() ||
  66. (external_validation_hash_store_pair_->first &&
  67. external_validation_hash_store_pair_->second));
  68. prefs::mojom::TrackedPreferenceValidationDelegate* delegate_ptr =
  69. (delegate_ ? delegate_->data.get() : nullptr);
  70. for (size_t i = 0; i < tracked_preferences.size(); ++i) {
  71. const prefs::mojom::TrackedPreferenceMetadata& metadata =
  72. *tracked_preferences[i];
  73. std::unique_ptr<TrackedPreference> tracked_preference;
  74. switch (metadata.strategy) {
  75. case PrefTrackingStrategy::ATOMIC:
  76. tracked_preference = std::make_unique<TrackedAtomicPreference>(
  77. metadata.name, metadata.reporting_id, reporting_ids_count,
  78. metadata.enforcement_level, metadata.value_type, delegate_ptr);
  79. break;
  80. case PrefTrackingStrategy::SPLIT:
  81. tracked_preference = std::make_unique<TrackedSplitPreference>(
  82. metadata.name, metadata.reporting_id, reporting_ids_count,
  83. metadata.enforcement_level, metadata.value_type, delegate_ptr);
  84. break;
  85. }
  86. DCHECK(tracked_preference);
  87. bool is_new = tracked_paths_
  88. .insert(std::make_pair(metadata.name,
  89. std::move(tracked_preference)))
  90. .second;
  91. DCHECK(is_new);
  92. }
  93. }
  94. PrefHashFilter::~PrefHashFilter() {
  95. // Ensure new values for all |changed_paths_| have been flushed to
  96. // |pref_hash_store_| already.
  97. DCHECK(changed_paths_.empty());
  98. }
  99. // static
  100. void PrefHashFilter::RegisterProfilePrefs(
  101. user_prefs::PrefRegistrySyncable* registry) {
  102. // See GetResetTime for why this is a StringPref and not Int64Pref.
  103. registry->RegisterStringPref(
  104. user_prefs::kPreferenceResetTime,
  105. base::NumberToString(base::Time().ToInternalValue()));
  106. }
  107. // static
  108. base::Time PrefHashFilter::GetResetTime(PrefService* user_prefs) {
  109. // Provide our own implementation (identical to the PrefService::GetInt64) in
  110. // order to ensure it remains consistent with the way we store this value
  111. // (which we do via a PrefStore, preventing us from reusing
  112. // PrefService::SetInt64).
  113. int64_t internal_value = base::Time().ToInternalValue();
  114. if (!base::StringToInt64(
  115. user_prefs->GetString(user_prefs::kPreferenceResetTime),
  116. &internal_value)) {
  117. // Somehow the value stored on disk is not a valid int64_t.
  118. NOTREACHED();
  119. return base::Time();
  120. }
  121. return base::Time::FromInternalValue(internal_value);
  122. }
  123. // static
  124. void PrefHashFilter::ClearResetTime(PrefService* user_prefs) {
  125. user_prefs->ClearPref(user_prefs::kPreferenceResetTime);
  126. }
  127. void PrefHashFilter::Initialize(base::DictionaryValue* pref_store_contents) {
  128. DictionaryHashStoreContents dictionary_contents(pref_store_contents);
  129. std::unique_ptr<PrefHashStoreTransaction> hash_store_transaction(
  130. pref_hash_store_->BeginTransaction(&dictionary_contents));
  131. for (auto it = tracked_paths_.begin(); it != tracked_paths_.end(); ++it) {
  132. const std::string& initialized_path = it->first;
  133. const TrackedPreference* initialized_preference = it->second.get();
  134. const base::Value* value = pref_store_contents->FindPath(initialized_path);
  135. initialized_preference->OnNewValue(value, hash_store_transaction.get());
  136. }
  137. }
  138. // Marks |path| has having changed if it is part of |tracked_paths_|. A new hash
  139. // will be stored for it the next time FilterSerializeData() is invoked.
  140. void PrefHashFilter::FilterUpdate(const std::string& path) {
  141. auto it = tracked_paths_.find(path);
  142. if (it != tracked_paths_.end())
  143. changed_paths_.insert(std::make_pair(path, it->second.get()));
  144. }
  145. // Updates the stored hashes for |changed_paths_| before serializing data to
  146. // disk. This is required as storing the hash everytime a pref's value changes
  147. // is too expensive (see perf regression @ http://crbug.com/331273).
  148. PrefFilter::OnWriteCallbackPair PrefHashFilter::FilterSerializeData(
  149. base::DictionaryValue* pref_store_contents) {
  150. // Generate the callback pair before clearing |changed_paths_|.
  151. PrefFilter::OnWriteCallbackPair callback_pair =
  152. GetOnWriteSynchronousCallbacks(pref_store_contents);
  153. if (!changed_paths_.empty()) {
  154. base::TimeTicks checkpoint = base::TimeTicks::Now();
  155. {
  156. DictionaryHashStoreContents dictionary_contents(pref_store_contents);
  157. std::unique_ptr<PrefHashStoreTransaction> hash_store_transaction(
  158. pref_hash_store_->BeginTransaction(&dictionary_contents));
  159. std::unique_ptr<PrefHashStoreTransaction>
  160. external_validation_hash_store_transaction;
  161. if (external_validation_hash_store_pair_) {
  162. external_validation_hash_store_transaction =
  163. external_validation_hash_store_pair_->first->BeginTransaction(
  164. external_validation_hash_store_pair_->second.get());
  165. }
  166. for (ChangedPathsMap::const_iterator it = changed_paths_.begin();
  167. it != changed_paths_.end(); ++it) {
  168. const std::string& changed_path = it->first;
  169. const TrackedPreference* changed_preference = it->second;
  170. const base::Value* value = pref_store_contents->FindPath(changed_path);
  171. changed_preference->OnNewValue(value, hash_store_transaction.get());
  172. }
  173. changed_paths_.clear();
  174. }
  175. UMA_HISTOGRAM_TIMES("Settings.FilterSerializeDataTime",
  176. base::TimeTicks::Now() - checkpoint);
  177. }
  178. return callback_pair;
  179. }
  180. void PrefHashFilter::OnStoreDeletionFromDisk() {
  181. if (external_validation_hash_store_pair_) {
  182. external_validation_hash_store_pair_->second.get()->Reset();
  183. // The PrefStore will attempt to write preferences even if it's marked for
  184. // deletion. Clear the external store pair to avoid re-writing to the
  185. // external store.
  186. external_validation_hash_store_pair_.reset();
  187. }
  188. }
  189. void PrefHashFilter::FinalizeFilterOnLoad(
  190. PostFilterOnLoadCallback post_filter_on_load_callback,
  191. std::unique_ptr<base::DictionaryValue> pref_store_contents,
  192. bool prefs_altered) {
  193. DCHECK(pref_store_contents);
  194. base::TimeTicks checkpoint = base::TimeTicks::Now();
  195. bool did_reset = false;
  196. {
  197. DictionaryHashStoreContents dictionary_contents(pref_store_contents.get());
  198. std::unique_ptr<PrefHashStoreTransaction> hash_store_transaction(
  199. pref_hash_store_->BeginTransaction(&dictionary_contents));
  200. std::unique_ptr<PrefHashStoreTransaction>
  201. external_validation_hash_store_transaction;
  202. if (external_validation_hash_store_pair_) {
  203. external_validation_hash_store_transaction =
  204. external_validation_hash_store_pair_->first->BeginTransaction(
  205. external_validation_hash_store_pair_->second.get());
  206. }
  207. CleanupDeprecatedTrackedPreferences(pref_store_contents.get(),
  208. hash_store_transaction.get());
  209. for (auto it = tracked_paths_.begin(); it != tracked_paths_.end(); ++it) {
  210. if (it->second->EnforceAndReport(
  211. pref_store_contents.get(), hash_store_transaction.get(),
  212. external_validation_hash_store_transaction.get())) {
  213. did_reset = true;
  214. prefs_altered = true;
  215. }
  216. }
  217. if (hash_store_transaction->StampSuperMac())
  218. prefs_altered = true;
  219. }
  220. if (did_reset) {
  221. pref_store_contents->SetString(
  222. user_prefs::kPreferenceResetTime,
  223. base::NumberToString(base::Time::Now().ToInternalValue()));
  224. FilterUpdate(user_prefs::kPreferenceResetTime);
  225. if (reset_on_load_observer_)
  226. reset_on_load_observer_->OnResetOnLoad();
  227. }
  228. reset_on_load_observer_.reset();
  229. UMA_HISTOGRAM_TIMES("Settings.FilterOnLoadTime",
  230. base::TimeTicks::Now() - checkpoint);
  231. std::move(post_filter_on_load_callback)
  232. .Run(std::move(pref_store_contents), prefs_altered);
  233. }
  234. // static
  235. void PrefHashFilter::ClearFromExternalStore(
  236. HashStoreContents* external_validation_hash_store_contents,
  237. const base::DictionaryValue* changed_paths_and_macs) {
  238. DCHECK(!changed_paths_and_macs->DictEmpty());
  239. for (base::DictionaryValue::Iterator it(*changed_paths_and_macs);
  240. !it.IsAtEnd(); it.Advance()) {
  241. external_validation_hash_store_contents->RemoveEntry(it.key());
  242. }
  243. }
  244. // static
  245. void PrefHashFilter::FlushToExternalStore(
  246. std::unique_ptr<HashStoreContents> external_validation_hash_store_contents,
  247. std::unique_ptr<base::DictionaryValue> changed_paths_and_macs,
  248. bool write_success) {
  249. DCHECK(!changed_paths_and_macs->DictEmpty());
  250. DCHECK(external_validation_hash_store_contents);
  251. if (!write_success)
  252. return;
  253. for (base::DictionaryValue::Iterator it(*changed_paths_and_macs);
  254. !it.IsAtEnd(); it.Advance()) {
  255. const std::string& changed_path = it.key();
  256. const base::DictionaryValue* split_values = nullptr;
  257. if (it.value().GetAsDictionary(&split_values)) {
  258. for (base::DictionaryValue::Iterator inner_it(*split_values);
  259. !inner_it.IsAtEnd(); inner_it.Advance()) {
  260. const std::string* mac = inner_it.value().GetIfString();
  261. bool is_string = !!mac;
  262. DCHECK(is_string);
  263. external_validation_hash_store_contents->SetSplitMac(
  264. changed_path, inner_it.key(), *mac);
  265. }
  266. } else {
  267. DCHECK(it.value().is_string());
  268. external_validation_hash_store_contents->SetMac(changed_path,
  269. it.value().GetString());
  270. }
  271. }
  272. }
  273. PrefFilter::OnWriteCallbackPair PrefHashFilter::GetOnWriteSynchronousCallbacks(
  274. base::DictionaryValue* pref_store_contents) {
  275. if (changed_paths_.empty() || !external_validation_hash_store_pair_) {
  276. return std::make_pair(base::OnceClosure(),
  277. base::OnceCallback<void(bool success)>());
  278. }
  279. std::unique_ptr<base::DictionaryValue> changed_paths_macs =
  280. std::make_unique<base::DictionaryValue>();
  281. for (ChangedPathsMap::const_iterator it = changed_paths_.begin();
  282. it != changed_paths_.end(); ++it) {
  283. const std::string& changed_path = it->first;
  284. const TrackedPreference* changed_preference = it->second;
  285. switch (changed_preference->GetType()) {
  286. case TrackedPreferenceType::ATOMIC: {
  287. const base::Value* new_value =
  288. pref_store_contents->FindPath(changed_path);
  289. changed_paths_macs->SetKey(
  290. changed_path,
  291. base::Value(external_validation_hash_store_pair_->first->ComputeMac(
  292. changed_path, new_value)));
  293. break;
  294. }
  295. case TrackedPreferenceType::SPLIT: {
  296. const base::DictionaryValue* dict_value = nullptr;
  297. pref_store_contents->GetDictionary(changed_path, &dict_value);
  298. changed_paths_macs->SetKey(
  299. changed_path,
  300. base::Value::FromUniquePtrValue(
  301. external_validation_hash_store_pair_->first->ComputeSplitMacs(
  302. changed_path, dict_value)));
  303. break;
  304. }
  305. }
  306. }
  307. DCHECK(external_validation_hash_store_pair_->second->IsCopyable())
  308. << "External HashStoreContents must be copyable as it needs to be used "
  309. "off-thread";
  310. std::unique_ptr<HashStoreContents> hash_store_contents_copy =
  311. external_validation_hash_store_pair_->second->MakeCopy();
  312. // We can use raw pointers for the first callback instead of making more
  313. // copies as it will be executed in sequence before the second callback,
  314. // which owns the pointers.
  315. HashStoreContents* raw_contents = hash_store_contents_copy.get();
  316. base::DictionaryValue* raw_changed_paths_macs = changed_paths_macs.get();
  317. return std::make_pair(
  318. base::BindOnce(&ClearFromExternalStore, base::Unretained(raw_contents),
  319. base::Unretained(raw_changed_paths_macs)),
  320. base::BindOnce(&FlushToExternalStore, std::move(hash_store_contents_copy),
  321. std::move(changed_paths_macs)));
  322. }