1 /*
2 * Copyright (C) 2020 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 #include "src/profiling/common/producer_support.h"
18
19 #include <algorithm>
20 #include <optional>
21
22 #include "perfetto/ext/base/android_utils.h"
23 #include "perfetto/ext/base/file_utils.h"
24 #include "perfetto/ext/base/string_splitter.h"
25 #include "perfetto/tracing/core/data_source_config.h"
26 #include "src/traced/probes/packages_list/packages_list_parser.h"
27
28 namespace perfetto {
29 namespace profiling {
30
31 namespace {
FindInPackagesList(uint64_t lookup_uid,const std::string & packages_list_path)32 std::optional<Package> FindInPackagesList(
33 uint64_t lookup_uid,
34 const std::string& packages_list_path) {
35 std::string content;
36 if (!base::ReadFile(packages_list_path, &content)) {
37 PERFETTO_ELOG("Failed to read %s", packages_list_path.c_str());
38 return std::nullopt;
39 }
40 for (base::StringSplitter ss(std::move(content), '\n'); ss.Next();) {
41 Package pkg;
42 if (!ReadPackagesListLine(ss.cur_token(), &pkg)) {
43 PERFETTO_ELOG("Failed to parse packages.list");
44 return std::nullopt;
45 }
46
47 if (pkg.uid == lookup_uid) {
48 return std::move(pkg); // -Wreturn-std-move-in-c++11
49 }
50 }
51 return std::nullopt;
52 }
53
AllPackagesProfileableByTrustedInitiator(const std::string & packages_list_path)54 bool AllPackagesProfileableByTrustedInitiator(
55 const std::string& packages_list_path) {
56 std::string content;
57 if (!base::ReadFile(packages_list_path, &content)) {
58 PERFETTO_ELOG("Failed to read %s", packages_list_path.c_str());
59 return false;
60 }
61 bool ret = true;
62 for (base::StringSplitter ss(std::move(content), '\n'); ss.Next();) {
63 Package pkg;
64 if (!ReadPackagesListLine(ss.cur_token(), &pkg)) {
65 PERFETTO_ELOG("Failed to parse packages.list");
66 return false;
67 }
68
69 ret = ret && (pkg.profileable || pkg.debuggable);
70 }
71 return ret;
72 }
73
74 } // namespace
75
CanProfile(const DataSourceConfig & ds_config,uint64_t uid,const std::vector<std::string> & installed_by)76 bool CanProfile(const DataSourceConfig& ds_config,
77 uint64_t uid,
78 const std::vector<std::string>& installed_by) {
79 // We restrict by !PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) because a
80 // sideloaded heapprofd should not be restricted by this. Do note though that,
81 // at the moment, there isn't really a way to sideload a functioning heapprofd
82 // onto user builds.
83 #if !PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) || \
84 !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
85 base::ignore_result(ds_config);
86 base::ignore_result(uid);
87 base::ignore_result(installed_by);
88 return true;
89 #else
90 std::string build_type = base::GetAndroidProp("ro.build.type");
91 return CanProfileAndroid(ds_config, uid, installed_by, build_type,
92 "/data/system/packages.list");
93 #endif
94 }
95
CanProfileAndroid(const DataSourceConfig & ds_config,uint64_t uid,const std::vector<std::string> & installed_by,const std::string & build_type,const std::string & packages_list_path)96 bool CanProfileAndroid(const DataSourceConfig& ds_config,
97 uint64_t uid,
98 const std::vector<std::string>& installed_by,
99 const std::string& build_type,
100 const std::string& packages_list_path) {
101 // These constants are replicated from libcutils android_filesystem_config.h,
102 // to allow for building and testing the profilers outside the android tree.
103 constexpr auto kAidUserOffset = 100000; // AID_USER_OFFSET
104 constexpr auto kAidAppStart = 10000; // AID_APP_START
105 constexpr auto kAidAppEnd = 19999; // AID_APP_END
106 constexpr auto kAidSdkSandboxStart = 20000; // AID_SDK_SANDBOX_PROCESS_START
107 constexpr auto kAidSdkSandboxEnd = 29999; // AID_SDK_SANDBOX_PROCESS_END
108 constexpr auto kAidIsolatedStart = 90000; // AID_ISOLATED_START
109 constexpr auto kAidIsolatedEnd = 99999; // AID_ISOLATED_END
110
111 if (!build_type.empty() && build_type != "user") {
112 return true;
113 }
114
115 bool trusted_initiator = ds_config.session_initiator() ==
116 DataSourceConfig::SESSION_INITIATOR_TRUSTED_SYSTEM;
117
118 uint64_t uid_without_profile = uid % kAidUserOffset;
119 uint64_t uid_for_lookup = 0;
120 if (uid_without_profile < kAidAppStart) {
121 // Platform processes are considered profileable by the platform itself.
122 // This includes platform UIDs from other profiles, e.g. "u10_system".
123 // It's possible that this is an app (e.g. com.android.settings runs as
124 // AID_SYSTEM), but we will skip checking packages.list for the profileable
125 // manifest flags, as running under a platform UID is considered sufficient.
126 // Minor consequence: shell cannot profile platform apps, even if their
127 // manifest flags opt into profiling from shell. Resolving this would
128 // require definitively disambiguating native processes from apps if both
129 // can run as the same platform UID.
130 return trusted_initiator;
131
132 } else if (uid_without_profile >= kAidAppStart &&
133 uid_without_profile <= kAidAppEnd) {
134 // normal app
135 uid_for_lookup = uid_without_profile;
136
137 } else if (uid_without_profile >= kAidSdkSandboxStart &&
138 uid_without_profile <= kAidSdkSandboxEnd) {
139 // sdk sandbox process, has deterministic mapping to corresponding app
140 uint64_t sdk_sandbox_offset = kAidSdkSandboxStart - kAidAppStart;
141 uid_for_lookup = uid_without_profile - sdk_sandbox_offset;
142
143 } else if (uid_without_profile >= kAidIsolatedStart &&
144 uid_without_profile <= kAidIsolatedEnd) {
145 // Isolated process. Such processes run under random UIDs and have no
146 // straightforward link to the original app's UID without consulting
147 // system_server. So we have to perform a very conservative check - if *all*
148 // packages are profileable, then any isolated process must be profileable
149 // as well, regardless of which package it's running for (which might not
150 // even be the package in which the service was defined).
151 // TODO(rsavitski): find a way for the platform to tell native services
152 // about isolated<->app relations.
153 return trusted_initiator &&
154 AllPackagesProfileableByTrustedInitiator(packages_list_path);
155
156 } else {
157 // disallow everything else on release builds
158 return false;
159 }
160
161 std::optional<Package> pkg =
162 FindInPackagesList(uid_for_lookup, packages_list_path);
163
164 if (!pkg)
165 return false;
166
167 // check installer constraint if given
168 if (!installed_by.empty()) {
169 if (pkg->installed_by.empty()) {
170 PERFETTO_ELOG("Cannot parse installer from packages.list");
171 return false;
172 }
173 if (std::find(installed_by.cbegin(), installed_by.cend(),
174 pkg->installed_by) == installed_by.cend()) {
175 // not installed by one of the requested origins
176 return false;
177 }
178 }
179
180 switch (ds_config.session_initiator()) {
181 case DataSourceConfig::SESSION_INITIATOR_UNSPECIFIED:
182 return pkg->profileable_from_shell || pkg->debuggable;
183 case DataSourceConfig::SESSION_INITIATOR_TRUSTED_SYSTEM:
184 return pkg->profileable || pkg->debuggable;
185 }
186 return false;
187 }
188
189 } // namespace profiling
190 } // namespace perfetto
191