1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "dumpsys/internal/filter_internal.h"
18 
19 #include <bluetooth/log.h>
20 
21 #include <algorithm>
22 #include <string>
23 
24 #include "flatbuffers/flatbuffers.h"
25 #include "flatbuffers/idl.h"
26 #include "os/log.h"
27 
28 #define DBG 0
29 
30 using namespace bluetooth;
31 using namespace dumpsys;
32 
33 constexpr flatbuffers::voffset_t kErasedFromTable = 0;
34 constexpr bool kFieldIsNotPopulated = true;
35 constexpr bool kFieldHasBeenFiltered = true;
36 constexpr bool kFieldContinueFiltering = false;
37 
ScrubFromTable(flatbuffers::Table * table,flatbuffers::voffset_t field_offset)38 void internal::ScrubFromTable(flatbuffers::Table* table, flatbuffers::voffset_t field_offset) {
39   log::assert_that(table != nullptr, "assert failed: table != nullptr");
40   uint8_t* vtable = const_cast<uint8_t*>(table->GetVTable());
41   vtable[field_offset] = kErasedFromTable;
42 }
43 
ReplaceInString(flatbuffers::String * string,int c)44 void internal::ReplaceInString(flatbuffers::String* string, int c) {
45   uint8_t* p = const_cast<uint8_t*>(string->Data());
46   memset(p, c, string->size());
47 }
48 
RandomizeInString(flatbuffers::String * string)49 void internal::RandomizeInString(flatbuffers::String* string) {
50   std::size_t hash = std::hash<std::string>{}(string->str());
51   std::string hashed_string = std::to_string(hash);
52   ReplaceInString(string, ' ');
53   size_t len = std::min(static_cast<size_t>(string->size()), hashed_string.size());
54   uint8_t* p = const_cast<uint8_t*>(string->Data());
55   memcpy(p, hashed_string.c_str(), len);
56 }
57 
PrivacyLevelName(PrivacyLevel privacy_level)58 const char* internal::PrivacyLevelName(PrivacyLevel privacy_level) {
59   switch (privacy_level) {
60     case kPrivate:
61       return "Private";
62       break;
63     case kOpaque:
64       return "Opaque";
65       break;
66     case kAnonymized:
67       return "Anonymized";
68       break;
69     case kAny:
70       return "Any";
71       break;
72   }
73 };
GetPrivacyLevelAttribute(const std::string & string)74 internal::PrivacyLevel internal::GetPrivacyLevelAttribute(const std::string& string) {
75   if (string == "Any") {
76     return kAny;
77   } else if (string == "Anonymized") {
78     return kAnonymized;
79   } else if (string == "Opaque") {
80     return kOpaque;
81   } else if (string == "Private") {
82     return kPrivate;
83   }
84   return kDefaultPrivacyLevel;
85 }
86 
FindFieldPrivacyLevel(const reflection::Field & field)87 internal::PrivacyLevel internal::FindFieldPrivacyLevel(const reflection::Field& field) {
88   PrivacyLevel privacy_level = kDefaultPrivacyLevel;
89 
90   if (field.attributes() != nullptr) {
91     auto key = field.attributes()->LookupByKey(kPrivacyAttributeKeyword);
92     if (key != nullptr) {
93       privacy_level = internal::GetPrivacyLevelAttribute(key->value()->str());
94     }
95   }
96   return privacy_level;
97 }
98 
FindReflectionObject(const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>> * objects,const flatbuffers::String * name)99 const reflection::Object* internal::FindReflectionObject(
100     const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>>* objects, const flatbuffers::String* name) {
101   log::assert_that(objects != nullptr, "assert failed: objects != nullptr");
102   log::assert_that(name != nullptr, "assert failed: name != nullptr");
103   for (auto it = objects->cbegin(); it != objects->cend(); ++it) {
104     if (it->name()->str() == name->str()) {
105       return *it;
106     }
107   }
108   return nullptr;
109 }
110 
FilterTypeBool(const reflection::Field & field,flatbuffers::Table * table,PrivacyLevel privacy_level)111 bool internal::FilterTypeBool(const reflection::Field& field, flatbuffers::Table* table, PrivacyLevel privacy_level) {
112   log::assert_that(table != nullptr, "assert failed: table != nullptr");
113 
114   const bool default_val = flatbuffers::GetFieldDefaultI<int8_t>(field);
115   flatbuffers::voffset_t field_offset = field.offset();
116 
117   // boolean privacy levels are simpler.
118   switch (privacy_level) {
119     case kPrivate:
120     case kOpaque:
121     case kAnonymized:
122       flatbuffers::SetField<int8_t>(table, field, default_val);
123       internal::ScrubFromTable(table, field_offset);
124       break;
125     default:
126     case kAny:
127       break;
128   }
129   return kFieldHasBeenFiltered;
130 }
131 
FilterTypeInteger(const reflection::Field & field,flatbuffers::Table * table,PrivacyLevel privacy_level)132 bool internal::FilterTypeInteger(
133     const reflection::Field& field, flatbuffers::Table* table, PrivacyLevel privacy_level) {
134   log::assert_that(table != nullptr, "assert failed: table != nullptr");
135   log::assert_that(
136       flatbuffers::IsInteger(field.type()->base_type()),
137       "assert failed: flatbuffers::IsInteger(field.type()->base_type())");
138 
139   int32_t default_val = flatbuffers::GetFieldDefaultI<int32_t>(field);
140   flatbuffers::voffset_t field_offset = field.offset();
141   [[maybe_unused]] int32_t val = table->GetField<int32_t>(field_offset, default_val);
142 
143   switch (privacy_level) {
144     case kPrivate:
145       flatbuffers::SetField<int32_t>(table, field, default_val);
146       internal::ScrubFromTable(table, field_offset);
147       break;
148     case kOpaque:
149       flatbuffers::SetField<int32_t>(table, field, default_val);
150       break;
151     case kAnonymized: {
152       auto target_field = flatbuffers::GetFieldI<int32_t>(*table, field);
153       int32_t new_val = static_cast<int32_t>(std::hash<std::string>{}(std::to_string(target_field)));
154       flatbuffers::SetField<int32_t>(table, field, new_val);
155     } break;
156     default:
157     case kAny:
158       break;
159   }
160 
161   if (DBG) {
162     log::info(
163         "Integer Field_name:{} privacy_level:{} old_value:{} / 0x{:x} ==> new_value:{}",
164         field.name()->c_str(),
165         PrivacyLevelName(privacy_level),
166         val,
167         val,
168         table->GetField<int32_t>(field_offset, default_val));
169   }
170   return kFieldHasBeenFiltered;
171 }
172 
FilterTypeFloat(const reflection::Field & field,flatbuffers::Table * table,PrivacyLevel privacy_level)173 bool internal::FilterTypeFloat(const reflection::Field& field, flatbuffers::Table* table, PrivacyLevel privacy_level) {
174   log::assert_that(table != nullptr, "assert failed: table != nullptr");
175   log::assert_that(
176       flatbuffers::IsFloat(field.type()->base_type()),
177       "assert failed: flatbuffers::IsFloat(field.type()->base_type())");
178 
179   float default_val = flatbuffers::GetFieldDefaultI<float>(field);
180   flatbuffers::voffset_t field_offset = field.offset();
181   [[maybe_unused]] float val = table->GetField<float>(field_offset, default_val);
182   switch (privacy_level) {
183     case kPrivate:
184       flatbuffers::SetField<float>(table, field, default_val);
185       internal::ScrubFromTable(table, field_offset);
186       break;
187     case kOpaque:
188       flatbuffers::SetField<float>(table, field, default_val);
189       break;
190     case kAnonymized: {
191       auto target_field = flatbuffers::GetFieldF<float>(*table, field);
192       int32_t new_val = static_cast<float>(std::hash<std::string>{}(std::to_string(target_field)));
193       flatbuffers::SetField<float>(table, field, new_val);
194     } break;
195     default:
196     case kAny:
197       break;
198   }
199   if (DBG) {
200     log::info(
201         "Float Field_name:{} privacy_level:{} old_value:{:f} ==> new_value:{:f}",
202         field.name()->c_str(),
203         PrivacyLevelName(privacy_level),
204         val,
205         table->GetField<float>(field_offset, default_val));
206   }
207   return kFieldHasBeenFiltered;
208 }
209 
FilterTypeLong(const reflection::Field & field,flatbuffers::Table * table,PrivacyLevel privacy_level)210 bool internal::FilterTypeLong(const reflection::Field& field, flatbuffers::Table* table, PrivacyLevel privacy_level) {
211   log::assert_that(table != nullptr, "assert failed: table != nullptr");
212 
213   const int64_t default_val = flatbuffers::GetFieldDefaultI<int64_t>(field);
214   flatbuffers::voffset_t field_offset = field.offset();
215 
216   switch (privacy_level) {
217     case kPrivate:
218       flatbuffers::SetField<int64_t>(table, field, default_val);
219       internal::ScrubFromTable(table, field_offset);
220       break;
221     case kOpaque:
222       flatbuffers::SetField<int64_t>(table, field, default_val);
223       break;
224     case kAnonymized: {
225       auto target_field = flatbuffers::GetFieldI<int64_t>(*table, field);
226       int64_t new_val = static_cast<int64_t>(std::hash<std::string>{}(std::to_string(target_field)));
227       flatbuffers::SetField<int64_t>(table, field, new_val);
228     } break;
229     default:
230     case kAny:
231       break;
232   }
233   return kFieldHasBeenFiltered;
234 }
235 
FilterTypeString(const reflection::Field & field,flatbuffers::Table * table,PrivacyLevel privacy_level)236 bool internal::FilterTypeString(const reflection::Field& field, flatbuffers::Table* table, PrivacyLevel privacy_level) {
237   log::assert_that(table != nullptr, "assert failed: table != nullptr");
238   log::assert_that(
239       field.type()->base_type() == reflection::BaseType::String,
240       "assert failed: field.type()->base_type() == reflection::BaseType::String");
241 
242   flatbuffers::voffset_t field_offset = field.offset();
243 
244   const flatbuffers::String* string = flatbuffers::GetFieldS(*table, field);
245   if (string == nullptr) {
246     return kFieldIsNotPopulated;
247     // Field is not populated
248   }
249   log::assert_that(string != nullptr, "assert failed: string != nullptr");
250   flatbuffers::String* mutable_string = const_cast<flatbuffers::String*>(string);
251 
252   [[maybe_unused]] std::string old_string(string->str());
253   switch (privacy_level) {
254     case kPrivate:
255       internal::ReplaceInString(mutable_string, '*');
256       internal::ScrubFromTable(table, field_offset);
257       break;
258     case kOpaque:
259       internal::ReplaceInString(mutable_string, '*');
260       break;
261     case kAnonymized:
262       internal::RandomizeInString(mutable_string);
263       break;
264     default:
265     case kAny:
266       break;
267   }
268   if (DBG) {
269     log::info(
270         "Field_name:{} size:{} privacy_level:{} old_string:{} ==> new_string:{}",
271         field.name()->c_str(),
272         string->size(),
273         PrivacyLevelName(privacy_level),
274         old_string,
275         string->c_str());
276   }
277   return kFieldHasBeenFiltered;
278 }
279 
FilterTypeStruct(const reflection::Field & field,flatbuffers::Table * table,PrivacyLevel privacy_level)280 bool internal::FilterTypeStruct(const reflection::Field& field, flatbuffers::Table* table, PrivacyLevel privacy_level) {
281   log::assert_that(table != nullptr, "assert failed: table != nullptr");
282   log::assert_that(
283       !flatbuffers::IsScalar(field.type()->base_type()),
284       "assert failed: !flatbuffers::IsScalar(field.type()->base_type())");
285 
286   flatbuffers::voffset_t field_offset = field.offset();
287 
288   if (privacy_level != kAny) {
289     flatbuffers::SetFieldT(table, field, nullptr);
290     internal::ScrubFromTable(table, field_offset);
291     if (DBG) {
292       log::info(
293           "Table Removing field name:{} privacy_level:{}",
294           field.name()->c_str(),
295           PrivacyLevelName(privacy_level));
296     }
297   }
298   return kFieldContinueFiltering;
299 }
300