1 // Copyright (C) 2019 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "apk_validator.h" 16 17 #include <iostream> 18 #include <sstream> 19 20 #include "androidfw/ZipFileRO.h" 21 22 #include <google/protobuf/io/zero_copy_stream_impl_lite.h> 23 24 namespace { 25 26 const char kTfProtoFilename[] = "tuningfork.proto"; 27 const char kDevProtoFilename[] = "dev_tuningfork.proto"; 28 const char kSettingsFilename[] = "tuningfork_settings.bin"; 29 30 int kMaxInstrumentationKeys = 256; // Sanity check 31 32 template <typename T> 33 std::ostream& operator<<(std::ostream& o, const std::vector<T>& vec) { 34 o << "["; 35 bool first = true; 36 for (auto& i : vec) { 37 if (!first) 38 o << ","; 39 else 40 first = false; 41 o << i; 42 } 43 o << "]"; 44 return o; 45 } 46 47 } // anonymous namespace 48 49 namespace tuningfork { 50 51 void ErrorCollector::AddError(const std::string& file_name, int line, int column, 52 const std::string& message) { 53 std::cerr << "Error in: " << file_name << " line: " << line << " column: " 54 << column << " message: " << message << std::endl; 55 } 56 57 #define ZERROR(CODE, MSG) {last_error_code_ = CODE; \ 58 {std::stringstream str; str << MSG; last_error_msg_ = str.str(); } \ 59 return nullptr;} 60 61 ZipSourceTree::ZipSourceTree(const std::shared_ptr<android::ZipFileRO>& zip_file, 62 const std::string& include_path) 63 : zip_file_(zip_file), include_path_(include_path), last_error_code_(0) { 64 } 65 66 pb::io::ZeroCopyInputStream* 67 ZipSourceTree::Open(const std::string& relative_path) { 68 std::string path = include_path_ + relative_path; 69 auto it = file_cache_.find(path); 70 if (file_cache_.end() == it) { 71 android::ZipEntryRO entry = zip_file_->findEntryByName(path.c_str()); 72 if (entry == nullptr) { 73 ZERROR(ERROR_NO_DEV_PROTO, "Could not find " << path); 74 } 75 uint32_t file_len = 0; 76 if (!zip_file_->getEntryInfo(entry, NULL, &file_len, 77 NULL, NULL, NULL, NULL)) { 78 ZERROR(ERROR_CANT_READ_PROTO, "Could not read " << path); 79 } 80 it = file_cache_.insert({path,{}}).first; 81 it->second.resize(file_len); 82 if (!zip_file_->uncompressEntry(entry, it->second.data(), file_len)) { 83 file_cache_.erase(it); 84 ZERROR(ERROR_CANT_UNCOMPRESS_PROTO, "Could not uncompress " << path); 85 } 86 } 87 return new pb::io::ArrayInputStream(it->second.data(), it->second.size()); 88 } 89 90 std::string ZipSourceTree::GetLastErrorMessage() { 91 return last_error_msg_; 92 } 93 94 int ZipSourceTree::GetLastErrorCode() { 95 return last_error_code_; 96 } 97 98 const std::shared_ptr<android::ZipFileRO>& ZipSourceTree::ZipFile() const { 99 return zip_file_; 100 } 101 102 std::string ZipSourceTree::IncludePath() const { 103 return include_path_; 104 } 105 106 ApkValidator::ApkValidator(const std::shared_ptr<android::ZipFileRO>& zip_file, 107 const std::string& asset_path, bool debug) 108 : source_tree_(zip_file, asset_path), debug_(debug) { 109 importer_ = std::unique_ptr<pb::compiler::Importer>( 110 new pb::compiler::Importer(&source_tree_, &error_collector_)); 111 } 112 113 int ApkValidator::Validate(bool enforce_enums_in_annotations, 114 bool check_dev_fidelity_params) { 115 // Import the dev proto 116 const pb::FileDescriptor* fdesc = importer_->Import(kDevProtoFilename); 117 if (!fdesc) 118 ERROR(source_tree_.GetLastErrorCode(), 119 source_tree_.GetLastErrorMessage()); 120 if (debug_) 121 std::cout << "Uncompressed " << kDevProtoFilename << std::endl; 122 DebugFileDesc(fdesc); 123 124 // Check Annotations 125 auto adesc = FindMessageIgnoringScope(fdesc, "Annotation"); 126 if (!adesc) 127 ERROR(ERROR_GETTING_ANNOTATION, "Error finding Annotation"); 128 std::vector<int> def_ann_enum_size; 129 auto aok = ValidateAnnotation(adesc, enforce_enums_in_annotations, 130 def_ann_enum_size); 131 if (aok != NO_ERROR) 132 ERROR(aok, "Error in Annotations"); 133 134 // Check FidelityParams 135 auto fpdesc = FindMessageIgnoringScope(fdesc, "FidelityParams"); 136 if (!fpdesc) 137 ERROR(ERROR_GETTING_FIDELITYPARAMS, "Error finding FidelityParams"); 138 auto fpok = ValidateFidelityParams(fpdesc); 139 if (fpok != NO_ERROR) 140 ERROR(fpok, "Error in FidelityParams"); 141 142 // Check the settings 143 const pb::FileDescriptor* tfdesc = importer_->Import(kTfProtoFilename); 144 if (!tfdesc) 145 ERROR(source_tree_.GetLastErrorCode(), 146 source_tree_.GetLastErrorMessage()); 147 if (debug_) 148 std::cout << "Uncompressed " << kTfProtoFilename << std::endl; 149 DebugFileDesc(tfdesc); 150 auto sdesc = FindMessageIgnoringScope(tfdesc, "Settings"); 151 if (!sdesc) 152 ERROR(ERROR_GETTING_SETTINGS, "Error finding Settings"); 153 auto sok = ValidateSettings(sdesc, def_ann_enum_size); 154 if (sok != NO_ERROR) 155 ERROR(sok, "Error validating settings"); 156 157 if (check_dev_fidelity_params) { 158 return ValidateDevFidelityParams(); 159 } 160 return NO_ERROR; 161 } 162 163 int ApkValidator::ValidateAnnotation(const pb::Descriptor* desc, 164 bool enforce_enums_in_annotations, 165 std::vector<int>& enum_sizes) { 166 enum_sizes.clear(); 167 DebugDesc(desc); 168 if (desc->oneof_decl_count() > 0 || desc->nested_type_count() > 0 169 || desc->extension_count() > 0 || desc->extension_range_count() > 0) 170 ERROR(ERROR_ANNOTATION_TOO_COMPLEX, "Annotation too complex"); 171 auto n = desc->field_count(); 172 for (int i=0; i < n; ++i) { 173 auto field = desc->field(i); 174 if (field->type() == pb::FieldDescriptor::TYPE_ENUM) { 175 enum_sizes.push_back(field->enum_type()->value_count()); 176 } 177 else { 178 if (enforce_enums_in_annotations) { 179 ERROR(ERROR_ANNOTATION_ONLY_ENUMS, 180 "Annotation can only contain enums"); 181 } else { 182 // Still check it's an int or bool 183 switch (field->type()) { 184 case pb::FieldDescriptor::TYPE_INT32: 185 case pb::FieldDescriptor::TYPE_UINT32: 186 enum_sizes.push_back(-1); 187 break; 188 case pb::FieldDescriptor::TYPE_BOOL: 189 enum_sizes.push_back(2); 190 break; 191 default: 192 ERROR(ERROR_ANNOTATION_ONLY_ENUMS, 193 "Annotation can only contain enums, ints or bools"); 194 } 195 } 196 } 197 } 198 return NO_ERROR; 199 } 200 201 int ApkValidator::ValidateFidelityParams(const pb::Descriptor* desc) { 202 DebugDesc(desc); 203 if (desc->oneof_decl_count() > 0 || desc->nested_type_count() > 0 204 || desc->extension_count() > 0 || desc->extension_range_count() > 0) 205 ERROR(ERROR_FIDELITYPARAMS_TOO_COMPLEX, "FidelityParams too complex"); 206 auto n = desc->field_count(); 207 for (int i=0; i < n; ++i) { 208 auto field = desc->field(i); 209 if (field->type() == pb::FieldDescriptor::TYPE_GROUP 210 || field->type() == pb::FieldDescriptor::TYPE_MESSAGE 211 || field->type() == pb::FieldDescriptor::TYPE_BYTES) 212 ERROR(ERROR_FIDELITYPARAMS_BAD_TYPE, 213 "FidelityParams can only contain scalars"); 214 } 215 return NO_ERROR; 216 } 217 218 int ApkValidator::ValidateSettings(const pb::Descriptor* sdesc, 219 const std::vector<int>& def_ann_enum_size) { 220 auto settings_proto = factory_.GetPrototype(sdesc); 221 if (!settings_proto) 222 ERROR(ERROR_GETTING_SETTINGS, "Error making Settings prototype"); 223 pb::Message* m = settings_proto->New(); 224 if (!m) 225 ERROR(ERROR_GETTING_SETTINGS, "Error making Settings object"); 226 std::unique_ptr<pb::io::ArrayInputStream> settings_bin( 227 dynamic_cast<pb::io::ArrayInputStream*>( 228 source_tree_.Open(kSettingsFilename))); 229 if (!settings_bin) 230 ERROR(ERROR_GETTING_SETTINGS, "Error reading tuningfork_settings.bin"); 231 if (debug_) 232 std::cout << "Uncompressed " << kSettingsFilename << std::endl; 233 const void* ptr=0; 234 int sz = 0; 235 if (!settings_bin->Next(&ptr,&sz)) 236 ERROR(ERROR_GETTING_SETTINGS, "Error reading tuningfork settings data"); 237 m->ParseFromString((const char*)ptr); 238 const pb::Reflection* refl = m->GetReflection(); 239 auto aggregation_strategy_field = m->GetDescriptor() 240 ->FindFieldByName("aggregation_strategy"); 241 const pb::Message& agg_strat = refl->GetMessage(*m, aggregation_strategy_field); 242 const pb::Reflection* as_refl = agg_strat.GetReflection(); 243 auto max_ikeys_field = agg_strat.GetDescriptor() 244 ->FindFieldByName("max_instrumentation_keys"); 245 auto max_ikeys = as_refl->GetInt32(agg_strat, max_ikeys_field); 246 if (debug_) 247 std::cout << "Max instrumentation keys = " << max_ikeys << std::endl; 248 if (max_ikeys < 1 || max_ikeys>kMaxInstrumentationKeys) 249 ERROR(ERROR_BAD_MAX_INSTRUMENTATION_KEYS, "max_ikeys = " << max_ikeys); 250 auto ann_enum_size_field = agg_strat.GetDescriptor() 251 ->FindFieldByName("annotation_enum_size"); 252 std::vector<int> ann_enum_size; 253 int n = as_refl->FieldSize(agg_strat, ann_enum_size_field); 254 for (int i=0; i < n; ++i) 255 ann_enum_size.push_back(as_refl->GetRepeatedInt32(agg_strat, 256 ann_enum_size_field, 257 i)); 258 if (debug_) { 259 std::cout << "Deduced annotation enum sizes from Annotation: " 260 << def_ann_enum_size << std::endl; 261 std::cout << "Annotation enum sizes in settings file: " << ann_enum_size << std::endl; 262 } 263 if (ann_enum_size.size() != def_ann_enum_size.size()) 264 ERROR(ERROR_BAD_ANNOTATION_ENUM_SIZE, 265 "Annotation enum size length bad: " 266 << ann_enum_size.size() << "!=" << def_ann_enum_size.size()); 267 for (int i=0; i < ann_enum_size.size(); ++i) { 268 if (def_ann_enum_size[i] != -1 && ann_enum_size[i] != def_ann_enum_size[i]) 269 ERROR(ERROR_BAD_ANNOTATION_ENUM_SIZE, 270 "Bad annotation enum size: " 271 << ann_enum_size[i] << "!=" << def_ann_enum_size[i]); 272 } 273 return NO_ERROR; 274 } 275 276 int ApkValidator::ValidateDevFidelityParams() { 277 void* cookie; 278 android::ZipFileRO* zip_file = source_tree_.ZipFile().get(); 279 std::string prefix = source_tree_.IncludePath() + "dev_tuningfork_fidelityparams_"; 280 char suffix[] = "bin"; 281 zip_file->startIteration(&cookie, prefix.c_str(), suffix); 282 int fp_count = 0; 283 for (auto entry = zip_file->nextEntry(cookie); 284 entry; entry=zip_file->nextEntry(cookie)) { 285 ++fp_count; 286 } 287 zip_file->endIteration(cookie); 288 if (debug_) 289 std::cout << "Found " << fp_count << " files matching " 290 << prefix + "*.bin" << std::endl; 291 if (fp_count == 0) 292 ERROR(ERROR_NO_DEV_FIDELITYPARAMS, "No dev fidelity params present"); 293 return NO_ERROR; 294 } 295 296 // Find a message type, ignoring the scope 297 const pb::Descriptor* 298 ApkValidator::FindMessageIgnoringScope(const pb::FileDescriptor* fdesc, 299 const std::string& name) { 300 int n = fdesc->message_type_count(); 301 for (int i=0; i<n; ++i) { 302 auto desc = fdesc->message_type(i); 303 if (desc->name() == name) return desc; 304 } 305 return nullptr; 306 } 307 308 void ApkValidator::DebugFileDesc(const pb::FileDescriptor* fdesc) { 309 if (debug_) { 310 std::cout << fdesc->name() 311 << ": messages: " << fdesc->message_type_count() 312 << ", dependencies: " << fdesc->dependency_count() 313 << ", extensions: " << fdesc->extension_count() 314 << ", enums: " << fdesc->enum_type_count() 315 << std::endl; 316 } 317 } 318 319 void ApkValidator::DebugDesc(const pb::Descriptor* desc) { 320 if (debug_) { 321 std::cout << desc->name() 322 << ": fields: " << desc->field_count() 323 << ", one-ofs: " << desc->oneof_decl_count() 324 << ", nested: " << desc->nested_type_count() 325 << ", enums: " << desc->enum_type_count() 326 << ", extensions: " << desc->extension_count() 327 << ", extension ranges: " << desc->extension_range_count() 328 << std::endl; 329 } 330 } 331 332 } 333