diff options
| author | 2017-11-14 02:54:59 +0000 | |
|---|---|---|
| committer | 2017-11-14 02:54:59 +0000 | |
| commit | 01d0be16bda54e09ab5d0f3fa5199b27dea4ae50 (patch) | |
| tree | 6e8fa8f52feb2705123ea899e07ba198ca373620 | |
| parent | 79ce1748ea4d3abc21929045da8ea337abadfd52 (diff) | |
| parent | 7d7654646abb84bbae6b245de270ac087cdc8a95 (diff) | |
Merge "Move secondary dex files processing in the app process"
| -rw-r--r-- | cmds/installd/dexopt.cpp | 415 | ||||
| -rw-r--r-- | cmds/installd/tests/Android.bp | 20 | ||||
| -rw-r--r-- | cmds/installd/tests/installd_dexopt_test.cpp | 359 | ||||
| -rw-r--r-- | cmds/installd/tests/installd_utils_test.cpp | 3 | ||||
| -rw-r--r-- | cmds/installd/tests/test_utils.h | 107 | ||||
| -rw-r--r-- | cmds/installd/utils.cpp | 30 | ||||
| -rw-r--r-- | cmds/installd/utils.h | 4 |
7 files changed, 763 insertions, 175 deletions
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index 20b7728921..af2c527056 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -1385,6 +1385,8 @@ bool maybe_open_oat_and_vdex_file(const std::string& apk_path, oat_dir.c_str(), is_secondary_dex, oat_path)) { + LOG(ERROR) << "Could not create oat out path for " + << apk_path << " with oat dir " << oat_dir; return false; } oat_file_fd->reset(open(oat_path, O_RDONLY)); @@ -1489,7 +1491,7 @@ static void exec_dexoptanalyzer(const std::string& dex_file, int vdex_fd, int oa // Prepares the oat dir for the secondary dex files. static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid, - const char* instruction_set, std::string* oat_dir_out) { + const char* instruction_set) { unsigned long dirIndex = dex_path.rfind('/'); if (dirIndex == std::string::npos) { LOG(ERROR ) << "Unexpected dir structure for secondary dex " << dex_path; @@ -1506,10 +1508,8 @@ static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid, char oat_dir[PKG_PATH_MAX]; snprintf(oat_dir, PKG_PATH_MAX, "%s/oat", dex_dir.c_str()); - oat_dir_out->assign(oat_dir); - // Create oat/isa output directory. - if (prepare_app_cache_dir(*oat_dir_out, instruction_set, oat_dir_mode, uid, uid) != 0) { + if (prepare_app_cache_dir(oat_dir, instruction_set, oat_dir_mode, uid, uid) != 0) { LOG(ERROR) << "Could not prepare oat/isa dir for secondary dex: " << dex_path; return false; } @@ -1517,166 +1517,236 @@ static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid, return true; } -static int constexpr DEXOPTANALYZER_BIN_EXEC_ERROR = 200; +// Return codes for identifying the reason why dexoptanalyzer was not invoked when processing +// secondary dex files. This return codes are returned by the child process created for +// analyzing secondary dex files in process_secondary_dex_dexopt. + +// The dexoptanalyzer was not invoked because of validation or IO errors. +static int constexpr SECONDARY_DEX_DEXOPTANALYZER_SKIPPED = 200; +// The dexoptanalyzer was not invoked because the dex file does not exist anymore. +static int constexpr SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE = 201; -// Verifies the result of dexoptanalyzer executed for the apk_path. +// Verifies the result of analyzing secondary dex files from process_secondary_dex_dexopt. // If the result is valid returns true and sets dexopt_needed_out to a valid value. // Returns false for errors or unexpected result values. -static bool process_dexoptanalyzer_result(const std::string& dex_path, int result, +// The result is expected to be either one of SECONDARY_DEX_* codes or a valid exit code +// of dexoptanalyzer. +static bool process_secondary_dexoptanalyzer_result(const std::string& dex_path, int result, int* dexopt_needed_out) { // The result values are defined in dexoptanalyzer. switch (result) { - case 0: // no_dexopt_needed + case 0: // dexoptanalyzer: no_dexopt_needed *dexopt_needed_out = NO_DEXOPT_NEEDED; return true; - case 1: // dex2oat_from_scratch + case 1: // dexoptanalyzer: dex2oat_from_scratch *dexopt_needed_out = DEX2OAT_FROM_SCRATCH; return true; - case 5: // dex2oat_for_bootimage_odex + case 5: // dexoptanalyzer: dex2oat_for_bootimage_odex *dexopt_needed_out = -DEX2OAT_FOR_BOOT_IMAGE; return true; - case 6: // dex2oat_for_filter_odex + case 6: // dexoptanalyzer: dex2oat_for_filter_odex *dexopt_needed_out = -DEX2OAT_FOR_FILTER; return true; - case 7: // dex2oat_for_relocation_odex + case 7: // dexoptanalyzer: dex2oat_for_relocation_odex *dexopt_needed_out = -DEX2OAT_FOR_RELOCATION; return true; - case 2: // dex2oat_for_bootimage_oat - case 3: // dex2oat_for_filter_oat - case 4: // dex2oat_for_relocation_oat + case 2: // dexoptanalyzer: dex2oat_for_bootimage_oat + case 3: // dexoptanalyzer: dex2oat_for_filter_oat + case 4: // dexoptanalyzer: dex2oat_for_relocation_oat LOG(ERROR) << "Dexoptnalyzer return the status of an oat file." << " Expected odex file status for secondary dex " << dex_path << " : dexoptanalyzer result=" << result; return false; + case SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE: + // If the file does not exist there's no need for dexopt. + *dexopt_needed_out = NO_DEXOPT_NEEDED; + return true; + case SECONDARY_DEX_DEXOPTANALYZER_SKIPPED: + return false; default: - LOG(ERROR) << "Unexpected result for dexoptanalyzer " << dex_path - << " exec_dexoptanalyzer result=" << result; + LOG(ERROR) << "Unexpected result from analyzing secondary dex " << dex_path + << " result=" << result; return false; } } -// 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: -// - 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 -// - dex_path_out: the real path of the dex file -static bool process_secondary_dex_dexopt(const char* original_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, std::string* dex_path_out, bool downgrade, - const char* class_loader_context) { - int storage_flag; +enum SecondaryDexAccess { + kSecondaryDexAccessReadOk = 0, + kSecondaryDexAccessDoesNotExist = 1, + kSecondaryDexAccessPermissionError = 2, + kSecondaryDexAccessIOError = 3 +}; + +static SecondaryDexAccess check_secondary_dex_access(const std::string& dex_path) { + // Check if the path exists and can be read. If not, there's nothing to do. + if (access(dex_path.c_str(), R_OK) == 0) { + return kSecondaryDexAccessReadOk; + } else { + if (errno == ENOENT) { + LOG(INFO) << "Secondary dex does not exist: " << dex_path; + return kSecondaryDexAccessDoesNotExist; + } else { + PLOG(ERROR) << "Could not access secondary dex " << dex_path; + return errno == EACCES + ? kSecondaryDexAccessPermissionError + : kSecondaryDexAccessIOError; + } + } +} + +static bool is_file_public(const std::string& filename) { + struct stat file_stat; + if (stat(filename.c_str(), &file_stat) == 0) { + return (file_stat.st_mode & S_IROTH) != 0; + } + return false; +} +// Create the oat file structure for the secondary dex 'dex_path' and assign +// the individual path component to the 'out_' parameters. +static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa, + char* out_oat_dir, char* out_oat_isa_dir, char* out_oat_path) { + size_t dirIndex = dex_path.rfind('/'); + if (dirIndex == std::string::npos) { + LOG(ERROR) << "Unexpected dir structure for dex file " << dex_path; + return false; + } + // TODO(calin): we have similar computations in at lest 3 other places + // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by + // using string append. + std::string apk_dir = dex_path.substr(0, dirIndex); + snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str()); + snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str()); + + if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir, + /*is_secondary_dex*/true, out_oat_path)) { + LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path; + return false; + } + return true; +} + +// Validate that the dexopt_flags contain a valid storage flag and convert that to an installd +// recognized storage flags (FLAG_STORAGE_CE or FLAG_STORAGE_DE). +static bool validate_dexopt_storage_flags(int dexopt_flags, int* out_storage_flag) { if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) { - storage_flag = FLAG_STORAGE_CE; + *out_storage_flag = FLAG_STORAGE_CE; if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) { LOG(ERROR) << "Ambiguous secondary dex storage flag. Both, CE and DE, flags are set"; return false; } } else if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) { - storage_flag = FLAG_STORAGE_DE; + *out_storage_flag = FLAG_STORAGE_DE; } else { LOG(ERROR) << "Secondary dex storage flag must be set"; return false; } + return true; +} - { - // As opposed to the primary apk, secondary dex files might contain symlinks. - // Resolve the path before passing it to the validate method to - // make sure the verification is done on the real location. - UniqueCPtr<char> dex_real_path_cstr(realpath(original_dex_path, nullptr)); - if (dex_real_path_cstr == nullptr) { - PLOG(ERROR) << "Could not get the real path of the secondary dex file " - << original_dex_path; - return false; - } else { - dex_path_out->assign(dex_real_path_cstr.get()); - } - } - const std::string& dex_path = *dex_path_out; - if (!validate_dex_path_size(dex_path)) { +// 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: +// - 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) { + LOG(DEBUG) << "Processing secondary dex path " << dex_path; + int storage_flag; + if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag)) { return false; } - if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) { - LOG(ERROR) << "Could not validate secondary dex path " << dex_path; + // Compute the oat dir as it's not easy to extract it from the child computation. + char oat_path[PKG_PATH_MAX]; + char oat_dir[PKG_PATH_MAX]; + char oat_isa_dir[PKG_PATH_MAX]; + if (!create_secondary_dex_oat_layout( + dex_path, instruction_set, oat_dir, oat_isa_dir, oat_path)) { + LOG(ERROR) << "Could not create secondary odex layout: " << dex_path; return false; } + oat_dir_out->assign(oat_dir); - // Check if the path exist. If not, there's nothing to do. - struct stat dex_path_stat; - if (stat(dex_path.c_str(), &dex_path_stat) != 0) { - if (errno == ENOENT) { - // Secondary dex files might be deleted any time by the app. - // Nothing to do if that's the case - ALOGV("Secondary dex does not exist %s", dex_path.c_str()); - return NO_DEXOPT_NEEDED; - } else { - PLOG(ERROR) << "Could not access secondary dex " << dex_path; + pid_t pid = fork(); + if (pid == 0) { + // child -- drop privileges before continuing. + drop_capabilities(uid); + + // Validate the path structure. + if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) { + LOG(ERROR) << "Could not validate secondary dex path " << dex_path; + _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED); } - } - // Check if we should make the oat file public. - // Note that if the dex file is not public the compiled code cannot be made public. - *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) && - ((dex_path_stat.st_mode & S_IROTH) != 0); + // Open the dex file. + unique_fd zip_fd; + zip_fd.reset(open(dex_path.c_str(), O_RDONLY)); + if (zip_fd.get() < 0) { + if (errno == ENOENT) { + _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE); + } else { + _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED); + } + } - // Prepare the oat directories. - if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set, oat_dir_out)) { - return false; - } + // Prepare the oat directories. + if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set)) { + _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED); + } - // Analyze profiles. - bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true); + // Open the vdex/oat files if any. + unique_fd oat_file_fd; + unique_fd vdex_file_fd; + if (!maybe_open_oat_and_vdex_file(dex_path, + *oat_dir_out, + instruction_set, + true /* is_secondary_dex */, + &oat_file_fd, + &vdex_file_fd)) { + _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED); + } - unique_fd oat_file_fd; - unique_fd vdex_file_fd; - unique_fd zip_fd; - zip_fd.reset(open(dex_path.c_str(), O_RDONLY)); - if (zip_fd.get() < 0) { - PLOG(ERROR) << "installd cannot open " << dex_path << " for input during dexopt"; - return false; - } - if (!maybe_open_oat_and_vdex_file(dex_path, - *oat_dir_out, - instruction_set, - true /* is_secondary_dex */, - &oat_file_fd, - &vdex_file_fd)) { - return false; - } + // Analyze profiles. + bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true); - pid_t pid = fork(); - if (pid == 0) { - // child -- drop privileges before continuing. - drop_capabilities(uid); - // Run dexoptanalyzer to get dexopt_needed code. + // Run dexoptanalyzer to get dexopt_needed code. This is not expected to return. exec_dexoptanalyzer(dex_path, vdex_file_fd.get(), oat_file_fd.get(), zip_fd.get(), instruction_set, - compiler_filter, - profile_was_updated, - downgrade, class_loader_context); - exit(DEXOPTANALYZER_BIN_EXEC_ERROR); + compiler_filter, profile_was_updated, + downgrade, + class_loader_context); + PLOG(ERROR) << "Failed to exec dexoptanalyzer"; + _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED); } /* parent */ - int result = wait_child(pid); if (!WIFEXITED(result)) { LOG(ERROR) << "dexoptanalyzer failed for path " << dex_path << ": " << result; return false; } result = WEXITSTATUS(result); - bool success = process_dexoptanalyzer_result(dex_path, result, dexopt_needed_out); + // Check that we successfully executed dexoptanalyzer. + bool success = process_secondary_dexoptanalyzer_result(dex_path, result, dexopt_needed_out); + + LOG(DEBUG) << "Processed secondary dex file " << dex_path << " result=" << result; + // Run dexopt only if needed or forced. - // Note that dexoptanalyzer is executed even if force compilation is enabled. - // We ignore its valid dexopNeeded result, but still check (in process_dexoptanalyzer_result) - // that we only get results for odex files (apk_dir/oat/isa/code.odex) and not - // for oat files from dalvik-cache. - if (success && ((dexopt_flags & DEXOPT_FORCE) != 0)) { + // Note that dexoptanalyzer is executed even if force compilation is enabled (because it + // makes the code simpler; force compilation is only needed during tests). + if (success && + (result != SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE) && + ((dexopt_flags & DEXOPT_FORCE) != 0)) { *dexopt_needed_out = DEX2OAT_FROM_SCRATCH; } + // Check if we should make the oat file public. + // Note that if the dex file is not public the compiled code cannot be made public. + // 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; } @@ -1708,13 +1778,11 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins // Check if we're dealing with a secondary dex file and if we need to compile it. std::string oat_dir_str; - std::string dex_real_path; if (is_secondary_dex) { if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid, instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str, - &dex_real_path, downgrade, class_loader_context)) { + downgrade, class_loader_context)) { oat_dir = oat_dir_str.c_str(); - dex_path = dex_real_path.c_str(); if (dexopt_needed == NO_DEXOPT_NEEDED) { return 0; // Nothing to do, report success. } @@ -1852,29 +1920,13 @@ static bool unlink_if_exists(const std::string& file) { return false; } -// Create the oat file structure for the secondary dex 'dex_path' and assign -// the individual path component to the 'out_' parameters. -static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa, - /*out*/char* out_oat_dir, /*out*/char* out_oat_isa_dir, /*out*/char* out_oat_path) { - size_t dirIndex = dex_path.rfind('/'); - if (dirIndex == std::string::npos) { - LOG(ERROR) << "Unexpected dir structure for dex file " << dex_path; - return false; - } - // TODO(calin): we have similar computations in at lest 3 other places - // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by - // use string append. - std::string apk_dir = dex_path.substr(0, dirIndex); - snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str()); - snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str()); - - if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir, - /*is_secondary_dex*/true, out_oat_path)) { - LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path; - return false; - } - return true; -} +enum ReconcileSecondaryDexResult { + kReconcileSecondaryDexExists = 0, + kReconcileSecondaryDexCleanedUp = 1, + kReconcileSecondaryDexValidationError = 2, + kReconcileSecondaryDexCleanUpError = 3, + kReconcileSecondaryDexAccessIOError = 4, +}; // Reconcile the secondary dex 'dex_path' and its generated oat files. // Return true if all the parameters are valid and the secondary dex file was @@ -1888,36 +1940,15 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid, const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volume_uuid, int storage_flag, /*out*/bool* out_secondary_dex_exists) { - // Set out to false to start with, just in case we have validation errors. - *out_secondary_dex_exists = false; - if (!validate_dex_path_size(dex_path)) { - return false; - } - + *out_secondary_dex_exists = false; // start by assuming the file does not exist. if (isas.size() == 0) { LOG(ERROR) << "reconcile_secondary_dex_file called with empty isas vector"; return false; } - const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str(); - - // Note that we cannot validate the package path here because the file might not exist - // and we cannot call realpath to resolve system symlinks. Since /data/user/0 symlinks to - // /data/data/ a lot of validations will fail if we attempt to check the package path. - // It is still ok to be more relaxed because any file removal is done after forking and - // dropping capabilities. - if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr, - uid, storage_flag, /*validate_package_path*/ false)) { - LOG(ERROR) << "Could not validate secondary dex path " << dex_path; - return false; - } - - if (access(dex_path.c_str(), F_OK) == 0) { - // The path exists, nothing to do. The odex files (if any) will be left untouched. - *out_secondary_dex_exists = true; - return true; - } else if (errno != ENOENT) { - PLOG(ERROR) << "Failed to check access to secondary dex " << dex_path; + if (storage_flag != FLAG_STORAGE_CE && storage_flag != FLAG_STORAGE_DE) { + LOG(ERROR) << "reconcile_secondary_dex_file called with invalid storage_flag: " + << storage_flag; return false; } @@ -1925,23 +1956,39 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, // of the package user id. So we fork and drop capabilities in the child. pid_t pid = fork(); if (pid == 0) { - // The secondary dex does not exist anymore. Clear any generated files. + /* child -- drop privileges before continuing */ + drop_capabilities(uid); + + const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str(); + if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr, + uid, storage_flag)) { + LOG(ERROR) << "Could not validate secondary dex path " << dex_path; + _exit(kReconcileSecondaryDexValidationError); + } + + SecondaryDexAccess access_check = check_secondary_dex_access(dex_path); + switch (access_check) { + case kSecondaryDexAccessDoesNotExist: + // File does not exist. Proceed with cleaning. + break; + case kSecondaryDexAccessReadOk: _exit(kReconcileSecondaryDexExists); + case kSecondaryDexAccessIOError: _exit(kReconcileSecondaryDexAccessIOError); + case kSecondaryDexAccessPermissionError: _exit(kReconcileSecondaryDexValidationError); + default: + LOG(ERROR) << "Unexpected result from check_secondary_dex_access: " << access_check; + _exit(kReconcileSecondaryDexValidationError); + } + + // The secondary dex does not exist anymore or it's. Clear any generated files. char oat_path[PKG_PATH_MAX]; char oat_dir[PKG_PATH_MAX]; char oat_isa_dir[PKG_PATH_MAX]; bool result = true; - /* child -- drop privileges before continuing */ - drop_capabilities(uid); for (size_t i = 0; i < isas.size(); i++) { - if (!create_secondary_dex_oat_layout(dex_path, - isas[i], - oat_dir, - oat_isa_dir, - oat_path)) { - LOG(ERROR) << "Could not create secondary odex layout: " - << dex_path; - result = false; - continue; + if (!create_secondary_dex_oat_layout( + dex_path,isas[i], oat_dir, oat_isa_dir, oat_path)) { + LOG(ERROR) << "Could not create secondary odex layout: " << dex_path; + _exit(kReconcileSecondaryDexValidationError); } // Delete oat/vdex/art files. @@ -1966,11 +2013,43 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, result = rmdir_if_empty(oat_isa_dir) && result; result = rmdir_if_empty(oat_dir) && result; } - result ? _exit(0) : _exit(1); + if (!result) { + PLOG(ERROR) << "Failed to clean secondary dex artifacts for location " << dex_path; + } + _exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError); } int return_code = wait_child(pid); - return return_code == 0; + if (!WIFEXITED(return_code)) { + LOG(WARNING) << "reconcile dex failed for location " << dex_path << ": " << return_code; + } else { + return_code = WEXITSTATUS(return_code); + } + + LOG(DEBUG) << "Reconcile secondary dex path " << dex_path << " result=" << return_code; + + switch (return_code) { + case kReconcileSecondaryDexCleanedUp: + case kReconcileSecondaryDexValidationError: + // If we couldn't validate assume the dex file does not exist. + // This will purge the entry from the PM records. + *out_secondary_dex_exists = false; + return true; + case kReconcileSecondaryDexExists: + *out_secondary_dex_exists = true; + return true; + case kReconcileSecondaryDexAccessIOError: + // We had an access IO error. + // Return false so that we can try again. + // The value of out_secondary_dex_exists does not matter in this case and by convention + // is set to false. + *out_secondary_dex_exists = false; + return false; + default: + LOG(ERROR) << "Unexpected code from reconcile_secondary_dex_file: " << return_code; + *out_secondary_dex_exists = false; + return false; + } } // Helper for move_ab, so that we can have common failure-case cleanup. diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 89c11aa160..1a22992cb8 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -55,3 +55,23 @@ cc_test { "liblogwrap", ], } + +cc_test { + name: "installd_dexopt_test", + clang: true, + srcs: ["installd_dexopt_test.cpp"], + cflags: ["-Wall", "-Werror"], + shared_libs: [ + "libbase", + "libbinder", + "libcutils", + "libselinux", + "libutils", + ], + static_libs: [ + "libdiskusage", + "libinstalld", + "liblog", + "liblogwrap", + ], +} diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp new file mode 100644 index 0000000000..821b96fc44 --- /dev/null +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -0,0 +1,359 @@ +/* + * 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. + */ + +#include <stdlib.h> +#include <string.h> + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <cutils/properties.h> +#include <gtest/gtest.h> + +#include "dexopt.h" +#include "InstalldNativeService.h" +#include "globals.h" +#include "tests/test_utils.h" +#include "utils.h" + +namespace android { +namespace installd { + +// TODO(calin): try to dedup this code. +#if defined(__arm__) +static const std::string kRuntimeIsa = "arm"; +#elif defined(__aarch64__) +static const std::string kRuntimeIsa = "arm64"; +#elif defined(__mips__) && !defined(__LP64__) +static const std::string kRuntimeIsa = "mips"; +#elif defined(__mips__) && defined(__LP64__) +static const std::string kRuntimeIsa = "mips64"; +#elif defined(__i386__) +static const std::string kRuntimeIsa = "x86"; +#elif defined(__x86_64__) +static const std::string kRuntimeIsa = "x86_64"; +#else +static const std::string kRuntimeIsa = "none"; +#endif + +int get_property(const char *key, char *value, const char *default_value) { + return property_get(key, value, default_value); +} + +bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, + const char *instruction_set) { + return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set); +} + +bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path, + const char *instruction_set) { + return calculate_odex_file_path_default(path, apk_path, instruction_set); +} + +bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) { + return create_cache_path_default(path, src, instruction_set); +} + +static void run_cmd(const std::string& cmd) { + system(cmd.c_str()); +} + +static void mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) { + ::mkdir(path.c_str(), mode); + ::chown(path.c_str(), owner, group); + ::chmod(path.c_str(), mode); +} + +// Base64 encoding of a simple dex files with 2 methods. +static const char kDexFile[] = + "UEsDBBQAAAAIAOiOYUs9y6BLCgEAABQCAAALABwAY2xhc3Nlcy5kZXhVVAkAA/Ns+lkOHv1ZdXgL" + "AAEEI+UCAASIEwAAS0mt4DIwNmX4qpn7j/2wA7v7N+ZvoQpCJRlVx5SWa4YaiDAxMBQwMDBUhJkI" + "MUBBDyMDAzsDRJwFxAdioBDDHAYEYAbiFUAM1M5wAIhFGCGKDIDYAogdgNgDiH2BOAiI0xghekDm" + "sQIxGxQzM6ACRijNhCbOhCZfyohdPYyuh8szgtVkMkLsLhAAqeCDi+ejibPZZOZlltgxsDnqZSWW" + "JTKwOUFoZh9HayDhZM0g5AMS0M9JzEvX90/KSk0usWZgDAMaws5nAyXBzmpoYGlgAjsAyJoBMp0b" + "zQ8gGhbOTEhhzYwU3qxIYc2GFN6MClC/AhUyKUDMAYU9M1Qc5F8GKBscVgIQM0FxCwBQSwECHgMU" + "AAAACADojmFLPcugSwoBAAAUAgAACwAYAAAAAAAAAAAAoIEAAAAAY2xhc3Nlcy5kZXhVVAUAA/Ns" + "+ll1eAsAAQQj5QIABIgTAABQSwUGAAAAAAEAAQBRAAAATwEAAAAA"; + + +class DexoptTest : public testing::Test { +protected: + static constexpr bool kDebug = false; + static constexpr uid_t kSystemUid = 1000; + static constexpr uid_t kSystemGid = 1000; + static constexpr int32_t kOSdkVersion = 25; + static constexpr int32_t kAppDataFlags = FLAG_STORAGE_CE | FLAG_STORAGE_DE; + static constexpr uid_t kTestAppUid = 19999; + static constexpr gid_t kTestAppGid = 19999; + static constexpr int32_t kTestUserId = 0; + + InstalldNativeService* service_; + std::unique_ptr<std::string> volume_uuid_; + std::string package_name_; + std::string app_apk_dir_; + std::string app_private_dir_ce_; + std::string app_private_dir_de_; + std::string se_info_; + + int64_t ce_data_inode_; + + std::string secondary_dex_ce_; + std::string secondary_dex_ce_link_; + std::string secondary_dex_de_; + + virtual void SetUp() { + setenv("ANDROID_LOG_TAGS", "*:v", 1); + android::base::InitLogging(nullptr); + + service_ = new InstalldNativeService(); + + volume_uuid_ = nullptr; + package_name_ = "com.installd.test.dexopt"; + se_info_ = "default"; + + init_globals_from_data_and_root(); + + app_apk_dir_ = android_app_dir + package_name_; + + create_mock_app(); + } + + virtual void TearDown() { + if (!kDebug) { + service_->destroyAppData( + volume_uuid_, package_name_, kTestUserId, kAppDataFlags, ce_data_inode_); + run_cmd("rm -rf " + app_apk_dir_); + run_cmd("rm -rf " + app_private_dir_ce_); + run_cmd("rm -rf " + app_private_dir_de_); + } + delete service_; + } + + void create_mock_app() { + // Create the oat dir. + std::string app_oat_dir = app_apk_dir_ + "/oat"; + mkdir(app_apk_dir_, kSystemUid, kSystemGid, 0755); + service_->createOatDir(app_oat_dir, kRuntimeIsa); + + // Copy the primary apk. + std::string apk_path = app_apk_dir_ + "/base.jar"; + ASSERT_TRUE(WriteBase64ToFile(kDexFile, apk_path, kSystemUid, kSystemGid, 0644)); + + // Create the app user data. + ASSERT_TRUE(service_->createAppData( + volume_uuid_, + package_name_, + kTestUserId, + kAppDataFlags, + kTestAppUid, + se_info_, + kOSdkVersion, + &ce_data_inode_).isOk()); + + // Create a secondary dex file on CE storage + const char* volume_uuid_cstr = volume_uuid_ == nullptr ? nullptr : volume_uuid_->c_str(); + app_private_dir_ce_ = create_data_user_ce_package_path( + volume_uuid_cstr, kTestUserId, package_name_.c_str()); + secondary_dex_ce_ = app_private_dir_ce_ + "/secondary_ce.jar"; + ASSERT_TRUE(WriteBase64ToFile(kDexFile, secondary_dex_ce_, kTestAppUid, kTestAppGid, 0600)); + std::string app_private_dir_ce_link = create_data_user_ce_package_path_as_user_link( + volume_uuid_cstr, kTestUserId, package_name_.c_str()); + secondary_dex_ce_link_ = app_private_dir_ce_link + "/secondary_ce.jar"; + + // Create a secondary dex file on DE storage. + app_private_dir_de_ = create_data_user_de_package_path( + volume_uuid_cstr, kTestUserId, package_name_.c_str()); + secondary_dex_de_ = app_private_dir_de_ + "/secondary_de.jar"; + ASSERT_TRUE(WriteBase64ToFile(kDexFile, secondary_dex_de_, kTestAppUid, kTestAppGid, 0600)); + + // Fix app data uid. + ASSERT_TRUE(service_->fixupAppData(volume_uuid_, kTestUserId).isOk()); + } + + + std::string get_secondary_dex_artifact(const std::string& path, const std::string& type) { + std::string::size_type end = path.rfind('.'); + std::string::size_type start = path.rfind('/', end); + return path.substr(0, start) + "/oat/" + kRuntimeIsa + "/" + + path.substr(start + 1, end - start) + type; + } + + void compile_secondary_dex(const std::string& path, int32_t dex_storage_flag, + bool should_binder_call_succeed, bool should_dex_be_compiled = true, + int uid = kTestAppUid) { + std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_)); + int32_t dexopt_needed = 0; // does not matter; + std::unique_ptr<std::string> out_path = nullptr; // does not matter + int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag; + std::string compiler_filter = "speed-profile"; + std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&")); + std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_)); + bool downgrade = false; + + binder::Status result = service_->dexopt(path, + uid, + package_name_ptr, + kRuntimeIsa, + dexopt_needed, + out_path, + dex_flags, + compiler_filter, + volume_uuid_, + class_loader_context_ptr, + se_info_ptr, + downgrade); + ASSERT_EQ(should_binder_call_succeed, result.isOk()); + int expected_access = should_dex_be_compiled ? 0 : -1; + std::string odex = get_secondary_dex_artifact(path, "odex"); + std::string vdex = get_secondary_dex_artifact(path, "vdex"); + std::string art = get_secondary_dex_artifact(path, "art"); + ASSERT_EQ(expected_access, access(odex.c_str(), R_OK)); + ASSERT_EQ(expected_access, access(vdex.c_str(), R_OK)); + ASSERT_EQ(-1, access(art.c_str(), R_OK)); // empty profiles do not generate an image. + } + + void reconcile_secondary_dex(const std::string& path, int32_t storage_flag, + bool should_binder_call_succeed, bool should_dex_exist, bool should_dex_be_deleted, + int uid = kTestAppUid, std::string* package_override = nullptr) { + std::vector<std::string> isas; + isas.push_back(kRuntimeIsa); + bool out_secondary_dex_exists = false; + binder::Status result = service_->reconcileSecondaryDexFile( + path, + package_override == nullptr ? package_name_ : *package_override, + uid, + isas, + volume_uuid_, + storage_flag, + &out_secondary_dex_exists); + + ASSERT_EQ(should_binder_call_succeed, result.isOk()); + ASSERT_EQ(should_dex_exist, out_secondary_dex_exists); + + int expected_access = should_dex_be_deleted ? -1 : 0; + std::string odex = get_secondary_dex_artifact(path, "odex"); + std::string vdex = get_secondary_dex_artifact(path, "vdex"); + std::string art = get_secondary_dex_artifact(path, "art"); + ASSERT_EQ(expected_access, access(odex.c_str(), F_OK)); + ASSERT_EQ(expected_access, access(vdex.c_str(), F_OK)); + ASSERT_EQ(-1, access(art.c_str(), R_OK)); // empty profiles do not generate an image. + } +}; + + +TEST_F(DexoptTest, DexoptSecondaryCe) { + LOG(INFO) << "DexoptSecondaryCe"; + compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_CE, + /*binder_ok*/ true, /*compile_ok*/ true); +} + +TEST_F(DexoptTest, DexoptSecondaryCeLink) { + LOG(INFO) << "DexoptSecondaryCeLink"; + compile_secondary_dex(secondary_dex_ce_link_, DEXOPT_STORAGE_CE, + /*binder_ok*/ true, /*compile_ok*/ true); +} + +TEST_F(DexoptTest, DexoptSecondaryDe) { + LOG(INFO) << "DexoptSecondaryDe"; + compile_secondary_dex(secondary_dex_de_, DEXOPT_STORAGE_DE, + /*binder_ok*/ true, /*compile_ok*/ true); +} + +TEST_F(DexoptTest, DexoptSecondaryDoesNotExist) { + LOG(INFO) << "DexoptSecondaryDoesNotExist"; + // If the file validates but does not exist we do not treat it as an error. + compile_secondary_dex(secondary_dex_ce_ + "not.there", DEXOPT_STORAGE_CE, + /*binder_ok*/ true, /*compile_ok*/ false); +} + +TEST_F(DexoptTest, DexoptSecondaryStorageValidationError) { + LOG(INFO) << "DexoptSecondaryStorageValidationError"; + compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_DE, + /*binder_ok*/ false, /*compile_ok*/ false); +} + +TEST_F(DexoptTest, DexoptSecondaryAppOwnershipValidationError) { + LOG(INFO) << "DexoptSecondaryAppOwnershipValidationError"; + compile_secondary_dex("/data/data/random.app/secondary.jar", DEXOPT_STORAGE_CE, + /*binder_ok*/ false, /*compile_ok*/ false); +} + +TEST_F(DexoptTest, DexoptSecondaryAcessViaDifferentUidError) { + LOG(INFO) << "DexoptSecondaryAcessViaDifferentUidError"; + compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_CE, + /*binder_ok*/ false, /*compile_ok*/ false, kSystemUid); +} + + +class ReconcileTest : public DexoptTest { + virtual void SetUp() { + DexoptTest::SetUp(); + compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_CE, + /*binder_ok*/ true, /*compile_ok*/ true); + compile_secondary_dex(secondary_dex_de_, DEXOPT_STORAGE_DE, + /*binder_ok*/ true, /*compile_ok*/ true); + } +}; + +TEST_F(ReconcileTest, ReconcileSecondaryCeExists) { + LOG(INFO) << "ReconcileSecondaryCeExists"; + reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE, + /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false); +} + +TEST_F(ReconcileTest, ReconcileSecondaryCeLinkExists) { + LOG(INFO) << "ReconcileSecondaryCeLinkExists"; + reconcile_secondary_dex(secondary_dex_ce_link_, FLAG_STORAGE_CE, + /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false); +} + +TEST_F(ReconcileTest, ReconcileSecondaryDeExists) { + LOG(INFO) << "ReconcileSecondaryDeExists"; + reconcile_secondary_dex(secondary_dex_de_, FLAG_STORAGE_DE, + /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false); +} + +TEST_F(ReconcileTest, ReconcileSecondaryDeDoesNotExist) { + LOG(INFO) << "ReconcileSecondaryDeDoesNotExist"; + run_cmd("rm -rf " + secondary_dex_de_); + reconcile_secondary_dex(secondary_dex_de_, FLAG_STORAGE_DE, + /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ true); +} + +TEST_F(ReconcileTest, ReconcileSecondaryStorageValidationError) { + // Validation errors will not clean the odex/vdex/art files but will mark + // the file as non existent so that the PM knows it should purge it from its + // records. + LOG(INFO) << "ReconcileSecondaryStorageValidationError"; + reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_DE, + /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false); +} + +TEST_F(ReconcileTest, ReconcileSecondaryAppOwnershipValidationError) { + LOG(INFO) << "ReconcileSecondaryAppOwnershipValidationError"; + // Attempt to reconcile the dex files of the test app from a different app. + std::string another_app = "another.app"; + reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE, + /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false, kSystemUid, &another_app); +} + +TEST_F(ReconcileTest, ReconcileSecondaryAcessViaDifferentUidError) { + LOG(INFO) << "ReconcileSecondaryAcessViaDifferentUidError"; + reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE, + /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false, kSystemUid); +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index 2ca7ac2350..a6a4451d9d 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -379,6 +379,7 @@ static void fail_secondary_dex_validation(const std::string& package_name, TEST_F(UtilsTest, ValidateSecondaryDexFilesPath) { std::string package_name = "com.test.app"; std::string app_dir_ce_user_0 = "/data/data/" + package_name; + std::string app_dir_ce_user_0_link = "/data/user/0/" + package_name; std::string app_dir_ce_user_10 = "/data/user/10/" + package_name; std::string app_dir_de_user_0 = "/data/user_de/0/" + package_name; @@ -400,6 +401,8 @@ TEST_F(UtilsTest, ValidateSecondaryDexFilesPath) { // Standard path for user 0 on CE storage. pass_secondary_dex_validation( package_name, app_dir_ce_user_0 + "/ce0.dex", app_uid_for_user_0, FLAG_STORAGE_CE); + pass_secondary_dex_validation( + package_name, app_dir_ce_user_0_link + "/ce0.dex", app_uid_for_user_0, FLAG_STORAGE_CE); // Standard path for user 10 on CE storage. pass_secondary_dex_validation( package_name, app_dir_ce_user_10 + "/ce10.dex", app_uid_for_user_10, FLAG_STORAGE_CE); diff --git a/cmds/installd/tests/test_utils.h b/cmds/installd/tests/test_utils.h new file mode 100644 index 0000000000..7d1162e673 --- /dev/null +++ b/cmds/installd/tests/test_utils.h @@ -0,0 +1,107 @@ +#include <android-base/logging.h> +#include <stdlib.h> +#include <string.h> + +uint8_t kBase64Map[256] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, + 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255 +}; + +uint8_t* DecodeBase64(const char* src, size_t* dst_size) { + CHECK(dst_size != nullptr); + std::vector<uint8_t> tmp; + uint32_t t = 0, y = 0; + int g = 3; + for (size_t i = 0; src[i] != '\0'; ++i) { + uint8_t c = kBase64Map[src[i] & 0xFF]; + if (c == 255) continue; + // the final = symbols are read and used to trim the remaining bytes + if (c == 254) { + c = 0; + // prevent g < 0 which would potentially allow an overflow later + if (--g < 0) { + *dst_size = 0; + return nullptr; + } + } else if (g != 3) { + // we only allow = to be at the end + *dst_size = 0; + return nullptr; + } + t = (t << 6) | c; + if (++y == 4) { + tmp.push_back((t >> 16) & 255); + if (g > 1) { + tmp.push_back((t >> 8) & 255); + } + if (g > 2) { + tmp.push_back(t & 255); + } + y = t = 0; + } + } + if (y != 0) { + *dst_size = 0; + return nullptr; + } + std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]); + *dst_size = tmp.size(); + std::copy(tmp.begin(), tmp.end(), dst.get()); + return dst.release(); +} + +bool WriteBase64ToFile(const char* base64, const std::string& file, + uid_t uid, gid_t gid, int mode) { + CHECK(base64 != nullptr); + size_t length; + std::unique_ptr<uint8_t[]> bytes(DecodeBase64(base64, &length)); + CHECK(bytes != nullptr); + + + int fd = open(file.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + + if (fd < 0) { + PLOG(ERROR) << "Could not open file " << file; + return false; + } + + size_t wrote = 0; + while (wrote < length) { + ssize_t cur = write(fd, bytes.get() + wrote, length - wrote); + if (cur == -1) { + PLOG(ERROR) << "Could not write file " << file; + return false; + } + wrote += cur; + } + + if (::chown(file.c_str(), uid, gid) != 0) { + PLOG(ERROR) << "Could not chown file " << file; + return false; + } + if (::chmod(file.c_str(), mode) != 0) { + PLOG(ERROR) << "Could not chmod file " << file; + return false; + } + return true; +} diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index ca0a82e066..7dca7c6422 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -87,6 +87,20 @@ std::string create_data_user_ce_package_path(const char* volume_uuid, create_data_user_ce_path(volume_uuid, user).c_str(), package_name); } +/** + * Create the path name where package data should be stored for the given + * volume UUID, package name, and user ID. An empty UUID is assumed to be + * internal storage. + * Compared to create_data_user_ce_package_path this method always return the + * ".../user/..." directory. + */ +std::string create_data_user_ce_package_path_as_user_link( + const char* volume_uuid, userid_t userid, const char* package_name) { + check_package_name(package_name); + std::string data(create_data_path(volume_uuid)); + return StringPrintf("%s/user/%u/%s", data.c_str(), userid, package_name); +} + std::string create_data_user_ce_package_path(const char* volume_uuid, userid_t user, const char* package_name, ino_t ce_data_inode) { // For testing purposes, rely on the inode when defined; this could be @@ -786,7 +800,7 @@ int validate_system_app_path(const char* path) { } bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path, - const char* volume_uuid, int uid, int storage_flag, bool validate_package_path) { + const char* volume_uuid, int uid, int storage_flag) { CHECK(storage_flag == FLAG_STORAGE_CE || storage_flag == FLAG_STORAGE_DE); // Empty paths are not allowed. @@ -800,16 +814,20 @@ bool validate_secondary_dex_path(const std::string& pkgname, const std::string& // The path should be at most PKG_PATH_MAX long. if (dex_path.size() > PKG_PATH_MAX) { return false; } - if (validate_package_path) { - // If we are asked to validate the package path check that - // the dex_path is under the app data directory. - std::string app_private_dir = storage_flag == FLAG_STORAGE_CE + // The dex_path should be under the app data directory. + std::string app_private_dir = storage_flag == FLAG_STORAGE_CE ? create_data_user_ce_package_path( volume_uuid, multiuser_get_user_id(uid), pkgname.c_str()) : create_data_user_de_package_path( volume_uuid, multiuser_get_user_id(uid), pkgname.c_str()); - if (strncmp(dex_path.c_str(), app_private_dir.c_str(), app_private_dir.size()) != 0) { + if (strncmp(dex_path.c_str(), app_private_dir.c_str(), app_private_dir.size()) != 0) { + // The check above might fail if the dex file is accessed via the /data/user/0 symlink. + // If that's the case, attempt to validate against the user data link. + std::string app_private_dir_symlink = create_data_user_ce_package_path_as_user_link( + volume_uuid, multiuser_get_user_id(uid), pkgname.c_str()); + if (strncmp(dex_path.c_str(), app_private_dir_symlink.c_str(), + app_private_dir_symlink.size()) != 0) { return false; } } diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 3e04af9178..b90caf9915 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -60,6 +60,8 @@ std::string create_data_user_ce_package_path(const char* volume_uuid, userid_t user, const char* package_name, ino_t ce_data_inode); std::string create_data_user_de_package_path(const char* volume_uuid, userid_t user, const char* package_name); +std::string create_data_user_ce_package_path_as_user_link( + const char* volume_uuid, userid_t userid, const char* package_name); std::string create_data_media_path(const char* volume_uuid, userid_t userid); std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name); @@ -114,7 +116,7 @@ void remove_path_xattr(const std::string& path, const char* inode_xattr); int validate_system_app_path(const char* path); bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path, - const char* volume_uuid, int uid, int storage_flag, bool validate_package_path = true); + const char* volume_uuid, int uid, int storage_flag); int validate_apk_path(const char *path); int validate_apk_path_subdirs(const char *path); |