Home | History | Annotate | Download | only in tfvalidate
      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