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