• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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