/* * Copyright (C) 2015 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 "oat_file_manager.h" #include #include #include #include #include "android-base/file.h" #include "android-base/stringprintf.h" #include "android-base/strings.h" #include "art_field-inl.h" #include "base/bit_vector-inl.h" #include "base/file_utils.h" #include "base/logging.h" // For VLOG. #include "base/mutex-inl.h" #include "base/sdk_version.h" #include "base/stl_util.h" #include "base/systrace.h" #include "class_linker.h" #include "class_loader_context.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_tracking_registrar.h" #include "gc/scoped_gc_critical_section.h" #include "gc/space/image_space.h" #include "handle_scope-inl.h" #include "jit/jit.h" #include "jni/java_vm_ext.h" #include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "oat_file.h" #include "oat_file_assistant.h" #include "obj_ptr-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" #include "thread_list.h" #include "thread_pool.h" #include "vdex_file.h" #include "verifier/verifier_deps.h" #include "well_known_classes.h" namespace art { using android::base::StringPrintf; // If true, we attempt to load the application image if it exists. static constexpr bool kEnableAppImage = true; const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr oat_file) { // Use class_linker vlog to match the log for dex file registration. VLOG(class_linker) << "Registered oat file " << oat_file->GetLocation(); PaletteNotifyOatFileLoaded(oat_file->GetLocation().c_str()); WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); CHECK(!only_use_system_oat_files_ || LocationIsTrusted(oat_file->GetLocation(), !Runtime::Current()->DenyArtApexDataFiles()) || !oat_file->IsExecutable()) << "Registering a non /system oat file: " << oat_file->GetLocation(); DCHECK(oat_file != nullptr); if (kIsDebugBuild) { CHECK(oat_files_.find(oat_file) == oat_files_.end()); for (const std::unique_ptr& existing : oat_files_) { CHECK_NE(oat_file.get(), existing.get()) << oat_file->GetLocation(); // Check that we don't have an oat file with the same address. Copies of the same oat file // should be loaded at different addresses. CHECK_NE(oat_file->Begin(), existing->Begin()) << "Oat file already mapped at that location"; } } const OatFile* ret = oat_file.get(); oat_files_.insert(std::move(oat_file)); return ret; } void OatFileManager::UnRegisterAndDeleteOatFile(const OatFile* oat_file) { WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); DCHECK(oat_file != nullptr); std::unique_ptr compare(oat_file); auto it = oat_files_.find(compare); CHECK(it != oat_files_.end()); oat_files_.erase(it); compare.release(); // NOLINT b/117926937 } const OatFile* OatFileManager::FindOpenedOatFileFromDexLocation( const std::string& dex_base_location) const { ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); for (const std::unique_ptr& oat_file : oat_files_) { const std::vector& oat_dex_files = oat_file->GetOatDexFiles(); for (const OatDexFile* oat_dex_file : oat_dex_files) { if (DexFileLoader::GetBaseLocation(oat_dex_file->GetDexFileLocation()) == dex_base_location) { return oat_file.get(); } } } return nullptr; } const OatFile* OatFileManager::FindOpenedOatFileFromOatLocation(const std::string& oat_location) const { ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); return FindOpenedOatFileFromOatLocationLocked(oat_location); } const OatFile* OatFileManager::FindOpenedOatFileFromOatLocationLocked( const std::string& oat_location) const { for (const std::unique_ptr& oat_file : oat_files_) { if (oat_file->GetLocation() == oat_location) { return oat_file.get(); } } return nullptr; } std::vector OatFileManager::GetBootOatFiles() const { std::vector image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); std::vector oat_files; oat_files.reserve(image_spaces.size()); for (gc::space::ImageSpace* image_space : image_spaces) { oat_files.push_back(image_space->GetOatFile()); } return oat_files; } OatFileManager::OatFileManager() : only_use_system_oat_files_(false) {} OatFileManager::~OatFileManager() { // Explicitly clear oat_files_ since the OatFile destructor calls back into OatFileManager for // UnRegisterOatFileLocation. oat_files_.clear(); } std::vector OatFileManager::RegisterImageOatFiles( const std::vector& spaces) { std::vector oat_files; oat_files.reserve(spaces.size()); for (gc::space::ImageSpace* space : spaces) { oat_files.push_back(RegisterOatFile(space->ReleaseOatFile())); } return oat_files; } bool OatFileManager::ShouldLoadAppImage(const OatFile* source_oat_file) const { Runtime* const runtime = Runtime::Current(); return kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable()); } std::vector> OatFileManager::OpenDexFilesFromOat( const char* dex_location, jobject class_loader, jobjectArray dex_elements, const OatFile** out_oat_file, std::vector* error_msgs) { ScopedTrace trace(StringPrintf("%s(%s)", __FUNCTION__, dex_location)); CHECK(dex_location != nullptr); CHECK(error_msgs != nullptr); // Verify we aren't holding the mutator lock, which could starve GC when // hitting the disk. Thread* const self = Thread::Current(); Locks::mutator_lock_->AssertNotHeld(self); Runtime* const runtime = Runtime::Current(); std::vector> dex_files; std::unique_ptr context( ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements)); // If the class_loader is null there's not much we can do. This happens if a dex files is loaded // directly with DexFile APIs instead of using class loaders. if (class_loader == nullptr) { LOG(WARNING) << "Opening an oat file without a class loader. " << "Are you using the deprecated DexFile APIs?"; } else if (context != nullptr) { OatFileAssistant oat_file_assistant(dex_location, kRuntimeISA, context.get(), runtime->GetOatFilesExecutable(), only_use_system_oat_files_); // Get the current optimization status for trace debugging. // Implementation detail note: GetOptimizationStatus will select the same // oat file as GetBestOatFile used below, and in doing so it already pre-populates // some OatFileAssistant internal fields. std::string odex_location; std::string compilation_filter; std::string compilation_reason; std::string odex_status; oat_file_assistant.GetOptimizationStatus( &odex_location, &compilation_filter, &compilation_reason, &odex_status); Runtime::Current()->GetAppInfo()->RegisterOdexStatus( dex_location, compilation_filter, compilation_reason, odex_status); ScopedTrace odex_loading(StringPrintf( "location=%s status=%s filter=%s reason=%s", odex_location.c_str(), odex_status.c_str(), compilation_filter.c_str(), compilation_reason.c_str())); // Proceed with oat file loading. std::unique_ptr oat_file(oat_file_assistant.GetBestOatFile().release()); VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()=" << (oat_file != nullptr ? oat_file->GetLocation() : "") << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")"; CHECK(oat_file == nullptr || odex_location == oat_file->GetLocation()) << "OatFileAssistant non-determinism in choosing best oat files. " << "optimization-status-location=" << odex_location << " best_oat_file-location=" << oat_file->GetLocation(); if (oat_file != nullptr) { // Load the dex files from the oat file. bool added_image_space = false; if (oat_file->IsExecutable()) { ScopedTrace app_image_timing("AppImage:Loading"); // We need to throw away the image space if we are debuggable but the oat-file source of the // image is not otherwise we might get classes with inlined methods or other such things. std::unique_ptr image_space; if (ShouldLoadAppImage(oat_file.get())) { image_space = oat_file_assistant.OpenImageSpace(oat_file.get()); } if (image_space != nullptr) { ScopedObjectAccess soa(self); StackHandleScope<1> hs(self); Handle h_loader( hs.NewHandle(soa.Decode(class_loader))); // Can not load app image without class loader. if (h_loader != nullptr) { std::string temp_error_msg; // Add image space has a race condition since other threads could be reading from the // spaces array. { ScopedThreadSuspension sts(self, kSuspended); gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseAddRemoveAppImageSpace, gc::kCollectorTypeAddRemoveAppImageSpace); ScopedSuspendAll ssa("Add image space"); runtime->GetHeap()->AddSpace(image_space.get()); } { ScopedTrace image_space_timing("Adding image space"); added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(), h_loader, /*out*/&dex_files, /*out*/&temp_error_msg); } if (added_image_space) { // Successfully added image space to heap, release the map so that it does not get // freed. image_space.release(); // NOLINT b/117926937 // Register for tracking. for (const auto& dex_file : dex_files) { dex::tracking::RegisterDexFile(dex_file.get()); } } else { LOG(INFO) << "Failed to add image file " << temp_error_msg; dex_files.clear(); { ScopedThreadSuspension sts(self, kSuspended); gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseAddRemoveAppImageSpace, gc::kCollectorTypeAddRemoveAppImageSpace); ScopedSuspendAll ssa("Remove image space"); runtime->GetHeap()->RemoveSpace(image_space.get()); } // Non-fatal, don't update error_msg. } } } } if (!added_image_space) { DCHECK(dex_files.empty()); if (oat_file->RequiresImage()) { LOG(WARNING) << "Loading " << oat_file->GetLocation() << "non-executable as it requires an image which we failed to load"; // file as non-executable. OatFileAssistant nonexecutable_oat_file_assistant(dex_location, kRuntimeISA, context.get(), /*load_executable=*/false, only_use_system_oat_files_); oat_file.reset(nonexecutable_oat_file_assistant.GetBestOatFile().release()); // The file could be deleted concurrently (for example background // dexopt, or secondary oat file being deleted by the app). if (oat_file == nullptr) { LOG(WARNING) << "Failed to reload oat file non-executable " << dex_location; } } if (oat_file != nullptr) { dex_files = oat_file_assistant.LoadDexFiles(*oat_file.get(), dex_location); // Register for tracking. for (const auto& dex_file : dex_files) { dex::tracking::RegisterDexFile(dex_file.get()); } } } if (dex_files.empty()) { ScopedTrace failed_to_open_dex_files("FailedToOpenDexFilesFromOat"); error_msgs->push_back("Failed to open dex files from " + odex_location); } else { // Opened dex files from an oat file, madvise them to their loaded state. for (const std::unique_ptr& dex_file : dex_files) { OatDexFile::MadviseDexFile(*dex_file, MadviseState::kMadviseStateAtLoad); } } if (oat_file != nullptr) { VLOG(class_linker) << "Registering " << oat_file->GetLocation(); *out_oat_file = RegisterOatFile(std::move(oat_file)); } } else { // oat_file == nullptr // Verify if any of the dex files being loaded is already in the class path. // If so, report an error with the current stack trace. // Most likely the developer didn't intend to do this because it will waste // performance and memory. if (oat_file_assistant.GetBestStatus() == OatFileAssistant::kOatContextOutOfDate) { std::set already_exists_in_classpath = context->CheckForDuplicateDexFiles(MakeNonOwningPointerVector(dex_files)); if (!already_exists_in_classpath.empty()) { ScopedTrace duplicate_dex_files("DuplicateDexFilesInContext"); auto duplicate_it = already_exists_in_classpath.begin(); std::string duplicates = (*duplicate_it)->GetLocation(); for (duplicate_it++ ; duplicate_it != already_exists_in_classpath.end(); duplicate_it++) { duplicates += "," + (*duplicate_it)->GetLocation(); } std::ostringstream out; out << "Trying to load dex files which is already loaded in the same ClassLoader " << "hierarchy.\n" << "This is a strong indication of bad ClassLoader construct which leads to poor " << "performance and wastes memory.\n" << "The list of duplicate dex files is: " << duplicates << "\n" << "The current class loader context is: " << context->EncodeContextForOatFile("") << "\n" << "Java stack trace:\n"; { ScopedObjectAccess soa(self); self->DumpJavaStack(out); } // We log this as an ERROR to stress the fact that this is most likely unintended. // Note that ART cannot do anything about it. It is up to the app to fix their logic. // Here we are trying to give a heads up on why the app might have performance issues. LOG(ERROR) << out.str(); } } } } // If we arrive here with an empty dex files list, it means we fail to load // it/them through an .oat file. if (dex_files.empty()) { std::string error_msg; static constexpr bool kVerifyChecksum = true; const ArtDexFileLoader dex_file_loader; if (!dex_file_loader.Open(dex_location, dex_location, Runtime::Current()->IsVerificationEnabled(), kVerifyChecksum, /*out*/ &error_msg, &dex_files)) { ScopedTrace fail_to_open_dex_from_apk("FailedToOpenDexFilesFromApk"); LOG(WARNING) << error_msg; error_msgs->push_back("Failed to open dex files from " + std::string(dex_location) + " because: " + error_msg); } } if (Runtime::Current()->GetJit() != nullptr) { Runtime::Current()->GetJit()->RegisterDexFiles(dex_files, class_loader); } // Now that we loaded the dex/odex files, notify the runtime. // Note that we do this everytime we load dex files. Runtime::Current()->NotifyDexFileLoaded(); return dex_files; } static std::vector GetDexFileHeaders(const std::vector& maps) { std::vector headers; headers.reserve(maps.size()); for (const MemMap& map : maps) { DCHECK(map.IsValid()); headers.push_back(reinterpret_cast(map.Begin())); } return headers; } std::vector> OatFileManager::OpenDexFilesFromOat( std::vector&& dex_mem_maps, jobject class_loader, jobjectArray dex_elements, const OatFile** out_oat_file, std::vector* error_msgs) { std::vector> dex_files = OpenDexFilesFromOat_Impl( std::move(dex_mem_maps), class_loader, dex_elements, out_oat_file, error_msgs); if (error_msgs->empty()) { // Remove write permission from DexFile pages. We do this at the end because // OatFile assigns OatDexFile pointer in the DexFile objects. for (std::unique_ptr& dex_file : dex_files) { if (!dex_file->DisableWrite()) { error_msgs->push_back("Failed to make dex file " + dex_file->GetLocation() + " read-only"); } } } if (!error_msgs->empty()) { return std::vector>(); } return dex_files; } std::vector> OatFileManager::OpenDexFilesFromOat_Impl( std::vector&& dex_mem_maps, jobject class_loader, jobjectArray dex_elements, const OatFile** out_oat_file, std::vector* error_msgs) { ScopedTrace trace(__FUNCTION__); std::string error_msg; DCHECK(error_msgs != nullptr); // Extract dex file headers from `dex_mem_maps`. const std::vector dex_headers = GetDexFileHeaders(dex_mem_maps); // Determine dex/vdex locations and the combined location checksum. std::string dex_location; std::string vdex_path; bool has_vdex = OatFileAssistant::AnonymousDexVdexLocation(dex_headers, kRuntimeISA, &dex_location, &vdex_path); // Attempt to open an existing vdex and check dex file checksums match. std::unique_ptr vdex_file = nullptr; if (has_vdex && OS::FileExists(vdex_path.c_str())) { vdex_file = VdexFile::Open(vdex_path, /* writable= */ false, /* low_4gb= */ false, /* unquicken= */ false, &error_msg); if (vdex_file == nullptr) { LOG(WARNING) << "Failed to open vdex " << vdex_path << ": " << error_msg; } else if (!vdex_file->MatchesDexFileChecksums(dex_headers)) { LOG(WARNING) << "Failed to open vdex " << vdex_path << ": dex file checksum mismatch"; vdex_file.reset(nullptr); } } // Load dex files. Skip structural dex file verification if vdex was found // and dex checksums matched. std::vector> dex_files; for (size_t i = 0; i < dex_mem_maps.size(); ++i) { static constexpr bool kVerifyChecksum = true; const ArtDexFileLoader dex_file_loader; std::unique_ptr dex_file(dex_file_loader.Open( DexFileLoader::GetMultiDexLocation(i, dex_location.c_str()), dex_headers[i]->checksum_, std::move(dex_mem_maps[i]), /* verify= */ (vdex_file == nullptr) && Runtime::Current()->IsVerificationEnabled(), kVerifyChecksum, &error_msg)); if (dex_file != nullptr) { dex::tracking::RegisterDexFile(dex_file.get()); // Register for tracking. dex_files.push_back(std::move(dex_file)); } else { error_msgs->push_back("Failed to open dex files from memory: " + error_msg); } } // Check if we should proceed to creating an OatFile instance backed by the vdex. // We need: (a) an existing vdex, (b) class loader (can be null if invoked via reflection), // and (c) no errors during dex file loading. if (vdex_file == nullptr || class_loader == nullptr || !error_msgs->empty()) { return dex_files; } // Attempt to create a class loader context, check OpenDexFiles succeeds (prerequisite // for using the context later). std::unique_ptr context = ClassLoaderContext::CreateContextForClassLoader( class_loader, dex_elements); if (context == nullptr) { LOG(ERROR) << "Could not create class loader context for " << vdex_path; return dex_files; } DCHECK(context->OpenDexFiles()) << "Context created from already opened dex files should not attempt to open again"; // Initialize an OatFile instance backed by the loaded vdex. std::unique_ptr oat_file(OatFile::OpenFromVdex(MakeNonOwningPointerVector(dex_files), std::move(vdex_file), dex_location)); if (oat_file != nullptr) { VLOG(class_linker) << "Registering " << oat_file->GetLocation(); *out_oat_file = RegisterOatFile(std::move(oat_file)); } return dex_files; } // Check how many vdex files exist in the same directory as the vdex file we are about // to write. If more than or equal to kAnonymousVdexCacheSize, unlink the least // recently used one(s) (according to stat-reported atime). static bool UnlinkLeastRecentlyUsedVdexIfNeeded(const std::string& vdex_path_to_add, std::string* error_msg) { std::string basename = android::base::Basename(vdex_path_to_add); if (!OatFileAssistant::IsAnonymousVdexBasename(basename)) { // File is not for in memory dex files. return true; } if (OS::FileExists(vdex_path_to_add.c_str())) { // File already exists and will be overwritten. // This will not change the number of entries in the cache. return true; } auto last_slash = vdex_path_to_add.rfind('/'); CHECK(last_slash != std::string::npos); std::string vdex_dir = vdex_path_to_add.substr(0, last_slash + 1); if (!OS::DirectoryExists(vdex_dir.c_str())) { // Folder does not exist yet. Cache has zero entries. return true; } std::vector> cache; DIR* c_dir = opendir(vdex_dir.c_str()); if (c_dir == nullptr) { *error_msg = "Unable to open " + vdex_dir + " to delete unused vdex files"; return false; } for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) { if (de->d_type != DT_REG) { continue; } basename = de->d_name; if (!OatFileAssistant::IsAnonymousVdexBasename(basename)) { continue; } std::string fullname = vdex_dir + basename; struct stat s; int rc = TEMP_FAILURE_RETRY(stat(fullname.c_str(), &s)); if (rc == -1) { *error_msg = "Failed to stat() anonymous vdex file " + fullname; return false; } cache.push_back(std::make_pair(s.st_atime, fullname)); } CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory."; if (cache.size() < OatFileManager::kAnonymousVdexCacheSize) { return true; } std::sort(cache.begin(), cache.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); for (size_t i = OatFileManager::kAnonymousVdexCacheSize - 1; i < cache.size(); ++i) { if (unlink(cache[i].second.c_str()) != 0) { *error_msg = "Could not unlink anonymous vdex file " + cache[i].second; return false; } } return true; } class BackgroundVerificationTask final : public Task { public: BackgroundVerificationTask(const std::vector& dex_files, jobject class_loader, const std::string& vdex_path) : dex_files_(dex_files), vdex_path_(vdex_path) { Thread* const self = Thread::Current(); ScopedObjectAccess soa(self); // Create a global ref for `class_loader` because it will be accessed from a different thread. class_loader_ = soa.Vm()->AddGlobalRef(self, soa.Decode(class_loader)); CHECK(class_loader_ != nullptr); } ~BackgroundVerificationTask() { Thread* const self = Thread::Current(); ScopedObjectAccess soa(self); soa.Vm()->DeleteGlobalRef(self, class_loader_); } void Run(Thread* self) override { std::string error_msg; ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); verifier::VerifierDeps verifier_deps(dex_files_); // Iterate over all classes and verify them. for (const DexFile* dex_file : dex_files_) { for (uint32_t cdef_idx = 0; cdef_idx < dex_file->NumClassDefs(); cdef_idx++) { const dex::ClassDef& class_def = dex_file->GetClassDef(cdef_idx); // Take handles inside the loop. The background verification is low priority // and we want to minimize the risk of blocking anyone else. ScopedObjectAccess soa(self); StackHandleScope<2> hs(self); Handle h_loader(hs.NewHandle( soa.Decode(class_loader_))); Handle h_class(hs.NewHandle(class_linker->FindClass( self, dex_file->GetClassDescriptor(class_def), h_loader))); if (h_class == nullptr) { CHECK(self->IsExceptionPending()); self->ClearException(); continue; } if (&h_class->GetDexFile() != dex_file) { // There is a different class in the class path or a parent class loader // with the same descriptor. This `h_class` is not resolvable, skip it. continue; } CHECK(h_class->IsResolved()) << h_class->PrettyDescriptor(); class_linker->VerifyClass(self, &verifier_deps, h_class); if (h_class->IsErroneous()) { // ClassLinker::VerifyClass throws, which isn't useful here. CHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); } CHECK(h_class->IsVerified() || h_class->IsErroneous()) << h_class->PrettyDescriptor() << ": state=" << h_class->GetStatus(); if (h_class->IsVerified()) { verifier_deps.RecordClassVerified(*dex_file, class_def); } } } // Delete old vdex files if there are too many in the folder. if (!UnlinkLeastRecentlyUsedVdexIfNeeded(vdex_path_, &error_msg)) { LOG(ERROR) << "Could not unlink old vdex files " << vdex_path_ << ": " << error_msg; return; } // Construct a vdex file and write `verifier_deps` into it. if (!VdexFile::WriteToDisk(vdex_path_, dex_files_, verifier_deps, &error_msg)) { LOG(ERROR) << "Could not write anonymous vdex " << vdex_path_ << ": " << error_msg; return; } } void Finalize() override { delete this; } private: const std::vector dex_files_; jobject class_loader_; const std::string vdex_path_; DISALLOW_COPY_AND_ASSIGN(BackgroundVerificationTask); }; void OatFileManager::RunBackgroundVerification(const std::vector& dex_files, jobject class_loader) { Runtime* const runtime = Runtime::Current(); Thread* const self = Thread::Current(); if (runtime->IsJavaDebuggable()) { // Threads created by ThreadPool ("runtime threads") are not allowed to load // classes when debuggable to match class-initialization semantics // expectations. Do not verify in the background. return; } if (!IsSdkVersionSetAndAtLeast(runtime->GetTargetSdkVersion(), SdkVersion::kQ)) { // Do not run for legacy apps as they may depend on the previous class loader behaviour. return; } if (runtime->IsShuttingDown(self)) { // Not allowed to create new threads during runtime shutdown. return; } if (dex_files.size() < 1) { // Nothing to verify. return; } std::string dex_location = dex_files[0]->GetLocation(); const std::string& data_dir = Runtime::Current()->GetProcessDataDirectory(); if (!android::base::StartsWith(dex_location, data_dir)) { // For now, we only run background verification for secondary dex files. // Running it for primary or split APKs could have some undesirable // side-effects, like overloading the device on app startup. return; } std::string error_msg; std::string odex_filename; if (!OatFileAssistant::DexLocationToOdexFilename(dex_location, kRuntimeISA, &odex_filename, &error_msg)) { LOG(WARNING) << "Could not get odex filename for " << dex_location << ": " << error_msg; return; } if (LocationIsOnArtApexData(odex_filename) && Runtime::Current()->DenyArtApexDataFiles()) { // Ignore vdex file associated with this odex file as the odex file is not trustworthy. return; } { WriterMutexLock mu(self, *Locks::oat_file_manager_lock_); if (verification_thread_pool_ == nullptr) { verification_thread_pool_.reset( new ThreadPool("Verification thread pool", /* num_threads= */ 1)); verification_thread_pool_->StartWorkers(self); } } verification_thread_pool_->AddTask(self, new BackgroundVerificationTask( dex_files, class_loader, GetVdexFilename(odex_filename))); } void OatFileManager::WaitForWorkersToBeCreated() { DCHECK(!Runtime::Current()->IsShuttingDown(Thread::Current())) << "Cannot create new threads during runtime shutdown"; if (verification_thread_pool_ != nullptr) { verification_thread_pool_->WaitForWorkersToBeCreated(); } } void OatFileManager::DeleteThreadPool() { verification_thread_pool_.reset(nullptr); } void OatFileManager::WaitForBackgroundVerificationTasks() { if (verification_thread_pool_ != nullptr) { Thread* const self = Thread::Current(); verification_thread_pool_->WaitForWorkersToBeCreated(); verification_thread_pool_->Wait(self, /* do_work= */ true, /* may_hold_locks= */ false); } } void OatFileManager::SetOnlyUseTrustedOatFiles() { ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); // Make sure all files that were loaded up to this point are on /system. // Skip the image files as they can encode locations that don't exist (eg not // containing the arch in the path, or for JIT zygote /nonx/existent). std::vector boot_vector = GetBootOatFiles(); std::unordered_set boot_set(boot_vector.begin(), boot_vector.end()); for (const std::unique_ptr& oat_file : oat_files_) { if (boot_set.find(oat_file.get()) == boot_set.end()) { // This method is called during runtime initialization before we can call // Runtime::Current()->DenyArtApexDataFiles(). Since we don't want to fail hard if // the ART APEX data files are untrusted, just treat them as trusted for the check here. const bool trust_art_apex_data_files = true; if (!LocationIsTrusted(oat_file->GetLocation(), trust_art_apex_data_files)) { // When the file is not in a trusted location, we check whether the oat file has any // AOT or DEX code. It is a fatal error if it has. if (CompilerFilter::IsAotCompilationEnabled(oat_file->GetCompilerFilter()) || oat_file->ContainsDexCode()) { LOG(FATAL) << "Executing untrusted code from " << oat_file->GetLocation(); } } } } only_use_system_oat_files_ = true; } void OatFileManager::DumpForSigQuit(std::ostream& os) { ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); std::vector boot_oat_files = GetBootOatFiles(); for (const std::unique_ptr& oat_file : oat_files_) { if (ContainsElement(boot_oat_files, oat_file.get())) { continue; } os << oat_file->GetLocation() << ": " << oat_file->GetCompilerFilter() << "\n"; } } } // namespace art