diff options
| -rw-r--r-- | dex2oat/dex2oat.cc | 17 | ||||
| -rw-r--r-- | runtime/jit/profile_compilation_info.cc | 83 | ||||
| -rw-r--r-- | runtime/jit/profile_compilation_info.h | 9 | ||||
| -rwxr-xr-x | test/707-checker-invalid-profile/check | 19 | ||||
| -rw-r--r-- | test/707-checker-invalid-profile/expected.txt | 1 |
5 files changed, 129 insertions, 0 deletions
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index d8caf42f69..d4934e587f 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -712,6 +712,10 @@ class Dex2Oat FINAL { } } + bool VerifyProfileData() { + return profile_compilation_info_->VerifyProfileData(dex_files_); + } + void ParseInstructionSetVariant(const StringPiece& option, ParserOptions* parser_options) { DCHECK(option.starts_with("--instruction-set-variant=")); StringPiece str = option.substr(strlen("--instruction-set-variant=")).data(); @@ -3101,6 +3105,19 @@ static dex2oat::ReturnCode Dex2oat(int argc, char** argv) { return setup_code; } + // TODO: Due to the cyclic dependencies, profile loading and verifying are + // being done separately. Refactor and place the two next to each other. + // If verification fails, we don't abort the compilation and instead log an + // error. + // TODO(b/62602192, b/65260586): We should consider aborting compilation when + // the profile verification fails. + // Note: If dex2oat fails, installd will remove the oat files causing the app + // to fallback to apk with possible in-memory extraction. We want to avoid + // that, and thus we're lenient towards profile corruptions. + if (dex2oat->UseProfile()) { + dex2oat->VerifyProfileData(); + } + // Helps debugging on device. Can be used to determine which dalvikvm instance invoked a dex2oat // instance. Used by tools/bisection_search/bisection_search.py. VLOG(compiler) << "Running dex2oat (parent PID = " << getppid() << ")"; diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 57fc4976f7..9ce91b82a7 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -1048,6 +1048,89 @@ bool ProfileCompilationInfo::Load(int fd, bool merge_classes) { } } +bool ProfileCompilationInfo::VerifyProfileData(const std::vector<const DexFile*>& dex_files) { + std::unordered_map<std::string, const DexFile*> key_to_dex_file; + for (const DexFile* dex_file : dex_files) { + key_to_dex_file.emplace(GetProfileDexFileKey(dex_file->GetLocation()), dex_file); + } + for (const DexFileData* dex_data : info_) { + const auto it = key_to_dex_file.find(dex_data->profile_key); + if (it == key_to_dex_file.end()) { + // It is okay if profile contains data for additional dex files. + continue; + } + const DexFile* dex_file = it->second; + const std::string& dex_location = dex_file->GetLocation(); + if (!ChecksumMatch(dex_data->checksum, dex_file->GetLocationChecksum())) { + LOG(ERROR) << "Dex checksum mismatch while verifying profile " + << "dex location " << dex_location << " (checksum=" + << dex_file->GetLocationChecksum() << ", profile checksum=" + << dex_data->checksum; + return false; + } + // Verify method_encoding. + for (const auto& method_it : dex_data->method_map) { + size_t method_id = (size_t)(method_it.first); + if (method_id >= dex_file->NumMethodIds()) { + LOG(ERROR) << "Invalid method id in profile file. dex location=" + << dex_location << " method_id=" << method_id << " NumMethodIds=" + << dex_file->NumMethodIds(); + return false; + } + + // Verify class indices of inline caches. + const InlineCacheMap &inline_cache_map = method_it.second; + for (const auto& inline_cache_it : inline_cache_map) { + const DexPcData dex_pc_data = inline_cache_it.second; + if (dex_pc_data.is_missing_types || dex_pc_data.is_megamorphic) { + // No class indices to verify. + continue; + } + + const ClassSet &classes = dex_pc_data.classes; + SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map; + // Group the classes by dex. We expect that most of the classes will come from + // the same dex, so this will be more efficient than encoding the dex index + // for each class reference. + GroupClassesByDex(classes, &dex_to_classes_map); + for (const auto &dex_it : dex_to_classes_map) { + uint8_t dex_profile_index = dex_it.first; + const auto dex_file_inline_cache_it = key_to_dex_file.find( + info_[dex_profile_index]->profile_key); + if (dex_file_inline_cache_it == key_to_dex_file.end()) { + // It is okay if profile contains data for additional dex files. + continue; + } + const DexFile *dex_file_for_inline_cache_check = dex_file_inline_cache_it->second; + const std::vector<dex::TypeIndex> &dex_classes = dex_it.second; + for (size_t i = 0; i < dex_classes.size(); i++) { + if (dex_classes[i].index_ >= dex_file_for_inline_cache_check->NumTypeIds()) { + LOG(ERROR) << "Invalid inline cache in profile file. dex location=" + << dex_location << " method_id=" << method_id + << " dex_profile_index=" + << static_cast<uint16_t >(dex_profile_index) << " type_index=" + << dex_classes[i].index_ + << " NumTypeIds=" + << dex_file_for_inline_cache_check->NumTypeIds(); + return false; + } + } + } + } + } + // Verify class_ids. + for (const auto& class_id : dex_data->class_set) { + if (class_id.index_ >= dex_file->NumTypeIds()) { + LOG(ERROR) << "Invalid class id in profile file. dex_file location " + << dex_location << " class_id=" << class_id.index_ << " NumClassIds=" + << dex_file->NumClassDefs(); + return false; + } + } + } + return true; +} + // TODO(calin): fail fast if the dex checksums don't match. ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal( int fd, std::string* error, bool merge_classes) { diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index 5c7448fe63..477bbe6c5c 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -303,6 +303,15 @@ class ProfileCompilationInfo { // If merge_classes is set to false, classes will not be merged/loaded. bool Load(int fd, bool merge_classes = true); + // Verify integrity of the profile file with the provided dex files. + // If there exists a DexData object which maps to a dex_file, then it verifies that: + // - The checksums of the DexData and dex_file are equals. + // - No method id exceeds NumMethodIds corresponding to the dex_file. + // - No class id exceeds NumTypeIds corresponding to the dex_file. + // - For every inline_caches, class_ids does not exceed NumTypeIds corresponding to + // the dex_file they are in. + bool VerifyProfileData(const std::vector<const DexFile *> &dex_files); + // Load profile information from the given file // If the current profile is non-empty the load will fail. // If clear_if_invalid is true and the file is invalid the method clears the diff --git a/test/707-checker-invalid-profile/check b/test/707-checker-invalid-profile/check new file mode 100755 index 0000000000..a5d96f042b --- /dev/null +++ b/test/707-checker-invalid-profile/check @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# When profile verification fails, dex2oat logs an error. The following +# command parses the error message was logged. +grep -q -f $1 $2 diff --git a/test/707-checker-invalid-profile/expected.txt b/test/707-checker-invalid-profile/expected.txt index e69de29bb2..4d84c96443 100644 --- a/test/707-checker-invalid-profile/expected.txt +++ b/test/707-checker-invalid-profile/expected.txt @@ -0,0 +1 @@ +Invalid inline cache in profile file. |