xref: /aosp_15_r20/external/perfetto/src/kallsyms/lazy_kernel_symbolizer.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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