Chromium Code Reviews| Index: components/sync/driver/about_sync_util.cc |
| diff --git a/components/sync/driver/about_sync_util.cc b/components/sync/driver/about_sync_util.cc |
| index c6c6a8bdddc14f3a22249f9737c8887b94fe5393..676265c30cfe27f6c99b0d78de625a2bb99e3159 100644 |
| --- a/components/sync/driver/about_sync_util.cc |
| +++ b/components/sync/driver/about_sync_util.cc |
| @@ -8,6 +8,7 @@ |
| #include <utility> |
| #include "base/location.h" |
| +#include "base/logging.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/stringprintf.h" |
| @@ -76,6 +77,11 @@ base::ListValue* AddSection(base::ListValue* parent_list, |
| section->SetString("title", title); |
| section->Set("data", section_contents); |
| section->SetBoolean("is_sensitive", false); |
| + // If the following |Append| results in a reallocation, pointers to |
| + // |parent_list| and its members will be invalidated. This would result in |
|
skym
2017/03/16 19:52:39
Wait, I thought I understood this, but now I'm con
jdoerrie
2017/03/23 18:11:17
My bad, your understanding is correct. I updated t
|
| + // use-after-free in |*SyncStat::SetValue|. This is why the following CHECK is |
| + // necessary to ensure no reallocation takes place. |
| + CHECK_LT(parent_list->GetSize(), parent_list->capacity()); |
| parent_list->Append(std::move(section)); |
| return section_contents; |
| } |
| @@ -89,6 +95,11 @@ base::ListValue* AddSensitiveSection(base::ListValue* parent_list, |
| section->SetString("title", title); |
| section->Set("data", section_contents); |
| section->SetBoolean("is_sensitive", true); |
| + // If the following |Append| results in a reallocation, pointers to |
| + // |parent_list| and its members will be invalidated. This would result in |
| + // use-after-free in |*SyncStat::SetValue|. This is why the following CHECK is |
| + // necessary to ensure no reallocation takes place. |
| + CHECK_LT(parent_list->GetSize(), parent_list->capacity()); |
| parent_list->Append(std::move(section)); |
| return section_contents; |
| } |
| @@ -117,7 +128,13 @@ StringSyncStat::StringSyncStat(base::ListValue* section, |
| stat_->SetString("stat_name", key); |
| stat_->SetString("stat_value", "Uninitialized"); |
| stat_->SetBoolean("is_valid", false); |
| + // |stat_| will be invalidated by |Append|, so it needs to be reset. |
| + // Furthermore, if |Append| results in a reallocation, |stat_| members of |
| + // other SyncStats will be invalidated. This is why the following check is |
| + // necessary, so that it is guaranteed that a reallocation will not happen. |
| + CHECK_LT(section->GetSize(), section->capacity()); |
| section->Append(base::WrapUnique(stat_)); |
| + section->GetDictionary(section->GetSize() - 1, &stat_); |
|
skym
2017/03/15 16:57:23
Are these GetDictionary(x, DictionaryValue**) meth
brettw
2017/03/15 22:14:14
These should all be removed, see the design doc I
|
| } |
| void StringSyncStat::SetValue(const std::string& value) { |
| @@ -145,7 +162,13 @@ BoolSyncStat::BoolSyncStat(base::ListValue* section, const std::string& key) { |
| stat_->SetString("stat_name", key); |
| stat_->SetBoolean("stat_value", false); |
| stat_->SetBoolean("is_valid", false); |
| + // |stat_| will be invalidated by |Append|, so it needs to be reset. |
| + // Furthermore, if |Append| results in a reallocation, |stat_| members of |
| + // other SyncStats will be invalidated. This is why the following check is |
| + // necessary, so that it is guaranteed that a reallocation will not happen. |
| + CHECK_LT(section->GetSize(), section->capacity()); |
| section->Append(base::WrapUnique(stat_)); |
| + section->GetDictionary(section->GetSize() - 1, &stat_); |
| } |
| void BoolSyncStat::SetValue(bool value) { |
| @@ -168,7 +191,13 @@ IntSyncStat::IntSyncStat(base::ListValue* section, const std::string& key) { |
| stat_->SetString("stat_name", key); |
| stat_->SetInteger("stat_value", 0); |
| stat_->SetBoolean("is_valid", false); |
| + // |stat_| will be invalidated by |Append|, so it needs to be reset. |
| + // Furthermore, if |Append| results in a reallocation, |stat_| members of |
| + // other SyncStats will be invalidated. This is why the following check is |
| + // necessary, so that it is guaranteed that a reallocation will not happen. |
| + CHECK_LT(section->GetSize(), section->capacity()); |
| section->Append(base::WrapUnique(stat_)); |
| + section->GetDictionary(section->GetSize() - 1, &stat_); |
| } |
| void IntSyncStat::SetValue(int value) { |
| @@ -251,24 +280,29 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( |
| // 'details': A list of sections. |
| base::ListValue* stats_list = new base::ListValue(); |
| + stats_list->Reserve(12); |
|
skym
2017/03/15 16:57:23
I've always really disliked how this file was impl
brettw
2017/03/15 22:14:14
Can you add TODOs referencing said bug on the Stat
jdoerrie
2017/03/23 18:11:17
Done.
|
| // The following lines define the sections and their fields. For each field, |
| // a class is instantiated, which allows us to reference the fields in |
| // 'setter' code later on in this function. |
| base::ListValue* section_summary = AddSection(stats_list, "Summary"); |
| + section_summary->Reserve(1); |
| StringSyncStat summary_string(section_summary, "Summary"); |
| base::ListValue* section_version = AddSection(stats_list, "Version Info"); |
| + section_version->Reserve(2); |
| StringSyncStat client_version(section_version, "Client Version"); |
| StringSyncStat server_url(section_version, "Server URL"); |
| base::ListValue* section_identity = |
| AddSensitiveSection(stats_list, kIdentityTitle); |
| + section_identity->Reserve(3); |
| StringSyncStat sync_id(section_identity, "Sync Client ID"); |
| StringSyncStat invalidator_id(section_identity, "Invalidator Client ID"); |
| StringSyncStat username(section_identity, "Username"); |
| base::ListValue* section_credentials = AddSection(stats_list, "Credentials"); |
| + section_credentials->Reserve(4); |
| StringSyncStat request_token_time(section_credentials, "Requested Token"); |
| StringSyncStat receive_token_time(section_credentials, "Received Token"); |
| StringSyncStat token_request_status(section_credentials, |
| @@ -276,6 +310,7 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( |
| StringSyncStat next_token_request(section_credentials, "Next Token Request"); |
| base::ListValue* section_local = AddSection(stats_list, "Local State"); |
| + section_local->Reserve(7); |
| StringSyncStat server_connection(section_local, "Server Connection"); |
| StringSyncStat last_synced(section_local, "Last Synced"); |
| BoolSyncStat is_setup_complete(section_local, |
| @@ -288,12 +323,14 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( |
| StringSyncStat local_backend_path(section_local, "Local backend path"); |
| base::ListValue* section_network = AddSection(stats_list, "Network"); |
| + section_network->Reserve(3); |
| BoolSyncStat is_throttled(section_network, "Throttled"); |
| StringSyncStat retry_time(section_network, "Retry time (maybe stale)"); |
| BoolSyncStat are_notifications_enabled(section_network, |
| "Notifications Enabled"); |
| base::ListValue* section_encryption = AddSection(stats_list, "Encryption"); |
| + section_encryption->Reserve(9); |
| BoolSyncStat is_using_explicit_passphrase(section_encryption, |
| "Explicit Passphrase"); |
| BoolSyncStat is_passphrase_required(section_encryption, |
| @@ -311,12 +348,14 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( |
| base::ListValue* section_last_session = |
| AddSection(stats_list, "Status from Last Completed Session"); |
| + section_last_session->Reserve(4); |
| StringSyncStat session_source(section_last_session, "Sync Source"); |
| StringSyncStat get_key_result(section_last_session, "GetKey Step Result"); |
| StringSyncStat download_result(section_last_session, "Download Step Result"); |
| StringSyncStat commit_result(section_last_session, "Commit Step Result"); |
| base::ListValue* section_counters = AddSection(stats_list, "Running Totals"); |
| + section_counters->Reserve(7); |
| IntSyncStat notifications_received(section_counters, |
| "Notifications Received"); |
| IntSyncStat updates_received(section_counters, "Updates Downloaded"); |
| @@ -330,6 +369,7 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( |
| base::ListValue* section_this_cycle = |
| AddSection(stats_list, "Transient Counters (this cycle)"); |
| + section_this_cycle->Reserve(4); |
| IntSyncStat encryption_conflicts(section_this_cycle, "Encryption Conflicts"); |
| IntSyncStat hierarchy_conflicts(section_this_cycle, "Hierarchy Conflicts"); |
| IntSyncStat server_conflicts(section_this_cycle, "Server Conflicts"); |
| @@ -337,12 +377,14 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( |
| base::ListValue* section_that_cycle = AddSection( |
| stats_list, "Transient Counters (last cycle of last completed session)"); |
| + section_that_cycle->Reserve(3); |
| IntSyncStat updates_downloaded(section_that_cycle, "Updates Downloaded"); |
| IntSyncStat committed_count(section_that_cycle, "Committed Count"); |
| IntSyncStat entries(section_that_cycle, "Entries"); |
| base::ListValue* section_nudge_info = |
| AddSection(stats_list, "Nudge Source Counters"); |
| + section_nudge_info->Reserve(3); |
| IntSyncStat nudge_source_notification(section_nudge_info, |
| "Server Invalidations"); |
| IntSyncStat nudge_source_local(section_nudge_info, "Local Changes"); |
| @@ -491,6 +533,7 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( |
| // actionable_error_detected is set. |
| base::ListValue* actionable_error = new base::ListValue(); |
| + actionable_error->Reserve(4); |
| about_info->Set("actionable_error", actionable_error); |
| StringSyncStat error_type(actionable_error, "Error Type"); |