1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "libprocessgroup"
19 
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <task_profiles.h>
24 #include <string>
25 
26 #include <android-base/file.h>
27 #include <android-base/logging.h>
28 #include <android-base/properties.h>
29 #include <android-base/stringprintf.h>
30 #include <android-base/strings.h>
31 #include <android-base/threads.h>
32 
33 #include <cutils/android_filesystem_config.h>
34 
35 #include <json/reader.h>
36 #include <json/value.h>
37 
38 #include <build_flags.h>
39 
40 // To avoid issues in sdk_mac build
41 #if defined(__ANDROID__)
42 #include <sys/prctl.h>
43 #endif
44 
45 using android::base::GetThreadId;
46 using android::base::GetUintProperty;
47 using android::base::StringPrintf;
48 using android::base::StringReplace;
49 using android::base::unique_fd;
50 using android::base::WriteStringToFile;
51 
52 static constexpr const char* TASK_PROFILE_DB_FILE = "/etc/task_profiles.json";
53 static constexpr const char* TASK_PROFILE_DB_VENDOR_FILE = "/vendor/etc/task_profiles.json";
54 
55 static constexpr const char* TEMPLATE_TASK_PROFILE_API_FILE =
56         "/etc/task_profiles/task_profiles_%u.json";
57 
58 class FdCacheHelper {
59   public:
60     enum FdState {
61         FDS_INACCESSIBLE = -1,
62         FDS_APP_DEPENDENT = -2,
63         FDS_NOT_CACHED = -3,
64     };
65 
66     static void Cache(const std::string& path, android::base::unique_fd& fd);
67     static void Drop(android::base::unique_fd& fd);
68     static void Init(const std::string& path, android::base::unique_fd& fd);
IsCached(const android::base::unique_fd & fd)69     static bool IsCached(const android::base::unique_fd& fd) { return fd > FDS_INACCESSIBLE; }
70 
71   private:
72     static bool IsAppDependentPath(const std::string& path);
73 };
74 
Init(const std::string & path,android::base::unique_fd & fd)75 void FdCacheHelper::Init(const std::string& path, android::base::unique_fd& fd) {
76     // file descriptors for app-dependent paths can't be cached
77     if (IsAppDependentPath(path)) {
78         // file descriptor is not cached
79         fd.reset(FDS_APP_DEPENDENT);
80         return;
81     }
82     // file descriptor can be cached later on request
83     fd.reset(FDS_NOT_CACHED);
84 }
85 
Cache(const std::string & path,android::base::unique_fd & fd)86 void FdCacheHelper::Cache(const std::string& path, android::base::unique_fd& fd) {
87     if (fd != FDS_NOT_CACHED) {
88         return;
89     }
90 
91     if (access(path.c_str(), W_OK) != 0) {
92         // file is not accessible
93         fd.reset(FDS_INACCESSIBLE);
94         return;
95     }
96 
97     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
98     if (tmp_fd < 0) {
99         PLOG(ERROR) << "Failed to cache fd '" << path << "'";
100         fd.reset(FDS_INACCESSIBLE);
101         return;
102     }
103 
104     fd = std::move(tmp_fd);
105 }
106 
Drop(android::base::unique_fd & fd)107 void FdCacheHelper::Drop(android::base::unique_fd& fd) {
108     if (fd == FDS_NOT_CACHED) {
109         return;
110     }
111 
112     fd.reset(FDS_NOT_CACHED);
113 }
114 
IsAppDependentPath(const std::string & path)115 bool FdCacheHelper::IsAppDependentPath(const std::string& path) {
116     return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
117 }
118 
119 IProfileAttribute::~IProfileAttribute() = default;
120 
file_name() const121 const std::string& ProfileAttribute::file_name() const {
122     if (controller()->version() == 2 && !file_v2_name_.empty()) return file_v2_name_;
123     return file_name_;
124 }
125 
Reset(const CgroupController & controller,const std::string & file_name,const std::string & file_v2_name)126 void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name,
127                              const std::string& file_v2_name) {
128     controller_ = controller;
129     file_name_ = file_name;
130     file_v2_name_ = file_v2_name;
131 }
132 
isSystemApp(uid_t uid)133 static bool isSystemApp(uid_t uid) {
134     return uid < AID_APP_START;
135 }
136 
ConvertUidToPath(const char * root_cgroup_path,uid_t uid)137 std::string ConvertUidToPath(const char* root_cgroup_path, uid_t uid) {
138     if (android::libprocessgroup_flags::cgroup_v2_sys_app_isolation()) {
139         if (isSystemApp(uid))
140             return StringPrintf("%s/system/uid_%u", root_cgroup_path, uid);
141         else
142             return StringPrintf("%s/apps/uid_%u", root_cgroup_path, uid);
143     }
144     return StringPrintf("%s/uid_%u", root_cgroup_path, uid);
145 }
146 
ConvertUidPidToPath(const char * root_cgroup_path,uid_t uid,pid_t pid)147 std::string ConvertUidPidToPath(const char* root_cgroup_path, uid_t uid, pid_t pid) {
148     const std::string uid_path = ConvertUidToPath(root_cgroup_path, uid);
149     return StringPrintf("%s/pid_%d", uid_path.c_str(), pid);
150 }
151 
GetPathForProcess(uid_t uid,pid_t pid,std::string * path) const152 bool ProfileAttribute::GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const {
153     if (controller()->version() == 2) {
154         const std::string cgroup_path = ConvertUidPidToPath(controller()->path(), uid, pid);
155         *path = cgroup_path + "/" + file_name();
156         return true;
157     }
158     return GetPathForTask(pid, path);
159 }
160 
GetPathForTask(pid_t tid,std::string * path) const161 bool ProfileAttribute::GetPathForTask(pid_t tid, std::string* path) const {
162     std::string subgroup;
163     if (!controller()->GetTaskGroup(tid, &subgroup)) {
164         return false;
165     }
166 
167     if (path == nullptr) {
168         return true;
169     }
170 
171     if (subgroup.empty()) {
172         *path = StringPrintf("%s/%s", controller()->path(), file_name().c_str());
173     } else {
174         *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
175                              file_name().c_str());
176     }
177     return true;
178 }
179 
180 // NOTE: This function is for cgroup v2 only
GetPathForUID(uid_t uid,std::string * path) const181 bool ProfileAttribute::GetPathForUID(uid_t uid, std::string* path) const {
182     if (path == nullptr) {
183         return true;
184     }
185 
186     const std::string cgroup_path = ConvertUidToPath(controller()->path(), uid);
187     *path = cgroup_path + "/" + file_name();
188     return true;
189 }
190 
ExecuteForProcess(uid_t,pid_t) const191 bool SetClampsAction::ExecuteForProcess(uid_t, pid_t) const {
192     // TODO: add support when kernel supports util_clamp
193     LOG(WARNING) << "SetClampsAction::ExecuteForProcess is not supported";
194     return false;
195 }
196 
ExecuteForTask(int) const197 bool SetClampsAction::ExecuteForTask(int) const {
198     // TODO: add support when kernel supports util_clamp
199     LOG(WARNING) << "SetClampsAction::ExecuteForTask is not supported";
200     return false;
201 }
202 
203 // To avoid issues in sdk_mac build
204 #if defined(__ANDROID__)
205 
IsTimerSlackSupported(pid_t tid)206 bool SetTimerSlackAction::IsTimerSlackSupported(pid_t tid) {
207     auto file = StringPrintf("/proc/%d/timerslack_ns", tid);
208 
209     return (access(file.c_str(), W_OK) == 0);
210 }
211 
ExecuteForTask(pid_t tid) const212 bool SetTimerSlackAction::ExecuteForTask(pid_t tid) const {
213     static bool sys_supports_timerslack = IsTimerSlackSupported(tid);
214 
215     // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
216     // TODO: once we've backported this, log if the open(2) fails.
217     if (sys_supports_timerslack) {
218         auto file = StringPrintf("/proc/%d/timerslack_ns", tid);
219         if (!WriteStringToFile(std::to_string(slack_), file)) {
220             if (errno == ENOENT) {
221                 // This happens when process is already dead
222                 return true;
223             }
224             PLOG(ERROR) << "set_timerslack_ns write failed";
225         }
226     }
227 
228     // TODO: Remove when /proc/<tid>/timerslack_ns interface is backported.
229     if (tid == 0 || tid == GetThreadId()) {
230         if (prctl(PR_SET_TIMERSLACK, slack_) == -1) {
231             PLOG(ERROR) << "set_timerslack_ns prctl failed";
232         }
233     }
234 
235     return true;
236 }
237 
238 #else
239 
ExecuteForTask(int) const240 bool SetTimerSlackAction::ExecuteForTask(int) const {
241     return true;
242 };
243 
244 #endif
245 
WriteValueToFile(const std::string & path) const246 bool SetAttributeAction::WriteValueToFile(const std::string& path) const {
247     if (!WriteStringToFile(value_, path)) {
248         if (access(path.c_str(), F_OK) < 0) {
249             if (optional_) {
250                 return true;
251             } else {
252                 LOG(ERROR) << "No such cgroup attribute: " << path;
253                 return false;
254             }
255         }
256         // The PLOG() statement below uses the error code stored in `errno` by
257         // WriteStringToFile() because access() only overwrites `errno` if it fails
258         // and because this code is only reached if the access() function returns 0.
259         PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;
260         return false;
261     }
262 
263     return true;
264 }
265 
ExecuteForProcess(uid_t uid,pid_t pid) const266 bool SetAttributeAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
267     std::string path;
268 
269     if (!attribute_->GetPathForProcess(uid, pid, &path)) {
270         LOG(ERROR) << "Failed to find cgroup for uid " << uid << " pid " << pid;
271         return false;
272     }
273 
274     return WriteValueToFile(path);
275 }
276 
ExecuteForTask(pid_t tid) const277 bool SetAttributeAction::ExecuteForTask(pid_t tid) const {
278     std::string path;
279 
280     if (!attribute_->GetPathForTask(tid, &path)) {
281         LOG(ERROR) << "Failed to find cgroup for tid " << tid;
282         return false;
283     }
284 
285     return WriteValueToFile(path);
286 }
287 
ExecuteForUID(uid_t uid) const288 bool SetAttributeAction::ExecuteForUID(uid_t uid) const {
289     std::string path;
290 
291     if (!attribute_->GetPathForUID(uid, &path)) {
292         LOG(ERROR) << "Failed to find cgroup for uid " << uid;
293         return false;
294     }
295 
296     if (!WriteStringToFile(value_, path)) {
297         if (access(path.c_str(), F_OK) < 0) {
298             if (optional_) {
299                 return true;
300             } else {
301                 LOG(ERROR) << "No such cgroup attribute: " << path;
302                 return false;
303             }
304         }
305         PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;
306         return false;
307     }
308     return true;
309 }
310 
IsValidForProcess(uid_t,pid_t pid) const311 bool SetAttributeAction::IsValidForProcess(uid_t, pid_t pid) const {
312     return IsValidForTask(pid);
313 }
314 
IsValidForTask(pid_t tid) const315 bool SetAttributeAction::IsValidForTask(pid_t tid) const {
316     std::string path;
317 
318     if (!attribute_->GetPathForTask(tid, &path)) {
319         return false;
320     }
321 
322     if (!access(path.c_str(), W_OK)) {
323         // operation will succeed
324         return true;
325     }
326 
327     if (!access(path.c_str(), F_OK)) {
328         // file exists but not writable
329         return false;
330     }
331 
332     // file does not exist, ignore if optional
333     return optional_;
334 }
335 
SetCgroupAction(const CgroupController & c,const std::string & p)336 SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
337     : controller_(c), path_(p) {
338     FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]);
339     // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
340     FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]);
341 }
342 
AddTidToCgroup(pid_t tid,int fd,ResourceCacheType cache_type) const343 bool SetCgroupAction::AddTidToCgroup(pid_t tid, int fd, ResourceCacheType cache_type) const {
344     if (tid <= 0) {
345         return true;
346     }
347 
348     std::string value = std::to_string(tid);
349 
350     if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) == value.length()) {
351         return true;
352     }
353 
354     // If the thread is in the process of exiting, don't flag an error
355     if (errno == ESRCH) {
356         return true;
357     }
358 
359     const char* controller_name = controller()->name();
360     // ENOSPC is returned when cpuset cgroup that we are joining has no online cpus
361     if (errno == ENOSPC && !strcmp(controller_name, "cpuset")) {
362         // This is an abnormal case happening only in testing, so report it only once
363         static bool empty_cpuset_reported = false;
364 
365         if (empty_cpuset_reported) {
366             return true;
367         }
368 
369         LOG(ERROR) << "Failed to add task '" << value
370                    << "' into cpuset because all cpus in that cpuset are offline";
371         empty_cpuset_reported = true;
372     } else {
373         PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; path=" << path_ << "; "
374                     << (cache_type == RCT_TASK ? "task" : "process");
375     }
376 
377     return false;
378 }
379 
UseCachedFd(ResourceCacheType cache_type,int id) const380 ProfileAction::CacheUseResult SetCgroupAction::UseCachedFd(ResourceCacheType cache_type,
381                                                            int id) const {
382     std::lock_guard<std::mutex> lock(fd_mutex_);
383     if (FdCacheHelper::IsCached(fd_[cache_type])) {
384         // fd is cached, reuse it
385         if (!AddTidToCgroup(id, fd_[cache_type], cache_type)) {
386             LOG(ERROR) << "Failed to add task into cgroup";
387             return ProfileAction::FAIL;
388         }
389         return ProfileAction::SUCCESS;
390     }
391 
392     if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {
393         // no permissions to access the file, ignore
394         return ProfileAction::SUCCESS;
395     }
396 
397     if (cache_type == ResourceCacheType::RCT_TASK &&
398         fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
399         // application-dependent path can't be used with tid
400         LOG(ERROR) << Name() << ": application profile can't be applied to a thread";
401         return ProfileAction::FAIL;
402     }
403 
404     return ProfileAction::UNUSED;
405 }
406 
ExecuteForProcess(uid_t uid,pid_t pid) const407 bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
408     CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, pid);
409     if (result != ProfileAction::UNUSED) {
410         return result == ProfileAction::SUCCESS;
411     }
412 
413     // fd was not cached or cached fd can't be used
414     std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
415     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
416     if (tmp_fd < 0) {
417         PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << procs_path;
418         return false;
419     }
420     if (!AddTidToCgroup(pid, tmp_fd, RCT_PROCESS)) {
421         LOG(ERROR) << "Failed to add task into cgroup";
422         return false;
423     }
424 
425     return true;
426 }
427 
ExecuteForTask(pid_t tid) const428 bool SetCgroupAction::ExecuteForTask(pid_t tid) const {
429     CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, tid);
430     if (result != ProfileAction::UNUSED) {
431         return result == ProfileAction::SUCCESS;
432     }
433 
434     // fd was not cached or cached fd can't be used
435     std::string tasks_path = controller()->GetTasksFilePath(path_);
436     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
437     if (tmp_fd < 0) {
438         PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << tasks_path;
439         return false;
440     }
441     if (!AddTidToCgroup(tid, tmp_fd, RCT_TASK)) {
442         LOG(ERROR) << "Failed to add task into cgroup";
443         return false;
444     }
445 
446     return true;
447 }
448 
EnableResourceCaching(ResourceCacheType cache_type)449 void SetCgroupAction::EnableResourceCaching(ResourceCacheType cache_type) {
450     std::lock_guard<std::mutex> lock(fd_mutex_);
451     // Return early to prevent unnecessary calls to controller_.Get{Tasks|Procs}FilePath() which
452     // include regex evaluations
453     if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {
454         return;
455     }
456     switch (cache_type) {
457         case (ProfileAction::RCT_TASK):
458             FdCacheHelper::Cache(controller_.GetTasksFilePath(path_), fd_[cache_type]);
459             break;
460         case (ProfileAction::RCT_PROCESS):
461             // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
462             FdCacheHelper::Cache(controller_.GetProcsFilePath(path_, 0, 0), fd_[cache_type]);
463             break;
464         default:
465             LOG(ERROR) << "Invalid cache type is specified!";
466             break;
467     }
468 }
469 
DropResourceCaching(ResourceCacheType cache_type)470 void SetCgroupAction::DropResourceCaching(ResourceCacheType cache_type) {
471     std::lock_guard<std::mutex> lock(fd_mutex_);
472     FdCacheHelper::Drop(fd_[cache_type]);
473 }
474 
IsValidForProcess(uid_t uid,pid_t pid) const475 bool SetCgroupAction::IsValidForProcess(uid_t uid, pid_t pid) const {
476     std::lock_guard<std::mutex> lock(fd_mutex_);
477     if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_PROCESS])) {
478         return true;
479     }
480 
481     if (fd_[ProfileAction::RCT_PROCESS] == FdCacheHelper::FDS_INACCESSIBLE) {
482         return false;
483     }
484 
485     std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
486     return access(procs_path.c_str(), W_OK) == 0;
487 }
488 
IsValidForTask(int) const489 bool SetCgroupAction::IsValidForTask(int) const {
490     std::lock_guard<std::mutex> lock(fd_mutex_);
491     if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_TASK])) {
492         return true;
493     }
494 
495     if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_INACCESSIBLE) {
496         return false;
497     }
498 
499     if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_APP_DEPENDENT) {
500         // application-dependent path can't be used with tid
501         return false;
502     }
503 
504     std::string tasks_path = controller()->GetTasksFilePath(path_);
505     return access(tasks_path.c_str(), W_OK) == 0;
506 }
507 
WriteFileAction(const std::string & task_path,const std::string & proc_path,const std::string & value,bool logfailures)508 WriteFileAction::WriteFileAction(const std::string& task_path, const std::string& proc_path,
509                                  const std::string& value, bool logfailures)
510     : task_path_(task_path), proc_path_(proc_path), value_(value), logfailures_(logfailures) {
511     FdCacheHelper::Init(task_path_, fd_[ProfileAction::RCT_TASK]);
512     if (!proc_path_.empty()) FdCacheHelper::Init(proc_path_, fd_[ProfileAction::RCT_PROCESS]);
513 }
514 
WriteValueToFile(const std::string & value_,ResourceCacheType cache_type,uid_t uid,pid_t pid,bool logfailures) const515 bool WriteFileAction::WriteValueToFile(const std::string& value_, ResourceCacheType cache_type,
516                                        uid_t uid, pid_t pid, bool logfailures) const {
517     std::string value(value_);
518 
519     value = StringReplace(value, "<uid>", std::to_string(uid), true);
520     value = StringReplace(value, "<pid>", std::to_string(pid), true);
521 
522     CacheUseResult result = UseCachedFd(cache_type, value);
523 
524     if (result != ProfileAction::UNUSED) {
525         return result == ProfileAction::SUCCESS;
526     }
527 
528     std::string path;
529     if (cache_type == ProfileAction::RCT_TASK || proc_path_.empty()) {
530         path = task_path_;
531     } else {
532         path = proc_path_;
533     }
534 
535     // Use WriteStringToFd instead of WriteStringToFile because the latter will open file with
536     // O_TRUNC which causes kernfs_mutex contention
537     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
538 
539     if (tmp_fd < 0) {
540         if (logfailures) PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << path;
541         return false;
542     }
543 
544     if (!WriteStringToFd(value, tmp_fd)) {
545         if (logfailures) PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
546         return false;
547     }
548 
549     return true;
550 }
551 
UseCachedFd(ResourceCacheType cache_type,const std::string & value) const552 ProfileAction::CacheUseResult WriteFileAction::UseCachedFd(ResourceCacheType cache_type,
553                                                            const std::string& value) const {
554     std::lock_guard<std::mutex> lock(fd_mutex_);
555     if (FdCacheHelper::IsCached(fd_[cache_type])) {
556         // fd is cached, reuse it
557         bool ret = WriteStringToFd(value, fd_[cache_type]);
558 
559         if (!ret && logfailures_) {
560             if (cache_type == ProfileAction::RCT_TASK || proc_path_.empty()) {
561                 PLOG(ERROR) << "Failed to write '" << value << "' to " << task_path_;
562             } else {
563                 PLOG(ERROR) << "Failed to write '" << value << "' to " << proc_path_;
564             }
565         }
566         return ret ? ProfileAction::SUCCESS : ProfileAction::FAIL;
567     }
568 
569     if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {
570         // no permissions to access the file, ignore
571         return ProfileAction::SUCCESS;
572     }
573 
574     if (cache_type == ResourceCacheType::RCT_TASK &&
575         fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
576         // application-dependent path can't be used with tid
577         LOG(ERROR) << Name() << ": application profile can't be applied to a thread";
578         return ProfileAction::FAIL;
579     }
580     return ProfileAction::UNUSED;
581 }
582 
ExecuteForProcess(uid_t uid,pid_t pid) const583 bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
584     if (!proc_path_.empty()) {
585         return WriteValueToFile(value_, ProfileAction::RCT_PROCESS, uid, pid, logfailures_);
586     }
587 
588     DIR* d;
589     struct dirent* de;
590     char proc_path[255];
591     pid_t t_pid;
592 
593     sprintf(proc_path, "/proc/%d/task", pid);
594     if (!(d = opendir(proc_path))) {
595         return false;
596     }
597 
598     while ((de = readdir(d))) {
599         if (de->d_name[0] == '.') {
600             continue;
601         }
602 
603         t_pid = atoi(de->d_name);
604 
605         if (!t_pid) {
606             continue;
607         }
608 
609         WriteValueToFile(value_, ProfileAction::RCT_TASK, uid, t_pid, logfailures_);
610     }
611 
612     closedir(d);
613 
614     return true;
615 }
616 
ExecuteForTask(pid_t tid) const617 bool WriteFileAction::ExecuteForTask(pid_t tid) const {
618     return WriteValueToFile(value_, ProfileAction::RCT_TASK, getuid(), tid, logfailures_);
619 }
620 
EnableResourceCaching(ResourceCacheType cache_type)621 void WriteFileAction::EnableResourceCaching(ResourceCacheType cache_type) {
622     std::lock_guard<std::mutex> lock(fd_mutex_);
623     if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {
624         return;
625     }
626     switch (cache_type) {
627         case (ProfileAction::RCT_TASK):
628             FdCacheHelper::Cache(task_path_, fd_[cache_type]);
629             break;
630         case (ProfileAction::RCT_PROCESS):
631             if (!proc_path_.empty()) FdCacheHelper::Cache(proc_path_, fd_[cache_type]);
632             break;
633         default:
634             LOG(ERROR) << "Invalid cache type is specified!";
635             break;
636     }
637 }
638 
DropResourceCaching(ResourceCacheType cache_type)639 void WriteFileAction::DropResourceCaching(ResourceCacheType cache_type) {
640     std::lock_guard<std::mutex> lock(fd_mutex_);
641     FdCacheHelper::Drop(fd_[cache_type]);
642 }
643 
IsValidForProcess(uid_t,pid_t) const644 bool WriteFileAction::IsValidForProcess(uid_t, pid_t) const {
645     std::lock_guard<std::mutex> lock(fd_mutex_);
646     if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_PROCESS])) {
647         return true;
648     }
649 
650     if (fd_[ProfileAction::RCT_PROCESS] == FdCacheHelper::FDS_INACCESSIBLE) {
651         return false;
652     }
653 
654     return access(proc_path_.empty() ? task_path_.c_str() : proc_path_.c_str(), W_OK) == 0;
655 }
656 
IsValidForTask(int) const657 bool WriteFileAction::IsValidForTask(int) const {
658     std::lock_guard<std::mutex> lock(fd_mutex_);
659     if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_TASK])) {
660         return true;
661     }
662 
663     if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_INACCESSIBLE) {
664         return false;
665     }
666 
667     if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_APP_DEPENDENT) {
668         // application-dependent path can't be used with tid
669         return false;
670     }
671 
672     return access(task_path_.c_str(), W_OK) == 0;
673 }
674 
ExecuteForProcess(uid_t uid,pid_t pid) const675 bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
676     for (const auto& profile : profiles_) {
677         profile->ExecuteForProcess(uid, pid);
678     }
679     return true;
680 }
681 
ExecuteForTask(pid_t tid) const682 bool ApplyProfileAction::ExecuteForTask(pid_t tid) const {
683     for (const auto& profile : profiles_) {
684         profile->ExecuteForTask(tid);
685     }
686     return true;
687 }
688 
EnableResourceCaching(ResourceCacheType cache_type)689 void ApplyProfileAction::EnableResourceCaching(ResourceCacheType cache_type) {
690     for (const auto& profile : profiles_) {
691         profile->EnableResourceCaching(cache_type);
692     }
693 }
694 
DropResourceCaching(ResourceCacheType cache_type)695 void ApplyProfileAction::DropResourceCaching(ResourceCacheType cache_type) {
696     for (const auto& profile : profiles_) {
697         profile->DropResourceCaching(cache_type);
698     }
699 }
700 
IsValidForProcess(uid_t uid,pid_t pid) const701 bool ApplyProfileAction::IsValidForProcess(uid_t uid, pid_t pid) const {
702     for (const auto& profile : profiles_) {
703         if (!profile->IsValidForProcess(uid, pid)) {
704             return false;
705         }
706     }
707     return true;
708 }
709 
IsValidForTask(pid_t tid) const710 bool ApplyProfileAction::IsValidForTask(pid_t tid) const {
711     for (const auto& profile : profiles_) {
712         if (!profile->IsValidForTask(tid)) {
713             return false;
714         }
715     }
716     return true;
717 }
718 
MoveTo(TaskProfile * profile)719 void TaskProfile::MoveTo(TaskProfile* profile) {
720     profile->elements_ = std::move(elements_);
721     profile->res_cached_ = res_cached_;
722 }
723 
ExecuteForProcess(uid_t uid,pid_t pid) const724 bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
725     for (const auto& element : elements_) {
726         if (!element->ExecuteForProcess(uid, pid)) {
727             LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
728             return false;
729         }
730     }
731     return true;
732 }
733 
ExecuteForTask(pid_t tid) const734 bool TaskProfile::ExecuteForTask(pid_t tid) const {
735     if (tid == 0) {
736         tid = GetThreadId();
737     }
738     for (const auto& element : elements_) {
739         if (!element->ExecuteForTask(tid)) {
740             LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
741             return false;
742         }
743     }
744     return true;
745 }
746 
ExecuteForUID(uid_t uid) const747 bool TaskProfile::ExecuteForUID(uid_t uid) const {
748     for (const auto& element : elements_) {
749         if (!element->ExecuteForUID(uid)) {
750             LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
751             return false;
752         }
753     }
754     return true;
755 }
756 
EnableResourceCaching(ProfileAction::ResourceCacheType cache_type)757 void TaskProfile::EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) {
758     if (res_cached_) {
759         return;
760     }
761 
762     for (auto& element : elements_) {
763         element->EnableResourceCaching(cache_type);
764     }
765 
766     res_cached_ = true;
767 }
768 
DropResourceCaching(ProfileAction::ResourceCacheType cache_type)769 void TaskProfile::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) {
770     if (!res_cached_) {
771         return;
772     }
773 
774     for (auto& element : elements_) {
775         element->DropResourceCaching(cache_type);
776     }
777 
778     res_cached_ = false;
779 }
780 
IsValidForProcess(uid_t uid,pid_t pid) const781 bool TaskProfile::IsValidForProcess(uid_t uid, pid_t pid) const {
782     for (const auto& element : elements_) {
783         if (!element->IsValidForProcess(uid, pid)) return false;
784     }
785     return true;
786 }
787 
IsValidForTask(pid_t tid) const788 bool TaskProfile::IsValidForTask(pid_t tid) const {
789     for (const auto& element : elements_) {
790         if (!element->IsValidForTask(tid)) return false;
791     }
792     return true;
793 }
794 
DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const795 void TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const {
796     for (auto& iter : profiles_) {
797         iter.second->DropResourceCaching(cache_type);
798     }
799 }
800 
GetInstance()801 TaskProfiles& TaskProfiles::GetInstance() {
802     // Deliberately leak this object to avoid a race between destruction on
803     // process exit and concurrent access from another thread.
804     static auto* instance = new TaskProfiles;
805     return *instance;
806 }
807 
TaskProfiles()808 TaskProfiles::TaskProfiles() {
809     // load system task profiles
810     if (!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_FILE)) {
811         LOG(ERROR) << "Loading " << TASK_PROFILE_DB_FILE << " for [" << getpid() << "] failed";
812     }
813 
814     // load API-level specific system task profiles if available
815     unsigned int api_level = GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
816     if (api_level > 0) {
817         std::string api_profiles_path =
818                 android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level);
819         if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) {
820             if (!Load(CgroupMap::GetInstance(), api_profiles_path)) {
821                 LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid() << "] failed";
822             }
823         }
824     }
825 
826     // load vendor task profiles if the file exists
827     if (!access(TASK_PROFILE_DB_VENDOR_FILE, F_OK) &&
828         !Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_VENDOR_FILE)) {
829         LOG(ERROR) << "Loading " << TASK_PROFILE_DB_VENDOR_FILE << " for [" << getpid()
830                    << "] failed";
831     }
832 }
833 
Load(const CgroupMap & cg_map,const std::string & file_name)834 bool TaskProfiles::Load(const CgroupMap& cg_map, const std::string& file_name) {
835     std::string json_doc;
836 
837     if (!android::base::ReadFileToString(file_name, &json_doc)) {
838         LOG(ERROR) << "Failed to read task profiles from " << file_name;
839         return false;
840     }
841 
842     Json::CharReaderBuilder builder;
843     std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
844     Json::Value root;
845     std::string errorMessage;
846     if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
847         LOG(ERROR) << "Failed to parse task profiles: " << errorMessage;
848         return false;
849     }
850 
851     const Json::Value& attr = root["Attributes"];
852     for (Json::Value::ArrayIndex i = 0; i < attr.size(); ++i) {
853         std::string name = attr[i]["Name"].asString();
854         std::string controller_name = attr[i]["Controller"].asString();
855         std::string file_attr = attr[i]["File"].asString();
856         std::string file_v2_attr = attr[i]["FileV2"].asString();
857 
858         if (!file_v2_attr.empty() && file_attr.empty()) {
859             LOG(ERROR) << "Attribute " << name << " has FileV2 but no File property";
860             return false;
861         }
862 
863         auto controller = cg_map.FindController(controller_name);
864         if (controller.HasValue()) {
865             auto iter = attributes_.find(name);
866             if (iter == attributes_.end()) {
867                 attributes_[name] =
868                         std::make_unique<ProfileAttribute>(controller, file_attr, file_v2_attr);
869             } else {
870                 iter->second->Reset(controller, file_attr, file_v2_attr);
871             }
872         } else {
873             LOG(WARNING) << "Controller " << controller_name << " is not found";
874         }
875     }
876 
877     const Json::Value& profiles_val = root["Profiles"];
878     for (Json::Value::ArrayIndex i = 0; i < profiles_val.size(); ++i) {
879         const Json::Value& profile_val = profiles_val[i];
880 
881         std::string profile_name = profile_val["Name"].asString();
882         const Json::Value& actions = profile_val["Actions"];
883         auto profile = std::make_shared<TaskProfile>(profile_name);
884 
885         for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
886             const Json::Value& action_val = actions[act_idx];
887             std::string action_name = action_val["Name"].asString();
888             const Json::Value& params_val = action_val["Params"];
889             if (action_name == "JoinCgroup") {
890                 std::string controller_name = params_val["Controller"].asString();
891                 std::string path = params_val["Path"].asString();
892 
893                 auto controller = cg_map.FindController(controller_name);
894                 if (controller.HasValue()) {
895                     if (controller.version() == 1) {
896                         profile->Add(std::make_unique<SetCgroupAction>(controller, path));
897                     } else {
898                         LOG(WARNING) << "A JoinCgroup action in the " << profile_name
899                                      << " profile is used for controller " << controller_name
900                                      << " in the cgroup v2 hierarchy and will be ignored";
901                     }
902                 } else {
903                     LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
904                 }
905             } else if (action_name == "SetTimerSlack") {
906                 std::string slack_value = params_val["Slack"].asString();
907                 char* end;
908                 unsigned long slack;
909 
910                 slack = strtoul(slack_value.c_str(), &end, 10);
911                 if (end > slack_value.c_str()) {
912                     profile->Add(std::make_unique<SetTimerSlackAction>(slack));
913                 } else {
914                     LOG(WARNING) << "SetTimerSlack: invalid parameter: " << slack_value;
915                 }
916             } else if (action_name == "SetAttribute") {
917                 std::string attr_name = params_val["Name"].asString();
918                 std::string attr_value = params_val["Value"].asString();
919                 bool optional = strcmp(params_val["Optional"].asString().c_str(), "true") == 0;
920 
921                 auto iter = attributes_.find(attr_name);
922                 if (iter != attributes_.end()) {
923                     profile->Add(std::make_unique<SetAttributeAction>(iter->second.get(),
924                                                                       attr_value, optional));
925                 } else {
926                     LOG(WARNING) << "SetAttribute: unknown attribute: " << attr_name;
927                 }
928             } else if (action_name == "SetClamps") {
929                 std::string boost_value = params_val["Boost"].asString();
930                 std::string clamp_value = params_val["Clamp"].asString();
931                 char* end;
932                 unsigned long boost;
933 
934                 boost = strtoul(boost_value.c_str(), &end, 10);
935                 if (end > boost_value.c_str()) {
936                     unsigned long clamp = strtoul(clamp_value.c_str(), &end, 10);
937                     if (end > clamp_value.c_str()) {
938                         profile->Add(std::make_unique<SetClampsAction>(boost, clamp));
939                     } else {
940                         LOG(WARNING) << "SetClamps: invalid parameter " << clamp_value;
941                     }
942                 } else {
943                     LOG(WARNING) << "SetClamps: invalid parameter: " << boost_value;
944                 }
945             } else if (action_name == "WriteFile") {
946                 std::string attr_filepath = params_val["FilePath"].asString();
947                 std::string attr_procfilepath = params_val["ProcFilePath"].asString();
948                 std::string attr_value = params_val["Value"].asString();
949                 // FilePath and Value are mandatory
950                 if (!attr_filepath.empty() && !attr_value.empty()) {
951                     std::string attr_logfailures = params_val["LogFailures"].asString();
952                     bool logfailures = attr_logfailures.empty() || attr_logfailures == "true";
953                     profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_procfilepath,
954                                                                    attr_value, logfailures));
955                 } else if (attr_filepath.empty()) {
956                     LOG(WARNING) << "WriteFile: invalid parameter: "
957                                  << "empty filepath";
958                 } else if (attr_value.empty()) {
959                     LOG(WARNING) << "WriteFile: invalid parameter: "
960                                  << "empty value";
961                 }
962             } else {
963                 LOG(WARNING) << "Unknown profile action: " << action_name;
964             }
965         }
966         auto iter = profiles_.find(profile_name);
967         if (iter == profiles_.end()) {
968             profiles_[profile_name] = profile;
969         } else {
970             // Move the content rather that replace the profile because old profile might be
971             // referenced from an aggregate profile if vendor overrides task profiles
972             profile->MoveTo(iter->second.get());
973             profile.reset();
974         }
975     }
976 
977     const Json::Value& aggregateprofiles_val = root["AggregateProfiles"];
978     for (Json::Value::ArrayIndex i = 0; i < aggregateprofiles_val.size(); ++i) {
979         const Json::Value& aggregateprofile_val = aggregateprofiles_val[i];
980 
981         std::string aggregateprofile_name = aggregateprofile_val["Name"].asString();
982         const Json::Value& aggregateprofiles = aggregateprofile_val["Profiles"];
983         std::vector<std::shared_ptr<TaskProfile>> profiles;
984         bool ret = true;
985 
986         for (Json::Value::ArrayIndex pf_idx = 0; pf_idx < aggregateprofiles.size(); ++pf_idx) {
987             std::string profile_name = aggregateprofiles[pf_idx].asString();
988 
989             if (profile_name == aggregateprofile_name) {
990                 LOG(WARNING) << "AggregateProfiles: recursive profile name: " << profile_name;
991                 ret = false;
992                 break;
993             } else if (profiles_.find(profile_name) == profiles_.end()) {
994                 LOG(WARNING) << "AggregateProfiles: undefined profile name: " << profile_name;
995                 ret = false;
996                 break;
997             } else {
998                 profiles.push_back(profiles_[profile_name]);
999             }
1000         }
1001         if (ret) {
1002             auto profile = std::make_shared<TaskProfile>(aggregateprofile_name);
1003             profile->Add(std::make_unique<ApplyProfileAction>(profiles));
1004             profiles_[aggregateprofile_name] = profile;
1005         }
1006     }
1007 
1008     return true;
1009 }
1010 
GetProfile(std::string_view name) const1011 TaskProfile* TaskProfiles::GetProfile(std::string_view name) const {
1012     auto iter = profiles_.find(name);
1013 
1014     if (iter != profiles_.end()) {
1015         return iter->second.get();
1016     }
1017     return nullptr;
1018 }
1019 
GetAttribute(std::string_view name) const1020 const IProfileAttribute* TaskProfiles::GetAttribute(std::string_view name) const {
1021     auto iter = attributes_.find(name);
1022 
1023     if (iter != attributes_.end()) {
1024         return iter->second.get();
1025     }
1026     return nullptr;
1027 }
1028 
1029 template <typename T>
SetUserProfiles(uid_t uid,std::span<const T> profiles,bool use_fd_cache)1030 bool TaskProfiles::SetUserProfiles(uid_t uid, std::span<const T> profiles, bool use_fd_cache) {
1031     for (const auto& name : profiles) {
1032         TaskProfile* profile = GetProfile(name);
1033         if (profile != nullptr) {
1034             if (use_fd_cache) {
1035                 profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);
1036             }
1037             if (!profile->ExecuteForUID(uid)) {
1038                 PLOG(WARNING) << "Failed to apply " << name << " process profile";
1039             }
1040         } else {
1041             PLOG(WARNING) << "Failed to find " << name << "process profile";
1042         }
1043     }
1044     return true;
1045 }
1046 
1047 template <typename T>
SetProcessProfiles(uid_t uid,pid_t pid,std::span<const T> profiles,bool use_fd_cache)1048 bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid, std::span<const T> profiles,
1049                                       bool use_fd_cache) {
1050     bool success = true;
1051     for (const auto& name : profiles) {
1052         TaskProfile* profile = GetProfile(name);
1053         if (profile != nullptr) {
1054             if (use_fd_cache) {
1055                 profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);
1056             }
1057             if (!profile->ExecuteForProcess(uid, pid)) {
1058                 LOG(WARNING) << "Failed to apply " << name << " process profile";
1059                 success = false;
1060             }
1061         } else {
1062             LOG(WARNING) << "Failed to find " << name << " process profile";
1063             success = false;
1064         }
1065     }
1066     return success;
1067 }
1068 
1069 template <typename T>
SetTaskProfiles(pid_t tid,std::span<const T> profiles,bool use_fd_cache)1070 bool TaskProfiles::SetTaskProfiles(pid_t tid, std::span<const T> profiles, bool use_fd_cache) {
1071     bool success = true;
1072     for (const auto& name : profiles) {
1073         TaskProfile* profile = GetProfile(name);
1074         if (profile != nullptr) {
1075             if (use_fd_cache) {
1076                 profile->EnableResourceCaching(ProfileAction::RCT_TASK);
1077             }
1078             if (!profile->ExecuteForTask(tid)) {
1079                 LOG(WARNING) << "Failed to apply " << name << " task profile";
1080                 success = false;
1081             }
1082         } else {
1083             LOG(WARNING) << "Failed to find " << name << " task profile";
1084             success = false;
1085         }
1086     }
1087     return success;
1088 }
1089 
1090 template bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
1091                                                std::span<const std::string> profiles,
1092                                                bool use_fd_cache);
1093 template bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
1094                                                std::span<const std::string_view> profiles,
1095                                                bool use_fd_cache);
1096 template bool TaskProfiles::SetTaskProfiles(pid_t tid, std::span<const std::string> profiles,
1097                                             bool use_fd_cache);
1098 template bool TaskProfiles::SetTaskProfiles(pid_t tid, std::span<const std::string_view> profiles,
1099                                             bool use_fd_cache);
1100 template bool TaskProfiles::SetUserProfiles(uid_t uid, std::span<const std::string> profiles,
1101                                             bool use_fd_cache);
1102