diff options
| -rw-r--r-- | cmds/installd/InstalldNativeService.cpp | 25 | ||||
| -rw-r--r-- | cmds/installd/InstalldNativeService.h | 5 | ||||
| -rw-r--r-- | cmds/installd/binder/android/os/IInstalld.aidl | 3 | ||||
| -rw-r--r-- | cmds/installd/dexopt.cpp | 82 | ||||
| -rw-r--r-- | cmds/installd/dexopt.h | 11 | ||||
| -rw-r--r-- | cmds/installd/tests/installd_dexopt_test.cpp | 243 | ||||
| -rw-r--r-- | cmds/installd/tests/installd_utils_test.cpp | 6 | ||||
| -rw-r--r-- | cmds/installd/tests/test_utils.h | 29 | ||||
| -rw-r--r-- | cmds/installd/utils.cpp | 9 | ||||
| -rw-r--r-- | cmds/installd/utils.h | 1 | 
10 files changed, 370 insertions, 44 deletions
| diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index e10fb6c14e..58786f6683 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -436,7 +436,7 @@ binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::s              // profiles.              int shared_app_gid = multiuser_get_shared_gid(0, appId);              if ((shared_app_gid != -1) && fs_prepare_dir_strict( -                    ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) { +                    ref_profile_path.c_str(), 0701, shared_app_gid, shared_app_gid) != 0) {                  return error("Failed to prepare " + ref_profile_path);              }          } @@ -1833,6 +1833,29 @@ binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::stri      return ok();  } +binder::Status InstalldNativeService::snapshotProfile(int32_t appId, const std::string& packageName, +        const std::string& codePath, bool* _aidl_return) { +    ENFORCE_UID(AID_SYSTEM); +    CHECK_ARGUMENT_PACKAGE_NAME(packageName); +    std::lock_guard<std::recursive_mutex> lock(mLock); + +    *_aidl_return = snapshot_profile(appId, packageName, codePath); +    return ok(); +} + +binder::Status InstalldNativeService::destroyProfileSnapshot(const std::string& packageName, +        const std::string& codePath) { +    ENFORCE_UID(AID_SYSTEM); +    CHECK_ARGUMENT_PACKAGE_NAME(packageName); +    std::lock_guard<std::recursive_mutex> lock(mLock); + +    std::string snapshot = create_snapshot_profile_path(packageName, codePath); +    if ((unlink(snapshot.c_str()) != 0) && (errno != ENOENT)) { +        return error("Failed to destroy profile snapshot for " + packageName + ":" + codePath); +    } +    return ok(); +} +  binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,          const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,          int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags, diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 5167a1413c..eb7231c9b2 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -96,6 +96,11 @@ public:      binder::Status clearAppProfiles(const std::string& packageName);      binder::Status destroyAppProfiles(const std::string& packageName); +    binder::Status snapshotProfile(int32_t appId, const std::string& packageName, +            const std::string& codePath, bool* _aidl_return); +    binder::Status destroyProfileSnapshot(const std::string& packageName, +            const std::string& codePath); +      binder::Status idmap(const std::string& targetApkPath, const std::string& overlayApkPath,              int32_t uid);      binder::Status removeIdmap(const std::string& overlayApkPath); diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 0f9862d050..16ba2af70a 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -62,6 +62,9 @@ interface IInstalld {      void clearAppProfiles(@utf8InCpp String packageName);      void destroyAppProfiles(@utf8InCpp String packageName); +    boolean snapshotProfile(int appId, @utf8InCpp String packageName, @utf8InCpp String codePath); +    void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String codePath); +      void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);      void removeIdmap(@utf8InCpp String overlayApkPath);      void rmPackageDir(@utf8InCpp String packageDir); diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index 6f160b9058..beffe00e40 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -566,14 +566,12 @@ static void SetDex2OatScheduling(bool set_to_bg) {      }  } -static bool create_profile(int uid, const std::string& profile) { -    unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), O_CREAT | O_NOFOLLOW, 0600))); +static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t flags) { +    unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags, 0600)));      if (fd.get() < 0) { -        if (errno == EEXIST) { -            return true; -        } else { +        if (errno != EEXIST) {              PLOG(ERROR) << "Failed to create profile " << profile; -            return false; +            return invalid_unique_fd();          }      }      // Profiles should belong to the app; make sure of that by giving ownership to @@ -581,27 +579,26 @@ static bool create_profile(int uid, const std::string& profile) {      // since dex2oat/profman will fail with SElinux denials.      if (fchown(fd.get(), uid, uid) < 0) {          PLOG(ERROR) << "Could not chwon profile " << profile; -        return false; +        return invalid_unique_fd();      } -    return true; +    return fd;  } -static unique_fd open_profile(int uid, const std::string& profile, bool read_write) { -    // Check if we need to open the profile for a read-write operation. If so, we -    // might need to create the profile since the file might not be there. Reference -    // profiles are created on the fly so they might not exist beforehand. -    if (read_write) { -        if (!create_profile(uid, profile)) { -            return invalid_unique_fd(); -        } -    } -    int flags = read_write ? O_RDWR : O_RDONLY; +static unique_fd open_profile(uid_t uid, const std::string& profile, int32_t flags) {      // Do not follow symlinks when opening a profile:      //   - primary profiles should not contain symlinks in their paths      //   - secondary dex paths should have been already resolved and validated      flags |= O_NOFOLLOW; -    unique_fd fd(TEMP_FAILURE_RETRY(open(profile.c_str(), flags))); +    // Check if we need to create the profile +    // Reference profiles and snapshots are created on the fly; so they might not exist beforehand. +    unique_fd fd; +    if ((flags & O_CREAT) != 0) { +        fd = create_profile(uid, profile, flags); +    } else { +        fd.reset(TEMP_FAILURE_RETRY(open(profile.c_str(), flags))); +    } +      if (fd.get() < 0) {          if (errno != ENOENT) {              // Profiles might be missing for various reasons. For example, in a @@ -621,13 +618,19 @@ static unique_fd open_profile(int uid, const std::string& profile, bool read_wri  static unique_fd open_current_profile(uid_t uid, userid_t user, const std::string& location,          bool is_secondary_dex) {      std::string profile = create_current_profile_path(user, location, is_secondary_dex); -    return open_profile(uid, profile, /*read_write*/false); +    return open_profile(uid, profile, O_RDONLY);  }  static unique_fd open_reference_profile(uid_t uid, const std::string& location, bool read_write,          bool is_secondary_dex) {      std::string profile = create_reference_profile_path(location, is_secondary_dex); -    return open_profile(uid, profile, read_write); +    return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY); +} + +static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name, +        const std::string& code_path) { +    std::string profile = create_snapshot_profile_path(package_name, code_path); +    return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC);  }  static void open_profile_files(uid_t uid, const std::string& location, bool is_secondary_dex, @@ -2396,5 +2399,42 @@ bool create_cache_path_default(char path[PKG_PATH_MAX], const char *src,      }  } +bool snapshot_profile(int32_t app_id, const std::string& package_name, +        const std::string& code_path) { +    int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id); + +    unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, code_path); +    if (snapshot_fd < 0) { +        return false; +    } + +    std::vector<unique_fd> profiles_fd; +    unique_fd reference_profile_fd; +    open_profile_files(app_shared_gid, package_name, /*is_secondary_dex*/ false, &profiles_fd, +            &reference_profile_fd); +    if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) { +        return false; +    } + +    profiles_fd.push_back(std::move(reference_profile_fd)); + +    pid_t pid = fork(); +    if (pid == 0) { +        /* child -- drop privileges before continuing */ +        drop_capabilities(app_shared_gid); +        run_profman_merge(profiles_fd, snapshot_fd); +        exit(42);   /* only get here on exec failure */ +    } + +    /* parent */ +    int return_code = wait_child(pid); +    if (!WIFEXITED(return_code)) { +        LOG(WARNING) << "profman failed for " << package_name << ":" << code_path; +        return false; +    } + +    return true; +} +  }  // namespace installd  }  // namespace android diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index 798e2117fa..8a9ffd203f 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h @@ -50,6 +50,17 @@ bool move_ab(const char* apk_path, const char* instruction_set, const char* outp  // the reference profiles accessible with open_reference_profile().  bool analyze_primary_profiles(uid_t uid, const std::string& pkgname); +// Create a snapshot of the profile information for the given package and code path. +// The profile snapshot is the aggregation of all existing profiles (all current user +// profiles & the reference profile) and is meant to capture the all the profile information +// without performing a merge into the reference profile which might impact future dex2oat +// compilations. +// The snapshot is created next to the reference profile of the package and the +// ownership is assigned to AID_SYSTEM. +// The snapshot location is reference_profile_location.snapshot. If a snapshot is already +// there, it will be truncated and overwritten. +bool snapshot_profile(int32_t app_id, const std::string& package, const std::string& code_path); +  bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths);  bool copy_system_profile(const std::string& system_profile, diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index 821b96fc44..19b42b57ff 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -14,20 +14,33 @@   * limitations under the License.   */ +#include <fcntl.h>  #include <stdlib.h>  #include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <android-base/file.h>  #include <android-base/logging.h>  #include <android-base/stringprintf.h> +#include <android-base/unique_fd.h> +  #include <cutils/properties.h> +  #include <gtest/gtest.h> +#include <selinux/android.h> +#include <selinux/avc.h> +  #include "dexopt.h"  #include "InstalldNativeService.h"  #include "globals.h"  #include "tests/test_utils.h"  #include "utils.h" +using android::base::ReadFully; +using android::base::unique_fd; +  namespace android {  namespace installd { @@ -76,6 +89,42 @@ static void mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode      ::chmod(path.c_str(), mode);  } +static int log_callback(int type, const char *fmt, ...) { // NOLINT +    va_list ap; +    int priority; + +    switch (type) { +        case SELINUX_WARNING: +            priority = ANDROID_LOG_WARN; +            break; +        case SELINUX_INFO: +            priority = ANDROID_LOG_INFO; +            break; +        default: +            priority = ANDROID_LOG_ERROR; +            break; +    } +    va_start(ap, fmt); +    LOG_PRI_VA(priority, "SELinux", fmt, ap); +    va_end(ap); +    return 0; +} + +static bool init_selinux() { +    int selinux_enabled = (is_selinux_enabled() > 0); + +    union selinux_callback cb; +    cb.func_log = log_callback; +    selinux_set_callback(SELINUX_CB_LOG, cb); + +    if (selinux_enabled && selinux_status_open(true) < 0) { +        LOG(ERROR) << "Could not open selinux status; exiting"; +        return false; +    } + +    return true; +} +  // Base64 encoding of a simple dex files with 2 methods.  static const char kDexFile[] =      "UEsDBBQAAAAIAOiOYUs9y6BLCgEAABQCAAALABwAY2xhc3Nlcy5kZXhVVAkAA/Ns+lkOHv1ZdXgL" @@ -97,6 +146,7 @@ protected:      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 uid_t kTestAppId = kTestAppUid;      static constexpr int32_t kTestUserId = 0;      InstalldNativeService* service_; @@ -116,15 +166,18 @@ protected:      virtual void SetUp() {          setenv("ANDROID_LOG_TAGS", "*:v", 1);          android::base::InitLogging(nullptr); - +        // Initialize the globals holding the file system main paths (/data/, /system/ etc..). +        // This is needed in order to compute the application and profile paths. +        ASSERT_TRUE(init_globals_from_data_and_root()); +        // Initialize selinux log callbacks. +        // This ensures that selinux is up and running and re-directs the selinux messages +        // to logcat (in order to make it easier to investigate test results). +        ASSERT_TRUE(init_selinux());          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(); @@ -183,14 +236,14 @@ protected:      } -    std::string get_secondary_dex_artifact(const std::string& path, const std::string& type) { +    std::string GetSecondaryDexArtifact(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, +    void CompileSecondaryDex(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_)); @@ -216,9 +269,9 @@ protected:                                                   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"); +        std::string odex = GetSecondaryDexArtifact(path, "odex"); +        std::string vdex = GetSecondaryDexArtifact(path, "vdex"); +        std::string art = GetSecondaryDexArtifact(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. @@ -243,56 +296,64 @@ protected:          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"); +        std::string odex = GetSecondaryDexArtifact(path, "odex"); +        std::string vdex = GetSecondaryDexArtifact(path, "vdex"); +        std::string art = GetSecondaryDexArtifact(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.      } + +    void CheckFileAccess(const std::string& file, uid_t uid, gid_t gid, mode_t mode) { +        struct stat st; +        ASSERT_EQ(0, stat(file.c_str(), &st)); +        ASSERT_EQ(uid, st.st_uid); +        ASSERT_EQ(gid, st.st_gid); +        ASSERT_EQ(mode, st.st_mode); +    }  };  TEST_F(DexoptTest, DexoptSecondaryCe) {      LOG(INFO) << "DexoptSecondaryCe"; -    compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_CE, +    CompileSecondaryDex(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, +    CompileSecondaryDex(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, +    CompileSecondaryDex(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, +    CompileSecondaryDex(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, +    CompileSecondaryDex(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, +    CompileSecondaryDex("/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, +    CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE,          /*binder_ok*/ false,  /*compile_ok*/ false, kSystemUid);  } @@ -300,9 +361,9 @@ TEST_F(DexoptTest, DexoptSecondaryAcessViaDifferentUidError) {  class ReconcileTest : public DexoptTest {      virtual void SetUp() {          DexoptTest::SetUp(); -        compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_CE, +        CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE,              /*binder_ok*/ true, /*compile_ok*/ true); -        compile_secondary_dex(secondary_dex_de_, DEXOPT_STORAGE_DE, +        CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE,              /*binder_ok*/ true, /*compile_ok*/ true);      }  }; @@ -355,5 +416,145 @@ TEST_F(ReconcileTest, ReconcileSecondaryAcessViaDifferentUidError) {          /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false, kSystemUid);  } +class ProfileTest : public DexoptTest { +  protected: +    std::string cur_profile_; +    std::string ref_profile_; +    std::string snap_profile_; + +    virtual void SetUp() { +        DexoptTest::SetUp(); +        cur_profile_ = create_current_profile_path( +                kTestUserId, package_name_, /*is_secondary_dex*/ false); +        ref_profile_ = create_reference_profile_path(package_name_, /*is_secondary_dex*/ false); +        snap_profile_ = create_snapshot_profile_path(package_name_, "base.jar"); +    } + +    void SetupProfile(const std::string& path, uid_t uid, gid_t gid, mode_t mode, int32_t seed) { +        run_cmd("profman --generate-test-profile-seed=" + std::to_string(seed) + +                " --generate-test-profile-num-dex=2 --generate-test-profile=" + path); +        ::chmod(path.c_str(), mode); +        ::chown(path.c_str(), uid, gid); +    } + +    void SetupProfiles(bool setup_ref) { +        SetupProfile(cur_profile_, kTestAppUid, kTestAppGid, 0600, 1); +        if (setup_ref) { +            SetupProfile(ref_profile_, kTestAppUid, kTestAppGid, 0060, 2); +        } +    } + +    void SnapshotProfile(int32_t appid, const std::string& package_name, bool expected_result) { +        bool result; +        binder::Status binder_result = service_->snapshotProfile( +                appid, package_name, "base.jar", &result); +        ASSERT_TRUE(binder_result.isOk()); +        ASSERT_EQ(expected_result, result); + +        if (!expected_result) { +            // Do not check the files if we expect to fail. +            return; +        } + +        // Check that the snapshot was created witht he expected acess flags. +        CheckFileAccess(snap_profile_, kSystemUid, kSystemGid, 0600 | S_IFREG); + +        // The snapshot should be equivalent to the merge of profiles. +        std::string expected_profile_content = snap_profile_ + ".expected"; +        run_cmd("rm -f " + expected_profile_content); +        run_cmd("touch " + expected_profile_content); +        run_cmd("profman --profile-file=" + cur_profile_ + +                " --profile-file=" + ref_profile_ + +                " --reference-profile-file=" + expected_profile_content); + +        ASSERT_TRUE(AreFilesEqual(expected_profile_content, snap_profile_)); + +        pid_t pid = fork(); +        if (pid == 0) { +            /* child */ +            TransitionToSystemServer(); + +            // System server should be able to open the the spanshot. +            unique_fd fd(open(snap_profile_.c_str(), O_RDONLY)); +            ASSERT_TRUE(fd > -1) << "Failed to open profile as kSystemUid: " << strerror(errno); +            _exit(0); +        } +        /* parent */ +        ASSERT_TRUE(WIFEXITED(wait_child(pid))); +    } + +  private: +    void TransitionToSystemServer() { +        ASSERT_TRUE(DropCapabilities(kSystemUid, kSystemGid)); +        int32_t res = selinux_android_setcontext( +                kSystemUid, true, se_info_.c_str(), "system_server"); +        ASSERT_EQ(0, res) << "Failed to setcon " << strerror(errno); +    } + +    bool AreFilesEqual(const std::string& file1, const std::string& file2) { +        std::vector<uint8_t> content1; +        std::vector<uint8_t> content2; + +        if (!ReadAll(file1, &content1)) return false; +        if (!ReadAll(file2, &content2)) return false; +        return content1 == content2; +    } + +    bool ReadAll(const std::string& file, std::vector<uint8_t>* content) { +        unique_fd fd(open(file.c_str(), O_RDONLY)); +        if (fd < 0) { +            PLOG(ERROR) << "Failed to open " << file; +            return false; +        } +        struct stat st; +        if (fstat(fd, &st) != 0) { +            PLOG(ERROR) << "Failed to stat " << file; +            return false; +        } +        content->resize(st.st_size); +        bool result = ReadFully(fd, content->data(), content->size()); +        if (!result) { +            PLOG(ERROR) << "Failed to read " << file; +        } +        return result; +    } +}; + +TEST_F(ProfileTest, ProfileSnapshotOk) { +    LOG(INFO) << "ProfileSnapshotOk"; + +    SetupProfiles(/*setup_ref*/ true); +    SnapshotProfile(kTestAppId, package_name_, /*expected_result*/ true); +} + +// The reference profile is created on the fly. We need to be able to +// snapshot without one. +TEST_F(ProfileTest, ProfileSnapshotOkNoReference) { +    LOG(INFO) << "ProfileSnapshotOkNoReference"; + +    SetupProfiles(/*setup_ref*/ false); +    SnapshotProfile(kTestAppId, package_name_, /*expected_result*/ true); +} + +TEST_F(ProfileTest, ProfileSnapshotFailWrongPackage) { +    LOG(INFO) << "ProfileSnapshotFailWrongPackage"; + +    SetupProfiles(/*setup_ref*/ true); +    SnapshotProfile(kTestAppId, "not.there", /*expected_result*/ false); +} + +TEST_F(ProfileTest, ProfileSnapshotDestroySnapshot) { +    LOG(INFO) << "ProfileSnapshotDestroySnapshot"; + +    SetupProfiles(/*setup_ref*/ true); +    SnapshotProfile(kTestAppId, package_name_, /*expected_result*/ true); + +    binder::Status binder_result = service_->destroyProfileSnapshot(package_name_, "base.jar"); +    ASSERT_TRUE(binder_result.isOk()); +    struct stat st; +    ASSERT_EQ(-1, stat(snap_profile_.c_str(), &st)); +    ASSERT_EQ(ENOENT, errno); +} +  }  // namespace installd  }  // namespace android diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index a6a4451d9d..49da85dcfc 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -350,6 +350,12 @@ TEST_F(UtilsTest, CreatePrimaryReferenceProfile) {              create_reference_profile_path("com.example", /*is_secondary*/false));  } +TEST_F(UtilsTest, CreateProfileSnapshot) { +    std::string expected = +        create_primary_reference_profile_package_dir_path("com.example") + "/primary.prof.snapshot"; +    EXPECT_EQ(expected, create_snapshot_profile_path("com.example", "base.apk")); +} +  TEST_F(UtilsTest, CreateSecondaryCurrentProfile) {      EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.cur.prof",              create_current_profile_path(/*user*/0, diff --git a/cmds/installd/tests/test_utils.h b/cmds/installd/tests/test_utils.h index 7d1162e673..b8785c68a0 100644 --- a/cmds/installd/tests/test_utils.h +++ b/cmds/installd/tests/test_utils.h @@ -1,6 +1,9 @@ -#include <android-base/logging.h>  #include <stdlib.h>  #include <string.h> +#include <sys/capability.h> + +#include <android-base/logging.h> +#include <selinux/android.h>  uint8_t kBase64Map[256] = {      255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, @@ -105,3 +108,27 @@ bool WriteBase64ToFile(const char* base64, const std::string& file,      }      return true;  } + +// TODO(calin): fix dexopt drop_capabilities and move to general utils (b/69678790). +bool DropCapabilities(uid_t uid, gid_t gid) { +    if (setgid(gid) != 0) { +        PLOG(ERROR) << "setgid failed: " <<  gid; +        return false; +    } +    if (setuid(uid) != 0) { +        PLOG(ERROR) << "setuid failed: " <<  uid; +        return false; +    } +    // drop capabilities +    struct __user_cap_header_struct capheader; +    struct __user_cap_data_struct capdata[2]; +    memset(&capheader, 0, sizeof(capheader)); +    memset(&capdata, 0, sizeof(capdata)); +    capheader.version = _LINUX_CAPABILITY_VERSION_3; +    if (capset(&capheader, &capdata[0]) < 0) { +        PLOG(ERROR) << "capset failed"; +        return false; +    } + +    return true; +} diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 7dca7c6422..61c9c8ff08 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -236,6 +236,7 @@ std::string create_data_dalvik_cache_path() {  const std::string PROFILE_EXT = ".prof";  const std::string CURRENT_PROFILE_EXT = ".cur";  const std::string PRIMARY_PROFILE_NAME = "primary" + PROFILE_EXT; +const std::string SNAPSHOT_PROFILE_EXT = ".snapshot";  // Gets the parent directory and the file name for the given secondary dex path.  // Returns true on success, false on failure (if the dex_path does not have the expected @@ -289,6 +290,14 @@ std::string create_reference_profile_path(const std::string& location, bool is_s      }  } +std::string create_snapshot_profile_path(const std::string& package, +        const std::string& code_path ATTRIBUTE_UNUSED) { +    // TODD(calin): code_path is ignored for now. It will be used when each split gets its own +    // profile file. +    std::string ref_profile = create_reference_profile_path(package, /*is_secondary_dex*/ false); +    return ref_profile + SNAPSHOT_PROFILE_EXT; +} +  std::vector<userid_t> get_known_users(const char* volume_uuid) {      std::vector<userid_t> users; diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index b90caf9915..53910615d9 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -83,6 +83,7 @@ std::string create_current_profile_path(          userid_t user, const std::string& package_name, bool is_secondary_dex);  std::string create_reference_profile_path(          const std::string& package_name, bool is_secondary_dex); +std::string create_snapshot_profile_path(const std::string& package, const std::string& code_path);  std::vector<userid_t> get_known_users(const char* volume_uuid); |