Loading init/snapuserd_transition.cpp +58 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <cutils/sockets.h> #include <fs_avb/fs_avb.h> #include <libsnapshot/snapshot.h> #include <libsnapshot/snapuserd_client.h> #include <private/android_filesystem_config.h> Loading Loading @@ -227,6 +228,56 @@ void SnapuserdSelinuxHelper::FinishTransition() { } } /* * Before starting init second stage, we will wait * for snapuserd daemon to be up and running; bionic libc * may read /system/etc/selinux/plat_property_contexts file * before invoking main() function. This will happen if * init initializes property during second stage. Any access * to /system without snapuserd daemon will lead to a deadlock. * * Thus, we do a simple probe by reading system partition. This * read will eventually be serviced by daemon confirming that * daemon is up and running. Furthermore, we are still in the kernel * domain and sepolicy has not been enforced yet. Thus, access * to these device mapper block devices are ok even though * we may see audit logs. */ bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() { std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix(); android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT)); if (fd < 0) { PLOG(ERROR) << "open " << dev << " failed"; return false; } void* addr; ssize_t page_size = getpagesize(); if (posix_memalign(&addr, page_size, page_size) < 0) { PLOG(ERROR) << "posix_memalign with page size " << page_size; return false; } std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free); int iter = 0; while (iter < 10) { ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), buffer.get(), page_size, 0)); if (n < 0) { // Wait for sometime before retry std::this_thread::sleep_for(100ms); } else if (n == page_size) { return true; } else { LOG(ERROR) << "pread returned: " << n << " from: " << dev << " expected: " << page_size; } iter += 1; } return false; } void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() { auto fd = GetRamdiskSnapuserdFd(); if (!fd) { Loading @@ -248,6 +299,13 @@ void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() { setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1); LOG(INFO) << "Relaunched snapuserd with pid: " << pid; if (!TestSnapuserdIsReady()) { PLOG(FATAL) << "snapuserd daemon failed to launch"; } else { LOG(INFO) << "snapuserd daemon is up and running"; } return; } Loading init/snapuserd_transition.h +1 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ class SnapuserdSelinuxHelper final { private: void RelaunchFirstStageSnapuserd(); void ExecSnapuserd(); bool TestSnapuserdIsReady(); std::unique_ptr<SnapshotManager> sm_; BlockDevInitializer block_dev_init_; Loading libprocessgroup/include/processgroup/processgroup.h +2 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& pr #ifndef __ANDROID_VNDK__ bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles); static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc"; bool UsePerAppMemcg(); Loading libprocessgroup/processgroup.cpp +7 −2 Original line number Diff line number Diff line Loading @@ -112,11 +112,16 @@ static bool isMemoryCgroupSupported() { } void DropTaskProfilesResourceCaching() { TaskProfiles::GetInstance().DropResourceCaching(); TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_TASK); TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_PROCESS); } bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) { return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles); return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, false); } bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) { return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, true); } bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) { Loading libprocessgroup/task_profiles.cpp +189 −107 Original line number Diff line number Diff line Loading @@ -51,6 +51,67 @@ static constexpr const char* TASK_PROFILE_DB_VENDOR_FILE = "/vendor/etc/task_pro static constexpr const char* TEMPLATE_TASK_PROFILE_API_FILE = "/etc/task_profiles/task_profiles_%u.json"; class FdCacheHelper { public: enum FdState { FDS_INACCESSIBLE = -1, FDS_APP_DEPENDENT = -2, FDS_NOT_CACHED = -3, }; static void Cache(const std::string& path, android::base::unique_fd& fd); static void Drop(android::base::unique_fd& fd); static void Init(const std::string& path, android::base::unique_fd& fd); static bool IsCached(const android::base::unique_fd& fd) { return fd > FDS_INACCESSIBLE; } private: static bool IsAppDependentPath(const std::string& path); }; void FdCacheHelper::Init(const std::string& path, android::base::unique_fd& fd) { // file descriptors for app-dependent paths can't be cached if (IsAppDependentPath(path)) { // file descriptor is not cached fd.reset(FDS_APP_DEPENDENT); return; } // file descriptor can be cached later on request fd.reset(FDS_NOT_CACHED); } void FdCacheHelper::Cache(const std::string& path, android::base::unique_fd& fd) { if (fd != FDS_NOT_CACHED) { return; } if (access(path.c_str(), W_OK) != 0) { // file is not accessible fd.reset(FDS_INACCESSIBLE); return; } unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC))); if (tmp_fd < 0) { PLOG(ERROR) << "Failed to cache fd '" << path << "'"; fd.reset(FDS_INACCESSIBLE); return; } fd = std::move(tmp_fd); } void FdCacheHelper::Drop(android::base::unique_fd& fd) { if (fd == FDS_NOT_CACHED) { return; } fd.reset(FDS_NOT_CACHED); } bool FdCacheHelper::IsAppDependentPath(const std::string& path) { return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos; } void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) { controller_ = controller; file_name_ = file_name; Loading Loading @@ -144,57 +205,11 @@ bool SetAttributeAction::ExecuteForTask(int tid) const { return true; } void CachedFdProfileAction::EnableResourceCaching() { std::lock_guard<std::mutex> lock(fd_mutex_); if (fd_ != FDS_NOT_CACHED) { return; } std::string tasks_path = GetPath(); if (access(tasks_path.c_str(), W_OK) != 0) { // file is not accessible fd_.reset(FDS_INACCESSIBLE); return; } unique_fd fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC))); if (fd < 0) { PLOG(ERROR) << "Failed to cache fd '" << tasks_path << "'"; fd_.reset(FDS_INACCESSIBLE); return; } fd_ = std::move(fd); } void CachedFdProfileAction::DropResourceCaching() { std::lock_guard<std::mutex> lock(fd_mutex_); if (fd_ == FDS_NOT_CACHED) { return; } fd_.reset(FDS_NOT_CACHED); } bool CachedFdProfileAction::IsAppDependentPath(const std::string& path) { return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos; } void CachedFdProfileAction::InitFd(const std::string& path) { // file descriptors for app-dependent paths can't be cached if (IsAppDependentPath(path)) { // file descriptor is not cached fd_.reset(FDS_APP_DEPENDENT); return; } // file descriptor can be cached later on request fd_.reset(FDS_NOT_CACHED); } SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p) : controller_(c), path_(p) { InitFd(controller_.GetTasksFilePath(path_)); FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]); // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]); } bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) { Loading Loading @@ -232,7 +247,40 @@ bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_nam return false; } ProfileAction::CacheUseResult SetCgroupAction::UseCachedFd(ResourceCacheType cache_type, int id) const { std::lock_guard<std::mutex> lock(fd_mutex_); if (FdCacheHelper::IsCached(fd_[cache_type])) { // fd is cached, reuse it if (!AddTidToCgroup(id, fd_[cache_type], controller()->name())) { LOG(ERROR) << "Failed to add task into cgroup"; return ProfileAction::FAIL; } return ProfileAction::SUCCESS; } if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) { // no permissions to access the file, ignore return ProfileAction::SUCCESS; } if (cache_type == ResourceCacheType::RCT_TASK && fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) { // application-dependent path can't be used with tid PLOG(ERROR) << "Application profile can't be applied to a thread"; return ProfileAction::FAIL; } return ProfileAction::UNUSED; } bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const { CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, pid); if (result != ProfileAction::UNUSED) { return result == ProfileAction::SUCCESS; } // fd was not cached or cached fd can't be used std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid); unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC))); if (tmp_fd < 0) { Loading @@ -248,28 +296,12 @@ bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const { } bool SetCgroupAction::ExecuteForTask(int tid) const { std::lock_guard<std::mutex> lock(fd_mutex_); if (IsFdValid()) { // fd is cached, reuse it if (!AddTidToCgroup(tid, fd_, controller()->name())) { LOG(ERROR) << "Failed to add task into cgroup"; return false; } return true; } if (fd_ == FDS_INACCESSIBLE) { // no permissions to access the file, ignore return true; } if (fd_ == FDS_APP_DEPENDENT) { // application-dependent path can't be used with tid PLOG(ERROR) << "Application profile can't be applied to a thread"; return false; CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, tid); if (result != ProfileAction::UNUSED) { return result == ProfileAction::SUCCESS; } // fd was not cached because cached fd can't be used // fd was not cached or cached fd can't be used std::string tasks_path = controller()->GetTasksFilePath(path_); unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC))); if (tmp_fd < 0) { Loading @@ -284,10 +316,36 @@ bool SetCgroupAction::ExecuteForTask(int tid) const { return true; } void SetCgroupAction::EnableResourceCaching(ResourceCacheType cache_type) { std::lock_guard<std::mutex> lock(fd_mutex_); // Return early to prevent unnecessary calls to controller_.Get{Tasks|Procs}FilePath() which // include regex evaluations if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) { return; } switch (cache_type) { case (ProfileAction::RCT_TASK): FdCacheHelper::Cache(controller_.GetTasksFilePath(path_), fd_[cache_type]); break; case (ProfileAction::RCT_PROCESS): // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them FdCacheHelper::Cache(controller_.GetProcsFilePath(path_, 0, 0), fd_[cache_type]); break; default: LOG(ERROR) << "Invalid cache type is specified!"; break; } } void SetCgroupAction::DropResourceCaching(ResourceCacheType cache_type) { std::lock_guard<std::mutex> lock(fd_mutex_); FdCacheHelper::Drop(fd_[cache_type]); } WriteFileAction::WriteFileAction(const std::string& path, const std::string& value, bool logfailures) : path_(path), value_(value), logfailures_(logfailures) { InitFd(path_); FdCacheHelper::Init(path_, fd_); } bool WriteFileAction::WriteValueToFile(const std::string& value, const std::string& path, Loading @@ -309,13 +367,43 @@ bool WriteFileAction::WriteValueToFile(const std::string& value, const std::stri return true; } bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const { ProfileAction::CacheUseResult WriteFileAction::UseCachedFd(ResourceCacheType cache_type, const std::string& value) const { std::lock_guard<std::mutex> lock(fd_mutex_); if (FdCacheHelper::IsCached(fd_)) { // fd is cached, reuse it if (!WriteStringToFd(value, fd_)) { if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_; return ProfileAction::FAIL; } return ProfileAction::SUCCESS; } if (fd_ == FdCacheHelper::FDS_INACCESSIBLE) { // no permissions to access the file, ignore return ProfileAction::SUCCESS; } if (cache_type == ResourceCacheType::RCT_TASK && fd_ == FdCacheHelper::FDS_APP_DEPENDENT) { // application-dependent path can't be used with tid PLOG(ERROR) << "Application profile can't be applied to a thread"; return ProfileAction::FAIL; } return ProfileAction::UNUSED; } bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const { std::string value(value_); std::string path(path_); value = StringReplace(value, "<uid>", std::to_string(uid), true); value = StringReplace(value, "<pid>", std::to_string(pid), true); CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, value); if (result != ProfileAction::UNUSED) { return result == ProfileAction::SUCCESS; } std::string path(path_); path = StringReplace(path, "<uid>", std::to_string(uid), true); path = StringReplace(path, "<pid>", std::to_string(pid), true); Loading @@ -323,41 +411,33 @@ bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const { } bool WriteFileAction::ExecuteForTask(int tid) const { std::lock_guard<std::mutex> lock(fd_mutex_); std::string value(value_); int uid = getuid(); value = StringReplace(value, "<uid>", std::to_string(uid), true); value = StringReplace(value, "<pid>", std::to_string(tid), true); if (IsFdValid()) { // fd is cached, reuse it if (!WriteStringToFd(value, fd_)) { if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_; return false; } return true; CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, value); if (result != ProfileAction::UNUSED) { return result == ProfileAction::SUCCESS; } if (fd_ == FDS_INACCESSIBLE) { // no permissions to access the file, ignore return true; return WriteValueToFile(value, path_, logfailures_); } if (fd_ == FDS_APP_DEPENDENT) { // application-dependent path can't be used with tid PLOG(ERROR) << "Application profile can't be applied to a thread"; return false; void WriteFileAction::EnableResourceCaching(ResourceCacheType) { std::lock_guard<std::mutex> lock(fd_mutex_); FdCacheHelper::Cache(path_, fd_); } return WriteValueToFile(value, path_, logfailures_); void WriteFileAction::DropResourceCaching(ResourceCacheType) { std::lock_guard<std::mutex> lock(fd_mutex_); FdCacheHelper::Drop(fd_); } bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const { for (const auto& profile : profiles_) { if (!profile->ExecuteForProcess(uid, pid)) { PLOG(WARNING) << "ExecuteForProcess failed for aggregate profile"; } profile->ExecuteForProcess(uid, pid); } return true; } Loading @@ -369,15 +449,15 @@ bool ApplyProfileAction::ExecuteForTask(int tid) const { return true; } void ApplyProfileAction::EnableResourceCaching() { void ApplyProfileAction::EnableResourceCaching(ResourceCacheType cache_type) { for (const auto& profile : profiles_) { profile->EnableResourceCaching(); profile->EnableResourceCaching(cache_type); } } void ApplyProfileAction::DropResourceCaching() { void ApplyProfileAction::DropResourceCaching(ResourceCacheType cache_type) { for (const auto& profile : profiles_) { profile->DropResourceCaching(); profile->DropResourceCaching(cache_type); } } Loading Loading @@ -407,33 +487,33 @@ bool TaskProfile::ExecuteForTask(int tid) const { return true; } void TaskProfile::EnableResourceCaching() { void TaskProfile::EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) { if (res_cached_) { return; } for (auto& element : elements_) { element->EnableResourceCaching(); element->EnableResourceCaching(cache_type); } res_cached_ = true; } void TaskProfile::DropResourceCaching() { void TaskProfile::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) { if (!res_cached_) { return; } for (auto& element : elements_) { element->DropResourceCaching(); element->DropResourceCaching(cache_type); } res_cached_ = false; } void TaskProfiles::DropResourceCaching() const { void TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const { for (auto& iter : profiles_) { iter.second->DropResourceCaching(); iter.second->DropResourceCaching(cache_type); } } Loading @@ -457,8 +537,7 @@ TaskProfiles::TaskProfiles() { android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level); if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) { if (!Load(CgroupMap::GetInstance(), api_profiles_path)) { LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid() << "] failed"; LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid() << "] failed"; } } } Loading Loading @@ -651,10 +730,13 @@ const ProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) cons } bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) { const std::vector<std::string>& profiles, bool use_fd_cache) { for (const auto& name : profiles) { TaskProfile* profile = GetProfile(name); if (profile != nullptr) { if (use_fd_cache) { profile->EnableResourceCaching(ProfileAction::RCT_PROCESS); } if (!profile->ExecuteForProcess(uid, pid)) { PLOG(WARNING) << "Failed to apply " << name << " process profile"; } Loading @@ -671,7 +753,7 @@ bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& prof TaskProfile* profile = GetProfile(name); if (profile != nullptr) { if (use_fd_cache) { profile->EnableResourceCaching(); profile->EnableResourceCaching(ProfileAction::RCT_TASK); } if (!profile->ExecuteForTask(tid)) { PLOG(WARNING) << "Failed to apply " << name << " task profile"; Loading Loading
init/snapuserd_transition.cpp +58 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <cutils/sockets.h> #include <fs_avb/fs_avb.h> #include <libsnapshot/snapshot.h> #include <libsnapshot/snapuserd_client.h> #include <private/android_filesystem_config.h> Loading Loading @@ -227,6 +228,56 @@ void SnapuserdSelinuxHelper::FinishTransition() { } } /* * Before starting init second stage, we will wait * for snapuserd daemon to be up and running; bionic libc * may read /system/etc/selinux/plat_property_contexts file * before invoking main() function. This will happen if * init initializes property during second stage. Any access * to /system without snapuserd daemon will lead to a deadlock. * * Thus, we do a simple probe by reading system partition. This * read will eventually be serviced by daemon confirming that * daemon is up and running. Furthermore, we are still in the kernel * domain and sepolicy has not been enforced yet. Thus, access * to these device mapper block devices are ok even though * we may see audit logs. */ bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() { std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix(); android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT)); if (fd < 0) { PLOG(ERROR) << "open " << dev << " failed"; return false; } void* addr; ssize_t page_size = getpagesize(); if (posix_memalign(&addr, page_size, page_size) < 0) { PLOG(ERROR) << "posix_memalign with page size " << page_size; return false; } std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free); int iter = 0; while (iter < 10) { ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), buffer.get(), page_size, 0)); if (n < 0) { // Wait for sometime before retry std::this_thread::sleep_for(100ms); } else if (n == page_size) { return true; } else { LOG(ERROR) << "pread returned: " << n << " from: " << dev << " expected: " << page_size; } iter += 1; } return false; } void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() { auto fd = GetRamdiskSnapuserdFd(); if (!fd) { Loading @@ -248,6 +299,13 @@ void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() { setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1); LOG(INFO) << "Relaunched snapuserd with pid: " << pid; if (!TestSnapuserdIsReady()) { PLOG(FATAL) << "snapuserd daemon failed to launch"; } else { LOG(INFO) << "snapuserd daemon is up and running"; } return; } Loading
init/snapuserd_transition.h +1 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ class SnapuserdSelinuxHelper final { private: void RelaunchFirstStageSnapuserd(); void ExecSnapuserd(); bool TestSnapuserdIsReady(); std::unique_ptr<SnapshotManager> sm_; BlockDevInitializer block_dev_init_; Loading
libprocessgroup/include/processgroup/processgroup.h +2 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& pr #ifndef __ANDROID_VNDK__ bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles); static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc"; bool UsePerAppMemcg(); Loading
libprocessgroup/processgroup.cpp +7 −2 Original line number Diff line number Diff line Loading @@ -112,11 +112,16 @@ static bool isMemoryCgroupSupported() { } void DropTaskProfilesResourceCaching() { TaskProfiles::GetInstance().DropResourceCaching(); TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_TASK); TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_PROCESS); } bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) { return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles); return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, false); } bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) { return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, true); } bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) { Loading
libprocessgroup/task_profiles.cpp +189 −107 Original line number Diff line number Diff line Loading @@ -51,6 +51,67 @@ static constexpr const char* TASK_PROFILE_DB_VENDOR_FILE = "/vendor/etc/task_pro static constexpr const char* TEMPLATE_TASK_PROFILE_API_FILE = "/etc/task_profiles/task_profiles_%u.json"; class FdCacheHelper { public: enum FdState { FDS_INACCESSIBLE = -1, FDS_APP_DEPENDENT = -2, FDS_NOT_CACHED = -3, }; static void Cache(const std::string& path, android::base::unique_fd& fd); static void Drop(android::base::unique_fd& fd); static void Init(const std::string& path, android::base::unique_fd& fd); static bool IsCached(const android::base::unique_fd& fd) { return fd > FDS_INACCESSIBLE; } private: static bool IsAppDependentPath(const std::string& path); }; void FdCacheHelper::Init(const std::string& path, android::base::unique_fd& fd) { // file descriptors for app-dependent paths can't be cached if (IsAppDependentPath(path)) { // file descriptor is not cached fd.reset(FDS_APP_DEPENDENT); return; } // file descriptor can be cached later on request fd.reset(FDS_NOT_CACHED); } void FdCacheHelper::Cache(const std::string& path, android::base::unique_fd& fd) { if (fd != FDS_NOT_CACHED) { return; } if (access(path.c_str(), W_OK) != 0) { // file is not accessible fd.reset(FDS_INACCESSIBLE); return; } unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC))); if (tmp_fd < 0) { PLOG(ERROR) << "Failed to cache fd '" << path << "'"; fd.reset(FDS_INACCESSIBLE); return; } fd = std::move(tmp_fd); } void FdCacheHelper::Drop(android::base::unique_fd& fd) { if (fd == FDS_NOT_CACHED) { return; } fd.reset(FDS_NOT_CACHED); } bool FdCacheHelper::IsAppDependentPath(const std::string& path) { return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos; } void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) { controller_ = controller; file_name_ = file_name; Loading Loading @@ -144,57 +205,11 @@ bool SetAttributeAction::ExecuteForTask(int tid) const { return true; } void CachedFdProfileAction::EnableResourceCaching() { std::lock_guard<std::mutex> lock(fd_mutex_); if (fd_ != FDS_NOT_CACHED) { return; } std::string tasks_path = GetPath(); if (access(tasks_path.c_str(), W_OK) != 0) { // file is not accessible fd_.reset(FDS_INACCESSIBLE); return; } unique_fd fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC))); if (fd < 0) { PLOG(ERROR) << "Failed to cache fd '" << tasks_path << "'"; fd_.reset(FDS_INACCESSIBLE); return; } fd_ = std::move(fd); } void CachedFdProfileAction::DropResourceCaching() { std::lock_guard<std::mutex> lock(fd_mutex_); if (fd_ == FDS_NOT_CACHED) { return; } fd_.reset(FDS_NOT_CACHED); } bool CachedFdProfileAction::IsAppDependentPath(const std::string& path) { return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos; } void CachedFdProfileAction::InitFd(const std::string& path) { // file descriptors for app-dependent paths can't be cached if (IsAppDependentPath(path)) { // file descriptor is not cached fd_.reset(FDS_APP_DEPENDENT); return; } // file descriptor can be cached later on request fd_.reset(FDS_NOT_CACHED); } SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p) : controller_(c), path_(p) { InitFd(controller_.GetTasksFilePath(path_)); FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]); // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]); } bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) { Loading Loading @@ -232,7 +247,40 @@ bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_nam return false; } ProfileAction::CacheUseResult SetCgroupAction::UseCachedFd(ResourceCacheType cache_type, int id) const { std::lock_guard<std::mutex> lock(fd_mutex_); if (FdCacheHelper::IsCached(fd_[cache_type])) { // fd is cached, reuse it if (!AddTidToCgroup(id, fd_[cache_type], controller()->name())) { LOG(ERROR) << "Failed to add task into cgroup"; return ProfileAction::FAIL; } return ProfileAction::SUCCESS; } if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) { // no permissions to access the file, ignore return ProfileAction::SUCCESS; } if (cache_type == ResourceCacheType::RCT_TASK && fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) { // application-dependent path can't be used with tid PLOG(ERROR) << "Application profile can't be applied to a thread"; return ProfileAction::FAIL; } return ProfileAction::UNUSED; } bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const { CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, pid); if (result != ProfileAction::UNUSED) { return result == ProfileAction::SUCCESS; } // fd was not cached or cached fd can't be used std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid); unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC))); if (tmp_fd < 0) { Loading @@ -248,28 +296,12 @@ bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const { } bool SetCgroupAction::ExecuteForTask(int tid) const { std::lock_guard<std::mutex> lock(fd_mutex_); if (IsFdValid()) { // fd is cached, reuse it if (!AddTidToCgroup(tid, fd_, controller()->name())) { LOG(ERROR) << "Failed to add task into cgroup"; return false; } return true; } if (fd_ == FDS_INACCESSIBLE) { // no permissions to access the file, ignore return true; } if (fd_ == FDS_APP_DEPENDENT) { // application-dependent path can't be used with tid PLOG(ERROR) << "Application profile can't be applied to a thread"; return false; CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, tid); if (result != ProfileAction::UNUSED) { return result == ProfileAction::SUCCESS; } // fd was not cached because cached fd can't be used // fd was not cached or cached fd can't be used std::string tasks_path = controller()->GetTasksFilePath(path_); unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC))); if (tmp_fd < 0) { Loading @@ -284,10 +316,36 @@ bool SetCgroupAction::ExecuteForTask(int tid) const { return true; } void SetCgroupAction::EnableResourceCaching(ResourceCacheType cache_type) { std::lock_guard<std::mutex> lock(fd_mutex_); // Return early to prevent unnecessary calls to controller_.Get{Tasks|Procs}FilePath() which // include regex evaluations if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) { return; } switch (cache_type) { case (ProfileAction::RCT_TASK): FdCacheHelper::Cache(controller_.GetTasksFilePath(path_), fd_[cache_type]); break; case (ProfileAction::RCT_PROCESS): // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them FdCacheHelper::Cache(controller_.GetProcsFilePath(path_, 0, 0), fd_[cache_type]); break; default: LOG(ERROR) << "Invalid cache type is specified!"; break; } } void SetCgroupAction::DropResourceCaching(ResourceCacheType cache_type) { std::lock_guard<std::mutex> lock(fd_mutex_); FdCacheHelper::Drop(fd_[cache_type]); } WriteFileAction::WriteFileAction(const std::string& path, const std::string& value, bool logfailures) : path_(path), value_(value), logfailures_(logfailures) { InitFd(path_); FdCacheHelper::Init(path_, fd_); } bool WriteFileAction::WriteValueToFile(const std::string& value, const std::string& path, Loading @@ -309,13 +367,43 @@ bool WriteFileAction::WriteValueToFile(const std::string& value, const std::stri return true; } bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const { ProfileAction::CacheUseResult WriteFileAction::UseCachedFd(ResourceCacheType cache_type, const std::string& value) const { std::lock_guard<std::mutex> lock(fd_mutex_); if (FdCacheHelper::IsCached(fd_)) { // fd is cached, reuse it if (!WriteStringToFd(value, fd_)) { if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_; return ProfileAction::FAIL; } return ProfileAction::SUCCESS; } if (fd_ == FdCacheHelper::FDS_INACCESSIBLE) { // no permissions to access the file, ignore return ProfileAction::SUCCESS; } if (cache_type == ResourceCacheType::RCT_TASK && fd_ == FdCacheHelper::FDS_APP_DEPENDENT) { // application-dependent path can't be used with tid PLOG(ERROR) << "Application profile can't be applied to a thread"; return ProfileAction::FAIL; } return ProfileAction::UNUSED; } bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const { std::string value(value_); std::string path(path_); value = StringReplace(value, "<uid>", std::to_string(uid), true); value = StringReplace(value, "<pid>", std::to_string(pid), true); CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, value); if (result != ProfileAction::UNUSED) { return result == ProfileAction::SUCCESS; } std::string path(path_); path = StringReplace(path, "<uid>", std::to_string(uid), true); path = StringReplace(path, "<pid>", std::to_string(pid), true); Loading @@ -323,41 +411,33 @@ bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const { } bool WriteFileAction::ExecuteForTask(int tid) const { std::lock_guard<std::mutex> lock(fd_mutex_); std::string value(value_); int uid = getuid(); value = StringReplace(value, "<uid>", std::to_string(uid), true); value = StringReplace(value, "<pid>", std::to_string(tid), true); if (IsFdValid()) { // fd is cached, reuse it if (!WriteStringToFd(value, fd_)) { if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_; return false; } return true; CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, value); if (result != ProfileAction::UNUSED) { return result == ProfileAction::SUCCESS; } if (fd_ == FDS_INACCESSIBLE) { // no permissions to access the file, ignore return true; return WriteValueToFile(value, path_, logfailures_); } if (fd_ == FDS_APP_DEPENDENT) { // application-dependent path can't be used with tid PLOG(ERROR) << "Application profile can't be applied to a thread"; return false; void WriteFileAction::EnableResourceCaching(ResourceCacheType) { std::lock_guard<std::mutex> lock(fd_mutex_); FdCacheHelper::Cache(path_, fd_); } return WriteValueToFile(value, path_, logfailures_); void WriteFileAction::DropResourceCaching(ResourceCacheType) { std::lock_guard<std::mutex> lock(fd_mutex_); FdCacheHelper::Drop(fd_); } bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const { for (const auto& profile : profiles_) { if (!profile->ExecuteForProcess(uid, pid)) { PLOG(WARNING) << "ExecuteForProcess failed for aggregate profile"; } profile->ExecuteForProcess(uid, pid); } return true; } Loading @@ -369,15 +449,15 @@ bool ApplyProfileAction::ExecuteForTask(int tid) const { return true; } void ApplyProfileAction::EnableResourceCaching() { void ApplyProfileAction::EnableResourceCaching(ResourceCacheType cache_type) { for (const auto& profile : profiles_) { profile->EnableResourceCaching(); profile->EnableResourceCaching(cache_type); } } void ApplyProfileAction::DropResourceCaching() { void ApplyProfileAction::DropResourceCaching(ResourceCacheType cache_type) { for (const auto& profile : profiles_) { profile->DropResourceCaching(); profile->DropResourceCaching(cache_type); } } Loading Loading @@ -407,33 +487,33 @@ bool TaskProfile::ExecuteForTask(int tid) const { return true; } void TaskProfile::EnableResourceCaching() { void TaskProfile::EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) { if (res_cached_) { return; } for (auto& element : elements_) { element->EnableResourceCaching(); element->EnableResourceCaching(cache_type); } res_cached_ = true; } void TaskProfile::DropResourceCaching() { void TaskProfile::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) { if (!res_cached_) { return; } for (auto& element : elements_) { element->DropResourceCaching(); element->DropResourceCaching(cache_type); } res_cached_ = false; } void TaskProfiles::DropResourceCaching() const { void TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const { for (auto& iter : profiles_) { iter.second->DropResourceCaching(); iter.second->DropResourceCaching(cache_type); } } Loading @@ -457,8 +537,7 @@ TaskProfiles::TaskProfiles() { android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level); if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) { if (!Load(CgroupMap::GetInstance(), api_profiles_path)) { LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid() << "] failed"; LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid() << "] failed"; } } } Loading Loading @@ -651,10 +730,13 @@ const ProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) cons } bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) { const std::vector<std::string>& profiles, bool use_fd_cache) { for (const auto& name : profiles) { TaskProfile* profile = GetProfile(name); if (profile != nullptr) { if (use_fd_cache) { profile->EnableResourceCaching(ProfileAction::RCT_PROCESS); } if (!profile->ExecuteForProcess(uid, pid)) { PLOG(WARNING) << "Failed to apply " << name << " process profile"; } Loading @@ -671,7 +753,7 @@ bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& prof TaskProfile* profile = GetProfile(name); if (profile != nullptr) { if (use_fd_cache) { profile->EnableResourceCaching(); profile->EnableResourceCaching(ProfileAction::RCT_TASK); } if (!profile->ExecuteForTask(tid)) { PLOG(WARNING) << "Failed to apply " << name << " task profile"; Loading