1*6dbdd20aSAndroid Build Coastguard Worker /*
2*6dbdd20aSAndroid Build Coastguard Worker * Copyright (C) 2020 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker *
4*6dbdd20aSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker *
8*6dbdd20aSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker *
10*6dbdd20aSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker * limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker */
16*6dbdd20aSAndroid Build Coastguard Worker
17*6dbdd20aSAndroid Build Coastguard Worker #include "src/kallsyms/lazy_kernel_symbolizer.h"
18*6dbdd20aSAndroid Build Coastguard Worker
19*6dbdd20aSAndroid Build Coastguard Worker #include <string>
20*6dbdd20aSAndroid Build Coastguard Worker
21*6dbdd20aSAndroid Build Coastguard Worker #include <unistd.h>
22*6dbdd20aSAndroid Build Coastguard Worker
23*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/base/build_config.h"
24*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/base/compiler.h"
25*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/file_utils.h"
26*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/scoped_file.h"
27*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/utils.h"
28*6dbdd20aSAndroid Build Coastguard Worker #include "src/kallsyms/kernel_symbol_map.h"
29*6dbdd20aSAndroid Build Coastguard Worker
30*6dbdd20aSAndroid Build Coastguard Worker #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
31*6dbdd20aSAndroid Build Coastguard Worker #include <sys/system_properties.h>
32*6dbdd20aSAndroid Build Coastguard Worker #endif
33*6dbdd20aSAndroid Build Coastguard Worker
34*6dbdd20aSAndroid Build Coastguard Worker namespace perfetto {
35*6dbdd20aSAndroid Build Coastguard Worker
36*6dbdd20aSAndroid Build Coastguard Worker namespace {
37*6dbdd20aSAndroid Build Coastguard Worker
38*6dbdd20aSAndroid Build Coastguard Worker const char kKallsymsPath[] = "/proc/kallsyms";
39*6dbdd20aSAndroid Build Coastguard Worker const char kPtrRestrictPath[] = "/proc/sys/kernel/kptr_restrict";
40*6dbdd20aSAndroid Build Coastguard Worker const char kLowerPtrRestrictAndroidProp[] = "security.lower_kptr_restrict";
41*6dbdd20aSAndroid Build Coastguard Worker
42*6dbdd20aSAndroid Build Coastguard Worker // This class takes care of temporarily lowering kptr_restrict and putting it
43*6dbdd20aSAndroid Build Coastguard Worker // back to the original value if necessary. It solves the following problem:
44*6dbdd20aSAndroid Build Coastguard Worker // When reading /proc/kallsyms on Linux/Android, the symbol addresses can be
45*6dbdd20aSAndroid Build Coastguard Worker // masked out (i.e. they are all 00000000) through the kptr_restrict file.
46*6dbdd20aSAndroid Build Coastguard Worker // On Android kptr_restrict defaults to 2. On Linux, it depends on the
47*6dbdd20aSAndroid Build Coastguard Worker // distribution. On Android we cannot simply write() kptr_restrict ourselves.
48*6dbdd20aSAndroid Build Coastguard Worker // Doing so requires the union of:
49*6dbdd20aSAndroid Build Coastguard Worker // - filesystem ACLs: kptr_restrict is rw-r--r--// and owned by root.
50*6dbdd20aSAndroid Build Coastguard Worker // - Selinux rules: kptr_restrict is labelled as proc_security and restricted.
51*6dbdd20aSAndroid Build Coastguard Worker // - CAP_SYS_ADMIN: when writing to kptr_restrict, the kernel enforces that the
52*6dbdd20aSAndroid Build Coastguard Worker // caller has the SYS_ADMIN capability at write() time.
53*6dbdd20aSAndroid Build Coastguard Worker // The latter would be problematic, we don't want traced_probes to have that,
54*6dbdd20aSAndroid Build Coastguard Worker // CAP_SYS_ADMIN is too broad.
55*6dbdd20aSAndroid Build Coastguard Worker // Instead, we opt for the following model: traced_probes sets an Android
56*6dbdd20aSAndroid Build Coastguard Worker // property introduced in S (security.lower_kptr_restrict); init (which
57*6dbdd20aSAndroid Build Coastguard Worker // satisfies all the requirements above) in turn sets kptr_restrict.
58*6dbdd20aSAndroid Build Coastguard Worker // On Linux and standalone builds, instead, we don't have many options. Either:
59*6dbdd20aSAndroid Build Coastguard Worker // - The system administrator takes care of lowering kptr_restrict before
60*6dbdd20aSAndroid Build Coastguard Worker // tracing.
61*6dbdd20aSAndroid Build Coastguard Worker // - The system administrator runs traced_probes as root / CAP_SYS_ADMIN and we
62*6dbdd20aSAndroid Build Coastguard Worker // temporarily lower and restore kptr_restrict ourselves.
63*6dbdd20aSAndroid Build Coastguard Worker // This class deals with all these cases.
64*6dbdd20aSAndroid Build Coastguard Worker class ScopedKptrUnrestrict {
65*6dbdd20aSAndroid Build Coastguard Worker public:
66*6dbdd20aSAndroid Build Coastguard Worker ScopedKptrUnrestrict(); // Lowers kptr_restrict if necessary.
67*6dbdd20aSAndroid Build Coastguard Worker ~ScopedKptrUnrestrict(); // Restores the initial kptr_restrict.
68*6dbdd20aSAndroid Build Coastguard Worker
69*6dbdd20aSAndroid Build Coastguard Worker private:
70*6dbdd20aSAndroid Build Coastguard Worker static void WriteKptrRestrict(const std::string&);
71*6dbdd20aSAndroid Build Coastguard Worker
72*6dbdd20aSAndroid Build Coastguard Worker static const bool kUseAndroidProperty;
73*6dbdd20aSAndroid Build Coastguard Worker std::string initial_value_;
74*6dbdd20aSAndroid Build Coastguard Worker bool restore_on_dtor_ = true;
75*6dbdd20aSAndroid Build Coastguard Worker };
76*6dbdd20aSAndroid Build Coastguard Worker
77*6dbdd20aSAndroid Build Coastguard Worker #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
78*6dbdd20aSAndroid Build Coastguard Worker // This is true only on Android in-tree builds (not on standalone).
79*6dbdd20aSAndroid Build Coastguard Worker const bool ScopedKptrUnrestrict::kUseAndroidProperty = true;
80*6dbdd20aSAndroid Build Coastguard Worker #else
81*6dbdd20aSAndroid Build Coastguard Worker const bool ScopedKptrUnrestrict::kUseAndroidProperty = false;
82*6dbdd20aSAndroid Build Coastguard Worker #endif
83*6dbdd20aSAndroid Build Coastguard Worker
ScopedKptrUnrestrict()84*6dbdd20aSAndroid Build Coastguard Worker ScopedKptrUnrestrict::ScopedKptrUnrestrict() {
85*6dbdd20aSAndroid Build Coastguard Worker if (LazyKernelSymbolizer::CanReadKernelSymbolAddresses()) {
86*6dbdd20aSAndroid Build Coastguard Worker // Everything seems to work (e.g., we are running as root and kptr_restrict
87*6dbdd20aSAndroid Build Coastguard Worker // is < 2). Don't touching anything.
88*6dbdd20aSAndroid Build Coastguard Worker restore_on_dtor_ = false;
89*6dbdd20aSAndroid Build Coastguard Worker return;
90*6dbdd20aSAndroid Build Coastguard Worker }
91*6dbdd20aSAndroid Build Coastguard Worker
92*6dbdd20aSAndroid Build Coastguard Worker if (kUseAndroidProperty) {
93*6dbdd20aSAndroid Build Coastguard Worker #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
94*6dbdd20aSAndroid Build Coastguard Worker __system_property_set(kLowerPtrRestrictAndroidProp, "1");
95*6dbdd20aSAndroid Build Coastguard Worker #endif
96*6dbdd20aSAndroid Build Coastguard Worker // Init takes some time to react to the property change.
97*6dbdd20aSAndroid Build Coastguard Worker // Unfortunately, we cannot read kptr_restrict because of SELinux. Instead,
98*6dbdd20aSAndroid Build Coastguard Worker // we detect this by reading the initial lines of kallsyms and checking
99*6dbdd20aSAndroid Build Coastguard Worker // that they are non-zero. This loop waits for at most 250ms (50 * 5ms).
100*6dbdd20aSAndroid Build Coastguard Worker for (int attempt = 1; attempt <= 50; ++attempt) {
101*6dbdd20aSAndroid Build Coastguard Worker usleep(5000);
102*6dbdd20aSAndroid Build Coastguard Worker if (LazyKernelSymbolizer::CanReadKernelSymbolAddresses())
103*6dbdd20aSAndroid Build Coastguard Worker return;
104*6dbdd20aSAndroid Build Coastguard Worker }
105*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_ELOG("kallsyms addresses are still masked after setting %s",
106*6dbdd20aSAndroid Build Coastguard Worker kLowerPtrRestrictAndroidProp);
107*6dbdd20aSAndroid Build Coastguard Worker return;
108*6dbdd20aSAndroid Build Coastguard Worker } // if (kUseAndroidProperty)
109*6dbdd20aSAndroid Build Coastguard Worker
110*6dbdd20aSAndroid Build Coastguard Worker // On Linux and Android standalone, read the kptr_restrict value and lower it
111*6dbdd20aSAndroid Build Coastguard Worker // if needed.
112*6dbdd20aSAndroid Build Coastguard Worker bool read_res = base::ReadFile(kPtrRestrictPath, &initial_value_);
113*6dbdd20aSAndroid Build Coastguard Worker if (!read_res) {
114*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_PLOG("Failed to read %s", kPtrRestrictPath);
115*6dbdd20aSAndroid Build Coastguard Worker return;
116*6dbdd20aSAndroid Build Coastguard Worker }
117*6dbdd20aSAndroid Build Coastguard Worker
118*6dbdd20aSAndroid Build Coastguard Worker // Progressively lower kptr_restrict until we can read kallsyms.
119*6dbdd20aSAndroid Build Coastguard Worker for (int value = atoi(initial_value_.c_str()); value > 0; --value) {
120*6dbdd20aSAndroid Build Coastguard Worker WriteKptrRestrict(std::to_string(value));
121*6dbdd20aSAndroid Build Coastguard Worker if (LazyKernelSymbolizer::CanReadKernelSymbolAddresses())
122*6dbdd20aSAndroid Build Coastguard Worker return;
123*6dbdd20aSAndroid Build Coastguard Worker }
124*6dbdd20aSAndroid Build Coastguard Worker }
125*6dbdd20aSAndroid Build Coastguard Worker
~ScopedKptrUnrestrict()126*6dbdd20aSAndroid Build Coastguard Worker ScopedKptrUnrestrict::~ScopedKptrUnrestrict() {
127*6dbdd20aSAndroid Build Coastguard Worker if (!restore_on_dtor_)
128*6dbdd20aSAndroid Build Coastguard Worker return;
129*6dbdd20aSAndroid Build Coastguard Worker if (kUseAndroidProperty) {
130*6dbdd20aSAndroid Build Coastguard Worker #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
131*6dbdd20aSAndroid Build Coastguard Worker __system_property_set(kLowerPtrRestrictAndroidProp, "0");
132*6dbdd20aSAndroid Build Coastguard Worker #endif
133*6dbdd20aSAndroid Build Coastguard Worker } else if (!initial_value_.empty()) {
134*6dbdd20aSAndroid Build Coastguard Worker WriteKptrRestrict(initial_value_);
135*6dbdd20aSAndroid Build Coastguard Worker }
136*6dbdd20aSAndroid Build Coastguard Worker }
137*6dbdd20aSAndroid Build Coastguard Worker
WriteKptrRestrict(const std::string & value)138*6dbdd20aSAndroid Build Coastguard Worker void ScopedKptrUnrestrict::WriteKptrRestrict(const std::string& value) {
139*6dbdd20aSAndroid Build Coastguard Worker // Note: kptr_restrict requires O_WRONLY. O_RDWR won't work.
140*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK(!value.empty());
141*6dbdd20aSAndroid Build Coastguard Worker base::ScopedFile fd = base::OpenFile(kPtrRestrictPath, O_WRONLY);
142*6dbdd20aSAndroid Build Coastguard Worker auto wsize = write(*fd, value.c_str(), value.size());
143*6dbdd20aSAndroid Build Coastguard Worker if (wsize <= 0)
144*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_PLOG("Failed to set %s to %s", kPtrRestrictPath, value.c_str());
145*6dbdd20aSAndroid Build Coastguard Worker }
146*6dbdd20aSAndroid Build Coastguard Worker
147*6dbdd20aSAndroid Build Coastguard Worker } // namespace
148*6dbdd20aSAndroid Build Coastguard Worker
149*6dbdd20aSAndroid Build Coastguard Worker LazyKernelSymbolizer::LazyKernelSymbolizer() = default;
150*6dbdd20aSAndroid Build Coastguard Worker LazyKernelSymbolizer::~LazyKernelSymbolizer() = default;
151*6dbdd20aSAndroid Build Coastguard Worker
GetOrCreateKernelSymbolMap()152*6dbdd20aSAndroid Build Coastguard Worker KernelSymbolMap* LazyKernelSymbolizer::GetOrCreateKernelSymbolMap() {
153*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK_THREAD(thread_checker_);
154*6dbdd20aSAndroid Build Coastguard Worker if (symbol_map_)
155*6dbdd20aSAndroid Build Coastguard Worker return symbol_map_.get();
156*6dbdd20aSAndroid Build Coastguard Worker
157*6dbdd20aSAndroid Build Coastguard Worker symbol_map_.reset(new KernelSymbolMap());
158*6dbdd20aSAndroid Build Coastguard Worker
159*6dbdd20aSAndroid Build Coastguard Worker // If kptr_restrict is set, try temporarily lifting it (it works only if
160*6dbdd20aSAndroid Build Coastguard Worker // traced_probes is run as a privileged user).
161*6dbdd20aSAndroid Build Coastguard Worker ScopedKptrUnrestrict kptr_unrestrict;
162*6dbdd20aSAndroid Build Coastguard Worker symbol_map_->Parse(kKallsymsPath);
163*6dbdd20aSAndroid Build Coastguard Worker return symbol_map_.get();
164*6dbdd20aSAndroid Build Coastguard Worker }
165*6dbdd20aSAndroid Build Coastguard Worker
Destroy()166*6dbdd20aSAndroid Build Coastguard Worker void LazyKernelSymbolizer::Destroy() {
167*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK_THREAD(thread_checker_);
168*6dbdd20aSAndroid Build Coastguard Worker symbol_map_.reset();
169*6dbdd20aSAndroid Build Coastguard Worker base::MaybeReleaseAllocatorMemToOS(); // For Scudo, b/170217718.
170*6dbdd20aSAndroid Build Coastguard Worker }
171*6dbdd20aSAndroid Build Coastguard Worker
172*6dbdd20aSAndroid Build Coastguard Worker // static
CanReadKernelSymbolAddresses(const char * ksyms_path_for_testing)173*6dbdd20aSAndroid Build Coastguard Worker bool LazyKernelSymbolizer::CanReadKernelSymbolAddresses(
174*6dbdd20aSAndroid Build Coastguard Worker const char* ksyms_path_for_testing) {
175*6dbdd20aSAndroid Build Coastguard Worker auto* path = ksyms_path_for_testing ? ksyms_path_for_testing : kKallsymsPath;
176*6dbdd20aSAndroid Build Coastguard Worker base::ScopedFile fd = base::OpenFile(path, O_RDONLY);
177*6dbdd20aSAndroid Build Coastguard Worker if (!fd) {
178*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_PLOG("open(%s) failed", kKallsymsPath);
179*6dbdd20aSAndroid Build Coastguard Worker return false;
180*6dbdd20aSAndroid Build Coastguard Worker }
181*6dbdd20aSAndroid Build Coastguard Worker // Don't just use fscanf() as that might read the whole file (b/36473442).
182*6dbdd20aSAndroid Build Coastguard Worker char buf[4096];
183*6dbdd20aSAndroid Build Coastguard Worker auto rsize_signed = base::Read(*fd, buf, sizeof(buf) - 1);
184*6dbdd20aSAndroid Build Coastguard Worker if (rsize_signed <= 0) {
185*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_PLOG("read(%s) failed", kKallsymsPath);
186*6dbdd20aSAndroid Build Coastguard Worker return false;
187*6dbdd20aSAndroid Build Coastguard Worker }
188*6dbdd20aSAndroid Build Coastguard Worker size_t rsize = static_cast<size_t>(rsize_signed);
189*6dbdd20aSAndroid Build Coastguard Worker buf[rsize] = '\0';
190*6dbdd20aSAndroid Build Coastguard Worker
191*6dbdd20aSAndroid Build Coastguard Worker // Iterate over the first page of kallsyms. If we find any non-zero address
192*6dbdd20aSAndroid Build Coastguard Worker // call it success. If all addresses are 0, pessimistically assume
193*6dbdd20aSAndroid Build Coastguard Worker // kptr_restrict is still restricted.
194*6dbdd20aSAndroid Build Coastguard Worker // We cannot look only at the first line because on some devices
195*6dbdd20aSAndroid Build Coastguard Worker // /proc/kallsyms can look like this (note the zeros in the first two addrs):
196*6dbdd20aSAndroid Build Coastguard Worker // 0000000000000000 A fixed_percpu_data
197*6dbdd20aSAndroid Build Coastguard Worker // 0000000000000000 A __per_cpu_start
198*6dbdd20aSAndroid Build Coastguard Worker // 0000000000001000 A cpu_debug_store
199*6dbdd20aSAndroid Build Coastguard Worker bool reading_addr = true;
200*6dbdd20aSAndroid Build Coastguard Worker bool addr_is_zero = true;
201*6dbdd20aSAndroid Build Coastguard Worker for (size_t i = 0; i < rsize; i++) {
202*6dbdd20aSAndroid Build Coastguard Worker const char c = buf[i];
203*6dbdd20aSAndroid Build Coastguard Worker if (reading_addr) {
204*6dbdd20aSAndroid Build Coastguard Worker const bool is_hex = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
205*6dbdd20aSAndroid Build Coastguard Worker if (is_hex) {
206*6dbdd20aSAndroid Build Coastguard Worker addr_is_zero = addr_is_zero && c == '0';
207*6dbdd20aSAndroid Build Coastguard Worker } else {
208*6dbdd20aSAndroid Build Coastguard Worker if (!addr_is_zero)
209*6dbdd20aSAndroid Build Coastguard Worker return true;
210*6dbdd20aSAndroid Build Coastguard Worker reading_addr = false; // Will consume the rest of the line until \n.
211*6dbdd20aSAndroid Build Coastguard Worker }
212*6dbdd20aSAndroid Build Coastguard Worker } else if (c == '\n') {
213*6dbdd20aSAndroid Build Coastguard Worker reading_addr = true;
214*6dbdd20aSAndroid Build Coastguard Worker } // if (!reading_addr)
215*6dbdd20aSAndroid Build Coastguard Worker } // for char in buf
216*6dbdd20aSAndroid Build Coastguard Worker
217*6dbdd20aSAndroid Build Coastguard Worker return false;
218*6dbdd20aSAndroid Build Coastguard Worker }
219*6dbdd20aSAndroid Build Coastguard Worker
220*6dbdd20aSAndroid Build Coastguard Worker } // namespace perfetto
221