privacy_filtering_check.cc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "services/tracing/perfetto/privacy_filtering_check.h"
  5. #include <string.h>
  6. #include <sstream>
  7. #include "base/check.h"
  8. #include "base/check_op.h"
  9. #include "base/logging.h"
  10. #include "services/tracing/perfetto/privacy_filtered_fields-inl.h"
  11. #include "third_party/perfetto/include/perfetto/protozero/proto_utils.h"
  12. #include "third_party/perfetto/protos/perfetto/trace/interned_data/interned_data.pbzero.h"
  13. #include "third_party/perfetto/protos/perfetto/trace/trace.pbzero.h"
  14. #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
  15. #include "third_party/perfetto/protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
  16. #include "third_party/perfetto/protos/perfetto/trace/track_event/track_event.pbzero.h"
  17. namespace tracing {
  18. namespace {
  19. using perfetto::protos::pbzero::InternedData;
  20. using perfetto::protos::pbzero::TracePacket;
  21. using perfetto::protos::pbzero::TrackDescriptor;
  22. using perfetto::protos::pbzero::TrackEvent;
  23. using protozero::ProtoDecoder;
  24. // Find the index of |value| in |arr|.
  25. int FindIndexOfValue(const int* const arr, uint32_t value) {
  26. for (unsigned i = 0; arr[i] != -1; ++i) {
  27. if (static_cast<int>(value) == arr[i])
  28. return i;
  29. }
  30. return -1;
  31. }
  32. #if DCHECK_IS_ON()
  33. // Logs the disallowed field with the list of parent field IDs.
  34. void LogDisallowedField(std::vector<uint32_t>* parent_ids, uint32_t field_id) {
  35. std::stringstream error;
  36. error << "Skipping field in Trace proto. IDs from root to child";
  37. for (int a : *parent_ids) {
  38. error << " : " << a;
  39. }
  40. error << " : " << field_id;
  41. VLOG(1) << error.rdbuf();
  42. }
  43. #endif // DCHECK_IS_ON()
  44. uint8_t* OffsetToPtr(size_t offset, std::string& str) {
  45. DCHECK_LT(offset, str.size());
  46. return reinterpret_cast<uint8_t*>(&str[0] + offset);
  47. }
  48. // Recursively copies the |proto|'s accepted field IDs including all sub
  49. // messages, over to |output|. Keeps track of |parent_ids| - field id in parent
  50. // message, in order of most recent child last.
  51. bool FilterProtoRecursively(const MessageInfo* root,
  52. ProtoDecoder* proto,
  53. std::vector<uint32_t>* parent_ids,
  54. std::string& output) {
  55. // Write any allowed fields of the message (the message's "payload") into
  56. // |output| at the |out_msg_start_offset|. This will not include the field ID
  57. // or size of the current message yet. We add those back below once we know
  58. // the final message size. Emitting the message payload into |output| saves
  59. // allocations for extra buffer, but will still require a memmove below. Other
  60. // alternative is to just use the max length bytes like protozero does.
  61. bool has_blocked_fields = false;
  62. const size_t out_msg_start_offset = output.size();
  63. proto->Reset();
  64. const uint8_t* current_field_start = proto->begin();
  65. const uint8_t* next_field_start = nullptr;
  66. for (auto f = proto->ReadField(); f.valid();
  67. f = proto->ReadField(), current_field_start = next_field_start) {
  68. next_field_start = proto->begin() + proto->read_offset();
  69. // If the field is not available in the accepted fields, then skip copying.
  70. int index = FindIndexOfValue(root->accepted_field_ids, f.id());
  71. if (index == -1) {
  72. #if DCHECK_IS_ON()
  73. LogDisallowedField(parent_ids, f.id());
  74. #endif
  75. has_blocked_fields = true;
  76. continue;
  77. }
  78. // If the field is allowed, then either the field is a nested message, or a
  79. // POD. If it's a nested message, then the message description will be
  80. // part of |sub_messages| list. If the message description is nullptr, then
  81. // assume it is POD.
  82. if (!root->sub_messages || root->sub_messages[index] == nullptr) {
  83. // PODs can just be copied over to output. Packed fields can be treated
  84. // just like primitive fields, by just copying over the full data. Note
  85. // that there cannot be packed nested messages. Note that we cannot use
  86. // |f.data()| here since it does not include the preamble (field id and
  87. // possibly length), so we need to keep track of |current_field_start|.
  88. output.append(current_field_start, next_field_start);
  89. } else {
  90. // Make recursive call to filter the nested message.
  91. ProtoDecoder decoder(f.data(), f.size());
  92. parent_ids->push_back(f.id());
  93. has_blocked_fields |= FilterProtoRecursively(
  94. root->sub_messages[index], &decoder, parent_ids, output);
  95. parent_ids->pop_back();
  96. }
  97. }
  98. const uint32_t payload_size = output.size() - out_msg_start_offset;
  99. // If there are any fields added, only then write the message to output.
  100. if (payload_size == 0) {
  101. return has_blocked_fields;
  102. }
  103. // The format is <field id><payload size><message data>.
  104. // This function wrote the payload of the current message starting from the
  105. // end of output. We need to insert the preamble (<field id><payload size>),
  106. // after moving the payload by the size of the preamble.
  107. const uint32_t field_id =
  108. protozero::proto_utils::MakeTagLengthDelimited(parent_ids->back());
  109. uint8_t field_id_buf[protozero::proto_utils::kMaxTagEncodedSize];
  110. uint8_t* field_id_end =
  111. protozero::proto_utils::WriteVarInt(field_id, field_id_buf);
  112. const uint8_t field_id_length = field_id_end - field_id_buf;
  113. uint8_t payload_size_buf[protozero::proto_utils::kMessageLengthFieldSize];
  114. uint8_t* payload_size_end =
  115. protozero::proto_utils::WriteVarInt(payload_size, payload_size_buf);
  116. const uint8_t payload_size_length = payload_size_end - payload_size_buf;
  117. // Resize |output| and move the payload, by size of the preamble.
  118. const size_t out_payload_start_offset =
  119. out_msg_start_offset + field_id_length + payload_size_length;
  120. output.append(field_id_length + payload_size_length, 0);
  121. memmove(OffsetToPtr(out_payload_start_offset, output),
  122. OffsetToPtr(out_msg_start_offset, output), payload_size);
  123. // Insert field id and payload length.
  124. memcpy(OffsetToPtr(out_msg_start_offset, output), field_id_buf,
  125. field_id_length);
  126. memcpy(OffsetToPtr(out_msg_start_offset + field_id_length, output),
  127. payload_size_buf, payload_size_length);
  128. return has_blocked_fields;
  129. }
  130. bool FilterProto(const std::string& serialized_trace_proto,
  131. std::string& output) {
  132. constexpr uint32_t kTracePacketFieldId = 1;
  133. // DO NOT use Trace::Decoder or TracePacket::Decoder since it sets the
  134. // TypedProtoDecoder does a memset of 0 for all field IDs. TracePacket is
  135. // especially bad because the max field ID is up to 1000s due to extensions.
  136. ProtoDecoder trace(
  137. reinterpret_cast<const uint8_t*>(serialized_trace_proto.data()),
  138. serialized_trace_proto.size());
  139. // Try to allocate all the memory before parsing the proto, so the parser runs
  140. // faster.
  141. output.reserve(serialized_trace_proto.size());
  142. std::vector<uint32_t> parent_ids;
  143. parent_ids.reserve(20);
  144. parent_ids.push_back(kTracePacketFieldId);
  145. bool has_blocked_fields = false;
  146. for (auto f = trace.ReadField(); f.valid(); f = trace.ReadField()) {
  147. CHECK_EQ(f.id(), kTracePacketFieldId);
  148. ProtoDecoder packet(f.data(), f.size());
  149. const MessageInfo* root = &kTracePacket;
  150. has_blocked_fields |=
  151. FilterProtoRecursively(root, &packet, &parent_ids, output);
  152. }
  153. return has_blocked_fields;
  154. }
  155. } // namespace
  156. PrivacyFilteringCheck::PrivacyFilteringCheck() = default;
  157. PrivacyFilteringCheck::~PrivacyFilteringCheck() = default;
  158. // static
  159. void PrivacyFilteringCheck::RemoveBlockedFields(
  160. std::string& serialized_trace_proto) {
  161. std::string output;
  162. FilterProto(serialized_trace_proto, output);
  163. serialized_trace_proto.swap(output);
  164. }
  165. void PrivacyFilteringCheck::CheckProtoForUnexpectedFields(
  166. const std::string& serialized_trace_proto) {
  167. std::string output;
  168. bool has_blocked_fields = FilterProto(serialized_trace_proto, output);
  169. DCHECK(!has_blocked_fields);
  170. perfetto::protos::pbzero::Trace::Decoder trace(
  171. reinterpret_cast<const uint8_t*>(serialized_trace_proto.data()),
  172. serialized_trace_proto.size());
  173. for (auto it = trace.packet(); !!it; ++it) {
  174. TracePacket::Decoder packet(*it);
  175. if (packet.has_track_event()) {
  176. ++stats_.track_event;
  177. } else if (packet.has_track_descriptor()) {
  178. TrackDescriptor::Decoder track_decoder(packet.track_descriptor());
  179. if (track_decoder.has_process()) {
  180. ++stats_.process_desc;
  181. } else if (track_decoder.has_thread()) {
  182. ++stats_.thread_desc;
  183. }
  184. }
  185. if (packet.has_interned_data()) {
  186. InternedData::Decoder interned_data(packet.interned_data().data,
  187. packet.interned_data().size);
  188. stats_.has_interned_names |= interned_data.has_event_names();
  189. stats_.has_interned_categories |= interned_data.has_event_categories();
  190. stats_.has_interned_source_locations |=
  191. interned_data.has_source_locations();
  192. stats_.has_interned_log_messages |= interned_data.has_log_message_body();
  193. }
  194. }
  195. }
  196. } // namespace tracing