/* * Copyright (C) 2016 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. */ #include "vdex_file.h" #include // For the PROT_* and MAP_* constants. #include // for mkdir() #include #include #include #include #include #include "base/bit_utils.h" #include "base/leb128.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" #include "base/zip_archive.h" #include "class_linker.h" #include "class_loader_context.h" #include "dex/art_dex_file_loader.h" #include "dex/class_accessor-inl.h" #include "dex/dex_file_loader.h" #include "gc/heap.h" #include "gc/space/image_space.h" #include "mirror/class-inl.h" #include "quicken_info.h" #include "handle_scope-inl.h" #include "runtime.h" #include "verifier/verifier_deps.h" namespace art { using android::base::StringPrintf; constexpr uint8_t VdexFile::VdexFileHeader::kVdexInvalidMagic[4]; constexpr uint8_t VdexFile::VdexFileHeader::kVdexMagic[4]; constexpr uint8_t VdexFile::VdexFileHeader::kVdexVersion[4]; bool VdexFile::VdexFileHeader::IsMagicValid() const { return (memcmp(magic_, kVdexMagic, sizeof(kVdexMagic)) == 0); } bool VdexFile::VdexFileHeader::IsVdexVersionValid() const { return (memcmp(vdex_version_, kVdexVersion, sizeof(kVdexVersion)) == 0); } VdexFile::VdexFileHeader::VdexFileHeader(bool has_dex_section ATTRIBUTE_UNUSED) : number_of_sections_(static_cast(VdexSection::kNumberOfSections)) { memcpy(magic_, kVdexMagic, sizeof(kVdexMagic)); memcpy(vdex_version_, kVdexVersion, sizeof(kVdexVersion)); DCHECK(IsMagicValid()); DCHECK(IsVdexVersionValid()); } std::unique_ptr VdexFile::OpenAtAddress(uint8_t* mmap_addr, size_t mmap_size, bool mmap_reuse, const std::string& vdex_filename, bool writable, bool low_4gb, std::string* error_msg) { ScopedTrace trace(("VdexFile::OpenAtAddress " + vdex_filename).c_str()); if (!OS::FileExists(vdex_filename.c_str())) { *error_msg = "File " + vdex_filename + " does not exist."; return nullptr; } std::unique_ptr vdex_file; if (writable) { vdex_file.reset(OS::OpenFileReadWrite(vdex_filename.c_str())); } else { vdex_file.reset(OS::OpenFileForReading(vdex_filename.c_str())); } if (vdex_file == nullptr) { *error_msg = "Could not open file " + vdex_filename + (writable ? " for read/write" : "for reading"); return nullptr; } int64_t vdex_length = vdex_file->GetLength(); if (vdex_length == -1) { *error_msg = "Could not read the length of file " + vdex_filename; return nullptr; } return OpenAtAddress(mmap_addr, mmap_size, mmap_reuse, vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, error_msg); } std::unique_ptr VdexFile::OpenAtAddress(uint8_t* mmap_addr, size_t mmap_size, bool mmap_reuse, int file_fd, size_t vdex_length, const std::string& vdex_filename, bool writable, bool low_4gb, std::string* error_msg) { if (mmap_addr != nullptr && mmap_size < vdex_length) { *error_msg = StringPrintf("Insufficient pre-allocated space to mmap vdex: %zu and %zu", mmap_size, vdex_length); return nullptr; } CHECK_IMPLIES(mmap_reuse, mmap_addr != nullptr); // Start as PROT_WRITE so we can mprotect back to it if we want to. MemMap mmap = MemMap::MapFileAtAddress( mmap_addr, vdex_length, PROT_READ | PROT_WRITE, writable ? MAP_SHARED : MAP_PRIVATE, file_fd, /* start= */ 0u, low_4gb, vdex_filename.c_str(), mmap_reuse, /* reservation= */ nullptr, error_msg); if (!mmap.IsValid()) { *error_msg = "Failed to mmap file " + vdex_filename + " : " + *error_msg; return nullptr; } std::unique_ptr vdex(new VdexFile(std::move(mmap))); if (!vdex->IsValid()) { *error_msg = "Vdex file is not valid"; return nullptr; } return vdex; } std::unique_ptr VdexFile::OpenFromDm(const std::string& filename, const ZipArchive& archive) { std::string error_msg; std::unique_ptr zip_entry(archive.Find(VdexFile::kVdexNameInDmFile, &error_msg)); if (zip_entry == nullptr) { LOG(INFO) << "No " << VdexFile::kVdexNameInDmFile << " file in DexMetadata archive. " << "Not doing fast verification."; return nullptr; } MemMap input_file = zip_entry->MapDirectlyOrExtract( filename.c_str(), VdexFile::kVdexNameInDmFile, &error_msg, alignof(VdexFile)); if (!input_file.IsValid()) { LOG(WARNING) << "Could not open vdex file in DexMetadata archive: " << error_msg; return nullptr; } std::unique_ptr vdex_file = std::make_unique(std::move(input_file)); if (!vdex_file->IsValid()) { LOG(WARNING) << "The dex metadata .vdex is not valid. Ignoring it."; return nullptr; } if (vdex_file->HasDexSection()) { LOG(ERROR) << "The dex metadata is not allowed to contain dex files"; android_errorWriteLog(0x534e4554, "178055795"); // Report to SafetyNet. return nullptr; } return vdex_file; } const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor, uint32_t dex_file_index) const { DCHECK(cursor == nullptr || (cursor > Begin() && cursor <= End())); if (cursor == nullptr) { // Beginning of the iteration, return the first dex file if there is one. return HasDexSection() ? DexBegin() : nullptr; } else if (dex_file_index >= GetNumberOfDexFiles()) { return nullptr; } else { // Fetch the next dex file. Return null if there is none. const uint8_t* data = cursor + reinterpret_cast(cursor)->file_size_; // Dex files are required to be 4 byte aligned. the OatWriter makes sure they are, see // OatWriter::SeekToDexFiles. return AlignUp(data, 4); } } const uint8_t* VdexFile::GetNextTypeLookupTableData(const uint8_t* cursor, uint32_t dex_file_index) const { if (cursor == nullptr) { // Beginning of the iteration, return the first dex file if there is one. return HasTypeLookupTableSection() ? TypeLookupTableDataBegin() : nullptr; } else if (dex_file_index >= GetNumberOfDexFiles()) { return nullptr; } else { const uint8_t* data = cursor + sizeof(uint32_t) + reinterpret_cast(cursor)[0]; // TypeLookupTables are required to be 4 byte aligned. the OatWriter makes sure they are. // We don't check this here to be defensive against corrupted vdex files. // Callers should check the returned value matches their expectations. return data; } } bool VdexFile::OpenAllDexFiles(std::vector>* dex_files, std::string* error_msg) const { const ArtDexFileLoader dex_file_loader; size_t i = 0; for (const uint8_t* dex_file_start = GetNextDexFileData(nullptr, i); dex_file_start != nullptr; dex_file_start = GetNextDexFileData(dex_file_start, ++i)) { size_t size = reinterpret_cast(dex_file_start)->file_size_; // TODO: Supply the location information for a vdex file. static constexpr char kVdexLocation[] = ""; std::string location = DexFileLoader::GetMultiDexLocation(i, kVdexLocation); std::unique_ptr dex(dex_file_loader.OpenWithDataSection( dex_file_start, size, /*data_base=*/ nullptr, /*data_size=*/ 0u, location, GetLocationChecksum(i), /*oat_dex_file=*/ nullptr, /*verify=*/ false, /*verify_checksum=*/ false, error_msg)); if (dex == nullptr) { return false; } dex_files->push_back(std::move(dex)); } return true; } static bool CreateDirectories(const std::string& child_path, /* out */ std::string* error_msg) { size_t last_slash_pos = child_path.find_last_of('/'); CHECK_NE(last_slash_pos, std::string::npos) << "Invalid path: " << child_path; std::string parent_path = child_path.substr(0, last_slash_pos); if (OS::DirectoryExists(parent_path.c_str())) { return true; } else if (CreateDirectories(parent_path, error_msg)) { if (mkdir(parent_path.c_str(), 0700) == 0) { return true; } *error_msg = "Could not create directory " + parent_path; return false; } else { return false; } } bool VdexFile::WriteToDisk(const std::string& path, const std::vector& dex_files, const verifier::VerifierDeps& verifier_deps, std::string* error_msg) { std::vector verifier_deps_data; verifier_deps.Encode(dex_files, &verifier_deps_data); uint32_t verifier_deps_size = verifier_deps_data.size(); // Add padding so the type lookup tables are 4 byte aligned. uint32_t verifier_deps_with_padding_size = RoundUp(verifier_deps_data.size(), 4); DCHECK_GE(verifier_deps_with_padding_size, verifier_deps_data.size()); verifier_deps_data.resize(verifier_deps_with_padding_size, 0); size_t type_lookup_table_size = 0u; for (const DexFile* dex_file : dex_files) { type_lookup_table_size += sizeof(uint32_t) + TypeLookupTable::RawDataLength(dex_file->NumClassDefs()); } VdexFile::VdexFileHeader vdex_header(/* has_dex_section= */ false); VdexFile::VdexSectionHeader sections[static_cast(VdexSection::kNumberOfSections)]; // Set checksum section. sections[VdexSection::kChecksumSection].section_kind = VdexSection::kChecksumSection; sections[VdexSection::kChecksumSection].section_offset = GetChecksumsOffset(); sections[VdexSection::kChecksumSection].section_size = sizeof(VdexFile::VdexChecksum) * dex_files.size(); // Set dex section. sections[VdexSection::kDexFileSection].section_kind = VdexSection::kDexFileSection; sections[VdexSection::kDexFileSection].section_offset = 0u; sections[VdexSection::kDexFileSection].section_size = 0u; // Set VerifierDeps section. sections[VdexSection::kVerifierDepsSection].section_kind = VdexSection::kVerifierDepsSection; sections[VdexSection::kVerifierDepsSection].section_offset = GetChecksumsOffset() + sections[kChecksumSection].section_size; sections[VdexSection::kVerifierDepsSection].section_size = verifier_deps_size; // Set TypeLookupTable section. sections[VdexSection::kTypeLookupTableSection].section_kind = VdexSection::kTypeLookupTableSection; sections[VdexSection::kTypeLookupTableSection].section_offset = sections[VdexSection::kVerifierDepsSection].section_offset + verifier_deps_with_padding_size; sections[VdexSection::kTypeLookupTableSection].section_size = type_lookup_table_size; if (!CreateDirectories(path, error_msg)) { return false; } std::unique_ptr out(OS::CreateEmptyFileWriteOnly(path.c_str())); if (out == nullptr) { *error_msg = "Could not open " + path + " for writing"; return false; } // Write header. if (!out->WriteFully(reinterpret_cast(&vdex_header), sizeof(vdex_header))) { *error_msg = "Could not write vdex header to " + path; out->Unlink(); return false; } // Write section infos. if (!out->WriteFully(reinterpret_cast(§ions), sizeof(sections))) { *error_msg = "Could not write vdex sections to " + path; out->Unlink(); return false; } // Write checksum section. for (const DexFile* dex_file : dex_files) { uint32_t checksum = dex_file->GetLocationChecksum(); const uint32_t* checksum_ptr = &checksum; static_assert(sizeof(*checksum_ptr) == sizeof(VdexFile::VdexChecksum)); if (!out->WriteFully(reinterpret_cast(checksum_ptr), sizeof(VdexFile::VdexChecksum))) { *error_msg = "Could not write dex checksums to " + path; out->Unlink(); return false; } } if (!out->WriteFully(reinterpret_cast(verifier_deps_data.data()), verifier_deps_with_padding_size)) { *error_msg = "Could not write verifier deps to " + path; out->Unlink(); return false; } size_t written_type_lookup_table_size = 0; for (const DexFile* dex_file : dex_files) { TypeLookupTable type_lookup_table = TypeLookupTable::Create(*dex_file); uint32_t size = type_lookup_table.RawDataLength(); DCHECK_ALIGNED(size, 4); if (!out->WriteFully(reinterpret_cast(&size), sizeof(uint32_t)) || !out->WriteFully(reinterpret_cast(type_lookup_table.RawData()), size)) { *error_msg = "Could not write type lookup table " + path; out->Unlink(); return false; } written_type_lookup_table_size += sizeof(uint32_t) + size; } DCHECK_EQ(written_type_lookup_table_size, type_lookup_table_size); if (out->FlushClose() != 0) { *error_msg = "Could not flush and close " + path; out->Unlink(); return false; } return true; } bool VdexFile::MatchesDexFileChecksums(const std::vector& dex_headers) const { if (dex_headers.size() != GetNumberOfDexFiles()) { LOG(WARNING) << "Mismatch of number of dex files in vdex (expected=" << GetNumberOfDexFiles() << ", actual=" << dex_headers.size() << ")"; return false; } const VdexChecksum* checksums = GetDexChecksumsArray(); for (size_t i = 0; i < dex_headers.size(); ++i) { if (checksums[i] != dex_headers[i]->checksum_) { LOG(WARNING) << "Mismatch of dex file checksum in vdex (index=" << i << ")"; return false; } } return true; } static ObjPtr FindClassAndClearException(ClassLinker* class_linker, Thread* self, const char* name, Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr result = class_linker->FindClass(self, name, class_loader); if (result == nullptr) { DCHECK(self->IsExceptionPending()); self->ClearException(); } return result; } static const char* GetStringFromId(const DexFile& dex_file, dex::StringIndex string_id, uint32_t number_of_extra_strings, const uint32_t* extra_strings_offsets, const uint8_t* verifier_deps) { uint32_t num_ids_in_dex = dex_file.NumStringIds(); if (string_id.index_ < num_ids_in_dex) { return dex_file.StringDataByIdx(string_id); } else { CHECK_LT(string_id.index_ - num_ids_in_dex, number_of_extra_strings); uint32_t offset = extra_strings_offsets[string_id.index_ - num_ids_in_dex]; return reinterpret_cast(verifier_deps) + offset; } } // Returns an array of offsets where the assignability checks for each class // definition are stored. static const uint32_t* GetDexFileClassDefs(const uint8_t* verifier_deps, uint32_t index) { uint32_t dex_file_offset = reinterpret_cast(verifier_deps)[index]; return reinterpret_cast(verifier_deps + dex_file_offset); } // Returns an array of offsets where extra strings are stored. static const uint32_t* GetExtraStringsOffsets(const DexFile& dex_file, const uint8_t* verifier_deps, const uint32_t* dex_file_class_defs, /*out*/ uint32_t* number_of_extra_strings) { // The information for strings is right after dex_file_class_defs, 4-byte // aligned uint32_t end_of_assignability_types = dex_file_class_defs[dex_file.NumClassDefs()]; const uint8_t* strings_data_start = AlignUp(verifier_deps + end_of_assignability_types, sizeof(uint32_t)); // First entry is the number of extra strings for this dex file. *number_of_extra_strings = *reinterpret_cast(strings_data_start); // Then an array of offsets in `verifier_deps` for the extra strings. return reinterpret_cast(strings_data_start + sizeof(uint32_t)); } ClassStatus VdexFile::ComputeClassStatus(Thread* self, Handle cls) const { const DexFile& dex_file = cls->GetDexFile(); uint16_t class_def_index = cls->GetDexClassDefIndex(); // Find which dex file index from within the vdex file. uint32_t index = 0; for (; index < GetNumberOfDexFiles(); ++index) { if (dex_file.GetLocationChecksum() == GetLocationChecksum(index)) { break; } } DCHECK_NE(index, GetNumberOfDexFiles()); const uint8_t* verifier_deps = GetVerifierDepsData().data(); const uint32_t* dex_file_class_defs = GetDexFileClassDefs(verifier_deps, index); // Fetch type checks offsets. uint32_t class_def_offset = dex_file_class_defs[class_def_index]; if (class_def_offset == verifier::VerifierDeps::kNotVerifiedMarker) { // Return a status that needs re-verification. return ClassStatus::kResolved; } // End offset for this class's type checks. We know there is one and the loop // will terminate. uint32_t end_offset = verifier::VerifierDeps::kNotVerifiedMarker; for (uint32_t i = class_def_index + 1; i < dex_file.NumClassDefs() + 1; ++i) { end_offset = dex_file_class_defs[i]; if (end_offset != verifier::VerifierDeps::kNotVerifiedMarker) { break; } } DCHECK_NE(end_offset, verifier::VerifierDeps::kNotVerifiedMarker); uint32_t number_of_extra_strings = 0; // Offset where extra strings are stored. const uint32_t* extra_strings_offsets = GetExtraStringsOffsets(dex_file, verifier_deps, dex_file_class_defs, &number_of_extra_strings); // Loop over and perform each assignability check. StackHandleScope<3> hs(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle class_loader(hs.NewHandle(cls->GetClassLoader())); MutableHandle source(hs.NewHandle(nullptr)); MutableHandle destination(hs.NewHandle(nullptr)); const uint8_t* cursor = verifier_deps + class_def_offset; const uint8_t* end = verifier_deps + end_offset; while (cursor < end) { uint32_t destination_index; uint32_t source_index; if (UNLIKELY(!DecodeUnsignedLeb128Checked(&cursor, end, &destination_index) || !DecodeUnsignedLeb128Checked(&cursor, end, &source_index))) { // Error parsing the data, just return that we are not verified. return ClassStatus::kResolved; } const char* destination_desc = GetStringFromId(dex_file, dex::StringIndex(destination_index), number_of_extra_strings, extra_strings_offsets, verifier_deps); destination.Assign( FindClassAndClearException(class_linker, self, destination_desc, class_loader)); const char* source_desc = GetStringFromId(dex_file, dex::StringIndex(source_index), number_of_extra_strings, extra_strings_offsets, verifier_deps); source.Assign(FindClassAndClearException(class_linker, self, source_desc, class_loader)); if (destination == nullptr || source == nullptr) { // The interpreter / compiler can handle a missing class. continue; } DCHECK(destination->IsResolved() && source->IsResolved()); if (!destination->IsAssignableFrom(source.Get())) { VLOG(verifier) << "Vdex checking failed for " << cls->PrettyClass() << ": expected " << destination->PrettyClass() << " to be assignable from " << source->PrettyClass(); // An implicit assignability check is failing in the code, return that the // class is not verified. return ClassStatus::kResolved; } } return ClassStatus::kVerifiedNeedsAccessChecks; } } // namespace art