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