Loading cmds/installd/InstalldNativeService.cpp +63 −0 Original line number Diff line number Diff line Loading @@ -289,6 +289,46 @@ static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t ui return 0; } /** * Ensure that we have a hard-limit quota to protect against abusive apps; * they should never use more than 90% of blocks or 50% of inodes. */ static int prepare_app_quota(const std::unique_ptr<std::string>& uuid, const std::string& device, uid_t uid) { if (device.empty()) return 0; struct dqblk dq; if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid, reinterpret_cast<char*>(&dq)) != 0) { PLOG(WARNING) << "Failed to find quota for " << uid; return -1; } if ((dq.dqb_bhardlimit == 0) || (dq.dqb_ihardlimit == 0)) { auto path = create_data_path(uuid ? uuid->c_str() : nullptr); struct statvfs stat; if (statvfs(path.c_str(), &stat) != 0) { PLOG(WARNING) << "Failed to statvfs " << path; return -1; } dq.dqb_valid = QIF_LIMITS; dq.dqb_bhardlimit = (((stat.f_blocks * stat.f_frsize) / 10) * 9) / QIF_DQBLKSIZE; dq.dqb_ihardlimit = (stat.f_files / 2); if (quotactl(QCMD(Q_SETQUOTA, USRQUOTA), device.c_str(), uid, reinterpret_cast<char*>(&dq)) != 0) { PLOG(WARNING) << "Failed to set hard quota for " << uid; return -1; } else { LOG(DEBUG) << "Applied hard quotas for " << uid; return 0; } } else { // Hard quota already set; assume it's reasonable return 0; } } binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) { Loading Loading @@ -358,6 +398,10 @@ binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::s return error("Failed to restorecon " + path); } if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid), uid)) { return error("Failed to set hard quota " + path); } if (property_get_bool("dalvik.vm.usejitprofiles", false)) { const std::string profile_dir = create_primary_current_profile_package_dir_path(userId, pkgname); Loading Loading @@ -709,6 +753,14 @@ binder::Status InstalldNativeService::createUserData(const std::unique_ptr<std:: } } } // Data under /data/media doesn't have an app, but we still want // to limit it to prevent abuse. if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid), multiuser_get_uid(userId, AID_MEDIA_RW))) { return error("Failed to set hard quota for media_rw"); } return ok(); } Loading Loading @@ -2006,6 +2058,17 @@ binder::Status InstalldNativeService::invalidateMounts() { reinterpret_cast<char*>(&dq)) == 0) { LOG(DEBUG) << "Found " << source << " with quota"; mQuotaDevices[target] = source; // ext4 only enables DQUOT_USAGE_ENABLED by default, so we // need to kick it again to enable DQUOT_LIMITS_ENABLED. if (quotactl(QCMD(Q_QUOTAON, USRQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0 && errno != EBUSY) { PLOG(ERROR) << "Failed to enable USRQUOTA on " << source; } if (quotactl(QCMD(Q_QUOTAON, GRPQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0 && errno != EBUSY) { PLOG(ERROR) << "Failed to enable GRPQUOTA on " << source; } } } } Loading cmds/installd/utils.cpp +4 −0 Original line number Diff line number Diff line Loading @@ -1030,6 +1030,10 @@ int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t ta } else if (st.st_gid == gid && actual_mode == target_mode) { // Everything looks good! return 0; } else { // Mismatched GID/mode is recoverable; fall through to update LOG(DEBUG) << "Mismatched cache GID/mode at " << path << ": found " << st.st_gid << " but expected " << gid; } // Directory is owned correctly, but GID or mode mismatch means it's Loading Loading
cmds/installd/InstalldNativeService.cpp +63 −0 Original line number Diff line number Diff line Loading @@ -289,6 +289,46 @@ static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t ui return 0; } /** * Ensure that we have a hard-limit quota to protect against abusive apps; * they should never use more than 90% of blocks or 50% of inodes. */ static int prepare_app_quota(const std::unique_ptr<std::string>& uuid, const std::string& device, uid_t uid) { if (device.empty()) return 0; struct dqblk dq; if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid, reinterpret_cast<char*>(&dq)) != 0) { PLOG(WARNING) << "Failed to find quota for " << uid; return -1; } if ((dq.dqb_bhardlimit == 0) || (dq.dqb_ihardlimit == 0)) { auto path = create_data_path(uuid ? uuid->c_str() : nullptr); struct statvfs stat; if (statvfs(path.c_str(), &stat) != 0) { PLOG(WARNING) << "Failed to statvfs " << path; return -1; } dq.dqb_valid = QIF_LIMITS; dq.dqb_bhardlimit = (((stat.f_blocks * stat.f_frsize) / 10) * 9) / QIF_DQBLKSIZE; dq.dqb_ihardlimit = (stat.f_files / 2); if (quotactl(QCMD(Q_SETQUOTA, USRQUOTA), device.c_str(), uid, reinterpret_cast<char*>(&dq)) != 0) { PLOG(WARNING) << "Failed to set hard quota for " << uid; return -1; } else { LOG(DEBUG) << "Applied hard quotas for " << uid; return 0; } } else { // Hard quota already set; assume it's reasonable return 0; } } binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) { Loading Loading @@ -358,6 +398,10 @@ binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::s return error("Failed to restorecon " + path); } if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid), uid)) { return error("Failed to set hard quota " + path); } if (property_get_bool("dalvik.vm.usejitprofiles", false)) { const std::string profile_dir = create_primary_current_profile_package_dir_path(userId, pkgname); Loading Loading @@ -709,6 +753,14 @@ binder::Status InstalldNativeService::createUserData(const std::unique_ptr<std:: } } } // Data under /data/media doesn't have an app, but we still want // to limit it to prevent abuse. if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid), multiuser_get_uid(userId, AID_MEDIA_RW))) { return error("Failed to set hard quota for media_rw"); } return ok(); } Loading Loading @@ -2006,6 +2058,17 @@ binder::Status InstalldNativeService::invalidateMounts() { reinterpret_cast<char*>(&dq)) == 0) { LOG(DEBUG) << "Found " << source << " with quota"; mQuotaDevices[target] = source; // ext4 only enables DQUOT_USAGE_ENABLED by default, so we // need to kick it again to enable DQUOT_LIMITS_ENABLED. if (quotactl(QCMD(Q_QUOTAON, USRQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0 && errno != EBUSY) { PLOG(ERROR) << "Failed to enable USRQUOTA on " << source; } if (quotactl(QCMD(Q_QUOTAON, GRPQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0 && errno != EBUSY) { PLOG(ERROR) << "Failed to enable GRPQUOTA on " << source; } } } } Loading
cmds/installd/utils.cpp +4 −0 Original line number Diff line number Diff line Loading @@ -1030,6 +1030,10 @@ int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t ta } else if (st.st_gid == gid && actual_mode == target_mode) { // Everything looks good! return 0; } else { // Mismatched GID/mode is recoverable; fall through to update LOG(DEBUG) << "Mismatched cache GID/mode at " << path << ": found " << st.st_gid << " but expected " << gid; } // Directory is owned correctly, but GID or mode mismatch means it's Loading