diff options
Diffstat (limited to 'cmds/installd/dexopt.cpp')
| -rw-r--r-- | cmds/installd/dexopt.cpp | 177 |
1 files changed, 157 insertions, 20 deletions
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index 210f977585..7bb9bef658 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -15,8 +15,8 @@ */ #define LOG_TAG "installd" -#include <array> #include <fcntl.h> +#include <signal.h> #include <stdlib.h> #include <string.h> #include <sys/capability.h> @@ -28,10 +28,14 @@ #include <sys/wait.h> #include <unistd.h> +#include <array> #include <iomanip> +#include <mutex> +#include <unordered_set> #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/no_destructor.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> @@ -47,6 +51,7 @@ #include <selinux/android.h> #include <server_configurable_flags/get_flags.h> #include <system/thread_defs.h> +#include <utils/Mutex.h> #include "dexopt.h" #include "dexopt_return_codes.h" @@ -69,6 +74,76 @@ using android::base::StringPrintf; using android::base::WriteFully; using android::base::unique_fd; +namespace { + +class DexOptStatus { + public: + // Check if dexopt is cancelled and fork if it is not cancelled. + // cancelled is set to true if cancelled. Otherwise it will be set to false. + // If it is not cancelled, it will return the return value of fork() call. + // If cancelled, fork will not happen and it will return -1. + pid_t check_cancellation_and_fork(/* out */ bool *cancelled) { + std::lock_guard<std::mutex> lock(dexopt_lock_); + if (dexopt_blocked_) { + *cancelled = true; + return -1; + } + pid_t pid = fork(); + *cancelled = false; + if (pid > 0) { // parent + dexopt_pids_.insert(pid); + } + return pid; + } + + // Returns true if pid was killed (is in killed list). It could have finished if killing + // happened after the process is finished. + bool check_if_killed_and_remove_dexopt_pid(pid_t pid) { + std::lock_guard<std::mutex> lock(dexopt_lock_); + dexopt_pids_.erase(pid); + if (dexopt_killed_pids_.erase(pid) == 1) { + return true; + } + return false; + } + + // Tells whether dexopt is blocked or not. + bool is_dexopt_blocked() { + std::lock_guard<std::mutex> lock(dexopt_lock_); + return dexopt_blocked_; + } + + // Enable or disable dexopt blocking. + void control_dexopt_blocking(bool block) { + std::lock_guard<std::mutex> lock(dexopt_lock_); + dexopt_blocked_ = block; + if (!block) { + return; + } + // Blocked, also kill currently running tasks + for (auto pid : dexopt_pids_) { + LOG(INFO) << "control_dexopt_blocking kill pid:" << pid; + kill(pid, SIGKILL); + dexopt_killed_pids_.insert(pid); + } + dexopt_pids_.clear(); + } + + private: + std::mutex dexopt_lock_; + // when true, dexopt is blocked and will not run. + bool dexopt_blocked_ GUARDED_BY(dexopt_lock_) = false; + // PIDs of child process while runinng dexopt. + // If the child process is finished, it should be removed. + std::unordered_set<pid_t> dexopt_pids_ GUARDED_BY(dexopt_lock_); + // PIDs of child processes killed by cancellation. + std::unordered_set<pid_t> dexopt_killed_pids_ GUARDED_BY(dexopt_lock_); +}; + +android::base::NoDestructor<DexOptStatus> dexopt_status_; + +} // namespace + namespace android { namespace installd { @@ -1525,23 +1600,46 @@ static std::string join_fds(const std::vector<unique_fd>& fds) { return ss.str(); } +void control_dexopt_blocking(bool block) { + dexopt_status_->control_dexopt_blocking(block); +} + +bool is_dexopt_blocked() { + return dexopt_status_->is_dexopt_blocked(); +} + +enum SecondaryDexOptProcessResult { + kSecondaryDexOptProcessOk = 0, + kSecondaryDexOptProcessCancelled = 1, + kSecondaryDexOptProcessError = 2 +}; + // Processes the dex_path as a secondary dex files and return true if the path dex file should -// be compiled. Returns false for errors (logged) or true if the secondary dex path was process -// successfully. -// When returning true, the output parameters will be: +// be compiled. +// Returns: kSecondaryDexOptProcessError for errors (logged). +// kSecondaryDexOptProcessOk if the secondary dex path was process successfully. +// kSecondaryDexOptProcessCancelled if the processing was cancelled. +// +// When returning kSecondaryDexOptProcessOk, the output parameters will be: // - is_public_out: whether or not the oat file should not be made public // - dexopt_needed_out: valid OatFileAsssitant::DexOptNeeded // - oat_dir_out: the oat dir path where the oat file should be stored -static bool process_secondary_dex_dexopt(const std::string& dex_path, const char* pkgname, - int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set, - const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out, - std::string* oat_dir_out, bool downgrade, const char* class_loader_context, - const std::vector<std::string>& context_dex_paths, /* out */ std::string* error_msg) { +static SecondaryDexOptProcessResult process_secondary_dex_dexopt(const std::string& dex_path, + const char* pkgname, int dexopt_flags, const char* volume_uuid, int uid, + const char* instruction_set, const char* compiler_filter, bool* is_public_out, + int* dexopt_needed_out, std::string* oat_dir_out, bool downgrade, + const char* class_loader_context, const std::vector<std::string>& context_dex_paths, + /* out */ std::string* error_msg) { LOG(DEBUG) << "Processing secondary dex path " << dex_path; + + if (dexopt_status_->is_dexopt_blocked()) { + return kSecondaryDexOptProcessCancelled; + } + int storage_flag; if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag, error_msg)) { LOG(ERROR) << *error_msg; - return false; + return kSecondaryDexOptProcessError; } // Compute the oat dir as it's not easy to extract it from the child computation. char oat_path[PKG_PATH_MAX]; @@ -1550,11 +1648,15 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char if (!create_secondary_dex_oat_layout( dex_path, instruction_set, oat_dir, oat_isa_dir, oat_path, error_msg)) { LOG(ERROR) << "Could not create secondary odex layout: " << *error_msg; - return false; + return kSecondaryDexOptProcessError; } oat_dir_out->assign(oat_dir); - pid_t pid = fork(); + bool cancelled = false; + pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled); + if (cancelled) { + return kSecondaryDexOptProcessCancelled; + } if (pid == 0) { // child -- drop privileges before continuing. drop_capabilities(uid); @@ -1623,12 +1725,17 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char /* parent */ int result = wait_child(pid); + cancelled = dexopt_status_->check_if_killed_and_remove_dexopt_pid(pid); if (!WIFEXITED(result)) { + if ((WTERMSIG(result) == SIGKILL) && cancelled) { + LOG(INFO) << "dexoptanalyzer cancelled for path:" << dex_path; + return kSecondaryDexOptProcessCancelled; + } *error_msg = StringPrintf("dexoptanalyzer failed for path %s: 0x%04x", dex_path.c_str(), result); LOG(ERROR) << *error_msg; - return false; + return kSecondaryDexOptProcessError; } result = WEXITSTATUS(result); // Check that we successfully executed dexoptanalyzer. @@ -1656,7 +1763,7 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char // It is ok to check this flag outside in the parent process. *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) && is_file_public(dex_path); - return success; + return success ? kSecondaryDexOptProcessOk : kSecondaryDexOptProcessError; } static std::string format_dexopt_error(int status, const char* dex_path) { @@ -1670,17 +1777,29 @@ static std::string format_dexopt_error(int status, const char* dex_path) { return StringPrintf("Dex2oat invocation for %s failed with 0x%04x", dex_path, status); } + int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* instruction_set, int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter, const char* volume_uuid, const char* class_loader_context, const char* se_info, bool downgrade, int target_sdk_version, const char* profile_name, - const char* dex_metadata_path, const char* compilation_reason, std::string* error_msg) { + const char* dex_metadata_path, const char* compilation_reason, std::string* error_msg, + /* out */ bool* completed) { CHECK(pkgname != nullptr); CHECK(pkgname[0] != 0); CHECK(error_msg != nullptr); CHECK_EQ(dexopt_flags & ~DEXOPT_MASK, 0) << "dexopt flags contains unknown fields: " << dexopt_flags; + bool local_completed; // local placeholder for nullptr case + if (completed == nullptr) { + completed = &local_completed; + } + *completed = true; + if (dexopt_status_->is_dexopt_blocked()) { + *completed = false; + return 0; + } + if (!validate_dex_path_size(dex_path)) { *error_msg = StringPrintf("Failed to validate %s", dex_path); return -1; @@ -1712,14 +1831,19 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins *error_msg = "Failed acquiring context dex paths"; return -1; // We had an error, logged in the process method. } - - if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid, - instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str, - downgrade, class_loader_context, context_dex_paths, error_msg)) { + SecondaryDexOptProcessResult sec_dex_result = process_secondary_dex_dexopt(dex_path, + pkgname, dexopt_flags, volume_uuid, uid,instruction_set, compiler_filter, + &is_public, &dexopt_needed, &oat_dir_str, downgrade, class_loader_context, + context_dex_paths, error_msg); + if (sec_dex_result == kSecondaryDexOptProcessOk) { oat_dir = oat_dir_str.c_str(); if (dexopt_needed == NO_DEXOPT_NEEDED) { return 0; // Nothing to do, report success. } + } else if (sec_dex_result == kSecondaryDexOptProcessCancelled) { + // cancelled, not an error. + *completed = false; + return 0; } else { if (error_msg->empty()) { // TODO: Make this a CHECK. *error_msg = "Failed processing secondary."; @@ -1849,7 +1973,11 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins use_jitzygote_image, compilation_reason); - pid_t pid = fork(); + bool cancelled = false; + pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled); + if (cancelled) { + return 0; + } if (pid == 0) { // Need to set schedpolicy before dropping privileges // for cgroup migration. See details at b/175178520. @@ -1867,9 +1995,16 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins runner.Exec(DexoptReturnCodes::kDex2oatExec); } else { int res = wait_child(pid); + bool cancelled = dexopt_status_->check_if_killed_and_remove_dexopt_pid(pid); if (res == 0) { LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' (success) ---"; } else { + if ((WTERMSIG(res) == SIGKILL) && cancelled) { + LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- cancelled"; + // cancelled, not an error + *completed = false; + return 0; + } LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- status=0x" << std::hex << std::setw(4) << res << ", process failed"; *error_msg = format_dexopt_error(res, dex_path); @@ -1877,12 +2012,14 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins } } + // TODO(b/156537504) Implement SWAP of completed files // We've been successful, don't delete output. out_oat.DisableCleanup(); out_vdex.DisableCleanup(); out_image.DisableCleanup(); reference_profile.DisableCleanup(); + *completed = true; return 0; } |