1 /*
2 * Copyright (C) 2023 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_TAG "uprobestats"
18
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <android-base/parseint.h>
22 #include <android-base/properties.h>
23 #include <android-base/scopeguard.h>
24 #include <android-base/strings.h>
25 #include <android/binder_process.h>
26 #include <android_uprobestats_mainline_flags.h>
27 #include <config.pb.h>
28 #include <iostream>
29 #include <stdio.h>
30 #include <string>
31 #include <thread>
32
33 #include "Bpf.h"
34 #include "ConfigResolver.h"
35 #include "DebugLog.h"
36 #include "FlagSelector.h"
37 #include "Guardrail.h"
38 #include <stats_event.h>
39
40 using namespace android::uprobestats;
41
42 const std::string kGenericBpfMapDetail =
43 std::string("GenericInstrumentation_call_detail");
44 const std::string kGenericBpfMapTimestamp =
45 std::string("GenericInstrumentation_call_timestamp");
46 const std::string kUpdateDeviceIdleTempAllowlistMap =
47 std::string("ProcessManagement_update_device_idle_temp_allowlist_records");
48 const std::string kProcessManagementMap =
49 std::string("ProcessManagement_output_buf");
50 const std::string kMalwareSignalMap = std::string("MalwareSignal_output_buf");
51 const int kJavaArgumentRegisterOffset = 2;
52
isUprobestatsEnabled()53 bool isUprobestatsEnabled() {
54 return android::uprobestats::flag_selector::enable_uprobestats();
55 }
56
57 const std::string kBpfPath = std::string("/sys/fs/bpf/uprobestats/");
prefixBpf(std::string value)58 std::string prefixBpf(std::string value) { return kBpfPath + value.c_str(); }
59
60 struct PollArgs {
61 std::string mapPath;
62 ::uprobestats::protos::UprobestatsConfig::Task taskConfig;
63 };
64
startsWith(const std::string & str,const std::string & prefix)65 bool startsWith(const std::string &str, const std::string &prefix) {
66 return str.length() >= prefix.length() &&
67 std::equal(prefix.begin(), prefix.end(), str.begin());
68 }
69
doPoll(PollArgs args)70 void doPoll(PollArgs args) {
71 auto mapPath = args.mapPath;
72 auto durationSeconds = args.taskConfig.duration_seconds();
73 auto duration = std::chrono::seconds(durationSeconds);
74 auto startTime = std::chrono::steady_clock::now();
75 auto now = startTime;
76 while (now - startTime < duration) {
77 auto remaining = duration - (std::chrono::steady_clock::now() - startTime);
78 auto timeoutMs = static_cast<int>(
79 std::chrono::duration_cast<std::chrono::milliseconds>(remaining)
80 .count());
81 if (mapPath.find(kGenericBpfMapDetail) != std::string::npos) {
82 LOG_IF_DEBUG("polling for GenericDetail result");
83 auto result =
84 bpf::pollRingBuf<bpf::CallResult>(mapPath.c_str(), timeoutMs);
85 for (auto value : result) {
86 LOG_IF_DEBUG("GenericDetail result...");
87 LOG_IF_DEBUG("register: pc = " << value.pc);
88 for (int i = 0; i < 10; i++) {
89 auto reg = value.regs[i];
90 LOG_IF_DEBUG("register: " << i << " = " << reg);
91 }
92 if (!args.taskConfig.has_statsd_logging_config()) {
93 LOG_IF_DEBUG("no statsd logging config");
94 continue;
95 }
96
97 auto statsd_logging_config = args.taskConfig.statsd_logging_config();
98 int atom_id = statsd_logging_config.atom_id();
99 LOG_IF_DEBUG("attempting to write atom id: " << atom_id);
100 AStatsEvent *event = AStatsEvent_obtain();
101 AStatsEvent_setAtomId(event, atom_id);
102 for (int primitiveArgumentPosition :
103 statsd_logging_config.primitive_argument_positions()) {
104 int primitiveArgument = value.regs[primitiveArgumentPosition +
105 kJavaArgumentRegisterOffset];
106 LOG_IF_DEBUG("writing argument value: " << primitiveArgument
107 << " from position: "
108 << primitiveArgumentPosition);
109 AStatsEvent_writeInt32(event, primitiveArgument);
110 }
111 AStatsEvent_write(event);
112 AStatsEvent_release(event);
113 LOG_IF_DEBUG("successfully wrote atom id: " << atom_id);
114 }
115 } else if (mapPath.find(kGenericBpfMapTimestamp) != std::string::npos) {
116 LOG_IF_DEBUG("polling for GenericTimestamp result");
117 auto result =
118 bpf::pollRingBuf<bpf::CallTimestamp>(mapPath.c_str(), timeoutMs);
119 for (auto value : result) {
120 LOG_IF_DEBUG("GenericTimestamp result: event "
121 << value.event << " timestampNs: " << value.timestampNs);
122 if (!args.taskConfig.has_statsd_logging_config()) {
123 LOG_IF_DEBUG("no statsd logging config");
124 continue;
125 }
126 // TODO: for now, we assume an atom structure of event, then timestamp.
127 // We will build a cleaner abstraction for handling "just give me
128 // timestamps when X API is called", but we're just trying ot get things
129 // working for now.
130 auto statsd_logging_config = args.taskConfig.statsd_logging_config();
131 int atom_id = statsd_logging_config.atom_id();
132 LOG_IF_DEBUG("attempting to write atom id: " << atom_id);
133 AStatsEvent *event = AStatsEvent_obtain();
134 AStatsEvent_setAtomId(event, atom_id);
135 AStatsEvent_writeInt32(event, value.event);
136 AStatsEvent_writeInt64(event, value.timestampNs);
137 AStatsEvent_write(event);
138 AStatsEvent_release(event);
139 LOG_IF_DEBUG("successfully wrote atom id: " << atom_id);
140 }
141 } else if (mapPath.find(kUpdateDeviceIdleTempAllowlistMap) !=
142 std::string::npos) {
143 LOG_IF_DEBUG("Polling for UpdateDeviceIdleTempAllowlistRecord result");
144 auto result = bpf::pollRingBuf<bpf::UpdateDeviceIdleTempAllowlistRecord>(
145 mapPath.c_str(), timeoutMs);
146 for (auto value : result) {
147 LOG_IF_DEBUG("UpdateDeviceIdleTempAllowlistRecord result... "
148 << " changing_uid: " << value.changing_uid
149 << " reason_code: " << value.reason_code << " reason: "
150 << value.reason << " calling_uid: " << value.calling_uid
151 << " mapPath: " << mapPath);
152 if (!args.taskConfig.has_statsd_logging_config()) {
153 LOG_IF_DEBUG("no statsd logging config");
154 continue;
155 }
156 auto statsd_logging_config = args.taskConfig.statsd_logging_config();
157 int atom_id = statsd_logging_config.atom_id();
158 AStatsEvent *event = AStatsEvent_obtain();
159 AStatsEvent_setAtomId(event, atom_id);
160 AStatsEvent_writeInt32(event, value.changing_uid);
161 AStatsEvent_writeBool(event, value.adding);
162 AStatsEvent_writeInt64(event, value.duration_ms);
163 AStatsEvent_writeInt32(event, value.type);
164 AStatsEvent_writeInt32(event, value.reason_code);
165 AStatsEvent_writeString(event, value.reason);
166 AStatsEvent_writeInt32(event, value.calling_uid);
167 AStatsEvent_write(event);
168 AStatsEvent_release(event);
169 }
170 } else if (mapPath.find(kProcessManagementMap) != std::string::npos) {
171 LOG_IF_DEBUG("Polling for SetUidTempAllowlistStateRecord result");
172 auto result = bpf::pollRingBuf<bpf::SetUidTempAllowlistStateRecord>(
173 mapPath.c_str(), timeoutMs);
174 for (auto value : result) {
175 LOG_IF_DEBUG("SetUidTempAllowlistStateRecord result... uid: "
176 << value.uid << " onAllowlist: " << value.onAllowlist
177 << " mapPath: " << mapPath);
178 if (!args.taskConfig.has_statsd_logging_config()) {
179 LOG_IF_DEBUG("no statsd logging config");
180 continue;
181 }
182 auto statsd_logging_config = args.taskConfig.statsd_logging_config();
183 int atom_id = statsd_logging_config.atom_id();
184 AStatsEvent *event = AStatsEvent_obtain();
185 AStatsEvent_setAtomId(event, atom_id);
186 AStatsEvent_writeInt32(event, value.uid);
187 AStatsEvent_writeBool(event, value.onAllowlist);
188 AStatsEvent_write(event);
189 AStatsEvent_release(event);
190 }
191 } else if (mapPath.find(kMalwareSignalMap) != std::string::npos) {
192 auto result =
193 bpf::pollRingBuf<bpf::MalwareSignal>(mapPath.c_str(), timeoutMs);
194 for (auto value : result) {
195 if (value.component_enabled_setting.initialized == true) {
196 LOG_IF_DEBUG(
197 "ComponentEnabledSetting: package_name="
198 << value.component_enabled_setting.package_name
199 << " class_name=" << value.component_enabled_setting.class_name
200 << " new_state=" << value.component_enabled_setting.new_state
201 << " calling_package_name="
202 << value.component_enabled_setting.calling_package_name);
203 }
204 if (value.wm_bound_uid.initialized == true) {
205 LOG_IF_DEBUG(
206 "WmBoundUid: clientUid:" << value.wm_bound_uid.client_uid);
207 LOG_IF_DEBUG(
208 "clientPackageName:" << value.wm_bound_uid.client_package_name);
209 LOG_IF_DEBUG("bindFlags:" << value.wm_bound_uid.bind_flags);
210 }
211 }
212 } else {
213 LOG_IF_DEBUG("Polling for i64 result");
214 auto result = bpf::pollRingBuf<uint64_t>(mapPath.c_str(), timeoutMs);
215 for (auto value : result) {
216 LOG_IF_DEBUG("Other result... value: " << value
217 << " mapPath: " << mapPath);
218 }
219 }
220 now = std::chrono::steady_clock::now();
221 }
222 LOG_IF_DEBUG("finished polling for mapPath: " << mapPath);
223 }
224
main()225 int main() {
226 if (android::uprobestats::flag_selector::executable_method_file_offsets()) {
227 ABinderProcess_startThreadPool();
228 }
229 if (!isUprobestatsEnabled()) {
230 LOG(ERROR) << "uprobestats disabled by flag. Exiting.";
231 return 1;
232 }
233 auto config =
234 config_resolver::readConfig("/data/misc/uprobestats-configs/config");
235 if (!config.has_value()) {
236 LOG(ERROR) << "Failed to parse uprobestats config.";
237 return 1;
238 }
239 if (!guardrail::isAllowed(
240 config.value(),
241 android::base::GetProperty("ro.build.type", "unknown"),
242 android::uprobestats::flag_selector::
243 executable_method_file_offsets())) {
244 LOG(ERROR) << "uprobestats probing config disallowed on this device.";
245 return 1;
246 }
247 auto resolvedTask = config_resolver::resolveSingleTask(config.value());
248 if (!resolvedTask.has_value()) {
249 LOG(ERROR) << "Failed to parse task";
250 return 1;
251 }
252
253 LOG_IF_DEBUG("Found task config: " << resolvedTask.value());
254 auto resolvedProbeConfigs =
255 config_resolver::resolveProbes(resolvedTask.value().taskConfig);
256 if (!resolvedProbeConfigs.has_value()) {
257 LOG(ERROR) << "Failed to resolve a probe config from task";
258 return 1;
259 }
260 for (auto &resolvedProbe : resolvedProbeConfigs.value()) {
261 LOG_IF_DEBUG("Opening bpf perf event from probe: " << resolvedProbe);
262 if (resolvedProbe.filename ==
263 "prog_ProcessManagement_uprobe_update_device_idle_temp_allowlist" &&
264 !android::uprobestats::flag_selector::
265 uprobestats_support_update_device_idle_temp_allowlist()) {
266 LOG(ERROR) << "update_device_idle_temp_allowlist disabled by flag";
267 }
268 if (resolvedProbe.filename ==
269 "prog_MalwareSignal_uprobe_add_bound_client_uid" &&
270 !android::uprobestats::mainline::flags::
271 uprobestats_monitor_disruptive_app_activities()) {
272 LOG(ERROR)
273 << "uprobestats_monitor_disruptive_app_activities disabled by flag";
274 continue;
275 }
276 if (resolvedProbe.filename ==
277 "prog_MalwareSignal_uprobe_set_component_enabled_setting" &&
278 !android::uprobestats::mainline::flags::
279 uprobestats_monitor_disruptive_app_activities()) {
280 LOG(ERROR)
281 << "uprobestats_monitor_disruptive_app_activities disabled by flag";
282 continue;
283 }
284 auto openResult = bpf::bpfPerfEventOpen(
285 resolvedProbe.filename.c_str(), resolvedProbe.offset,
286 resolvedTask.value().pid,
287 prefixBpf(resolvedProbe.probeConfig.bpf_name()).c_str());
288 if (openResult != 0) {
289 LOG(ERROR) << "Failed to open bpf "
290 << resolvedProbe.probeConfig.bpf_name();
291 return 1;
292 }
293 }
294
295 std::vector<std::thread> threads;
296 for (auto mapPath : resolvedTask.value().taskConfig.bpf_maps()) {
297 if (mapPath ==
298 "map_ProcessManagement_update_device_idle_temp_allowlist_record" &&
299 !android::uprobestats::flag_selector::
300 uprobestats_support_update_device_idle_temp_allowlist()) {
301 LOG(ERROR) << "update_device_idle_temp_allowlist disabled by flag";
302 }
303 if (mapPath == "map_MalwareSignal_output_buf" &&
304 !android::uprobestats::mainline::flags::
305 uprobestats_monitor_disruptive_app_activities()) {
306 LOG(ERROR)
307 << "uprobestats_monitor_disruptive_app_activities disabled by flag";
308 continue;
309 }
310 auto pollArgs =
311 PollArgs{prefixBpf(mapPath), resolvedTask.value().taskConfig};
312 LOG_IF_DEBUG(
313 "Starting thread to collect results from mapPath: " << mapPath);
314 threads.emplace_back(doPoll, pollArgs);
315 }
316 for (auto &thread : threads) {
317 thread.join();
318 }
319
320 LOG_IF_DEBUG("done.");
321
322 return 0;
323 }
324