• 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 <sys/file.h>
22 #include <unistd.h>
23 
24 #include "perfetto/base/build_config.h"
25 #include "perfetto/base/compiler.h"
26 #include "perfetto/ext/base/file_utils.h"
27 #include "perfetto/ext/base/scoped_file.h"
28 #include "perfetto/ext/base/string_utils.h"
29 #include "perfetto/ext/base/utils.h"
30 #include "src/kallsyms/kernel_symbol_map.h"
31 
32 namespace perfetto {
33 namespace {
34 
35 const char kKallsymsPath[] = "/proc/kallsyms";
36 const char kPtrRestrictPath[] = "/proc/sys/kernel/kptr_restrict";
37 const char kEnvName[] = "ANDROID_FILE__proc_kallsyms";
38 
ParseInheritedAndroidKallsyms(KernelSymbolMap * symbol_map)39 size_t ParseInheritedAndroidKallsyms(KernelSymbolMap* symbol_map) {
40   const char* fd_str = getenv(kEnvName);
41   auto inherited_fd = base::CStringToInt32(fd_str ? fd_str : "");
42   // Note: this is also the early exit for non-platform builds.
43   if (!inherited_fd.has_value()) {
44     PERFETTO_DLOG("Failed to parse %s (%s)", kEnvName, fd_str ? fd_str : "N/A");
45     return 0;
46   }
47 
48   // We've inherited a special fd for kallsyms from init, but we might be
49   // sharing the underlying open file description with a concurrent process.
50   // Even if we use pread() for reading at absolute offsets, the underlying
51   // kernel seqfile is stateful and remembers where the last read stopped. In
52   // the worst case, two concurrent readers will cause a quadratic slowdown
53   // since the kernel reconstructs the seqfile from the beginning whenever two
54   // reads are not consequent.
55   // The chosen approach is to use provisional file locks to coordinate access.
56   // However we cannot use the special fd for locking, since the locks are based
57   // on the underlying open file description (in other words, both sharers will
58   // think they own the same lock). Therefore we open /proc/kallsyms again
59   // purely for locking purposes.
60   base::ScopedFile fd_for_lock = base::OpenFile(kKallsymsPath, O_RDONLY);
61   if (!fd_for_lock) {
62     PERFETTO_PLOG("Failed to open kallsyms for locking.");
63     return 0;
64   }
65 
66   // Blocking lock since the only possible contention is
67   // traced_probes<->traced_perf, which will both lock only for the duration of
68   // the parse. Worst case, the task watchdog will restart the process.
69   //
70   // Lock goes away when |fd_for_lock| gets closed at end of scope.
71   if (flock(*fd_for_lock, LOCK_EX) != 0) {
72     PERFETTO_PLOG("Unexpected error in flock(kallsyms).");
73     return 0;
74   }
75 
76   return symbol_map->Parse(*inherited_fd);
77 }
78 
79 // This class takes care of temporarily lowering the kptr_restrict sysctl.
80 // Otherwise the symbol addresses in /proc/kallsyms will be zeroed out on most
81 // Linux configurations.
82 //
83 // On Android platform builds, this is solved by inheriting a kallsyms fd from
84 // init, with symbols being visible as that is evaluated at the time of the
85 // initial open().
86 //
87 // On Linux and standalone builds, we rely on this class in combination with
88 // either:
89 // - the sysctls (kptr_restrict, perf_event_paranoid) or this process'
90 //   capabilitied to be sufficient for addresses to be visible.
91 // - this process to be running as root / CAP_SYS_ADMIN, in which case this
92 //   class will attempt to temporarily override kptr_restrict ourselves.
93 class ScopedKptrUnrestrict {
94  public:
95   ScopedKptrUnrestrict();   // Lowers kptr_restrict if necessary.
96   ~ScopedKptrUnrestrict();  // Restores the initial kptr_restrict.
97 
98  private:
99   static void WriteKptrRestrict(const std::string&);
100 
101   std::string initial_value_;
102 };
103 
ScopedKptrUnrestrict()104 ScopedKptrUnrestrict::ScopedKptrUnrestrict() {
105   if (LazyKernelSymbolizer::CanReadKernelSymbolAddresses()) {
106     // Symbols already visible, don't touch anything.
107     return;
108   }
109 
110   bool read_res = base::ReadFile(kPtrRestrictPath, &initial_value_);
111   if (!read_res) {
112     PERFETTO_PLOG("Failed to read %s", kPtrRestrictPath);
113     return;
114   }
115 
116   // Progressively lower kptr_restrict until we can read kallsyms.
117   for (int value = atoi(initial_value_.c_str()); value > 0; --value) {
118     WriteKptrRestrict(std::to_string(value));
119     if (LazyKernelSymbolizer::CanReadKernelSymbolAddresses())
120       return;
121   }
122 }
123 
~ScopedKptrUnrestrict()124 ScopedKptrUnrestrict::~ScopedKptrUnrestrict() {
125   if (initial_value_.empty())
126     return;
127   WriteKptrRestrict(initial_value_);
128 }
129 
WriteKptrRestrict(const std::string & value)130 void ScopedKptrUnrestrict::WriteKptrRestrict(const std::string& value) {
131   // Note: kptr_restrict requires O_WRONLY. O_RDWR won't work.
132   PERFETTO_DCHECK(!value.empty());
133   base::ScopedFile fd = base::OpenFile(kPtrRestrictPath, O_WRONLY);
134   auto wsize = write(*fd, value.c_str(), value.size());
135   if (wsize <= 0) {
136     PERFETTO_PLOG("Failed to set %s to %s", kPtrRestrictPath, value.c_str());
137   }
138 }
139 
140 }  // namespace
141 
142 LazyKernelSymbolizer::LazyKernelSymbolizer() = default;
143 LazyKernelSymbolizer::~LazyKernelSymbolizer() = default;
144 
GetOrCreateKernelSymbolMap()145 KernelSymbolMap* LazyKernelSymbolizer::GetOrCreateKernelSymbolMap() {
146   PERFETTO_DCHECK_THREAD(thread_checker_);
147   if (symbol_map_)
148     return symbol_map_.get();
149 
150   symbol_map_ = std::make_unique<KernelSymbolMap>();
151 
152   // Android platform builds: we have an fd from init.
153   size_t num_syms = ParseInheritedAndroidKallsyms(symbol_map_.get());
154   if (num_syms) {
155     return symbol_map_.get();
156   }
157 
158   // Otherwise, try reading the file directly, temporarily lowering
159   // kptr_restrict if we're running with sufficient privileges.
160   ScopedKptrUnrestrict kptr_unrestrict;
161   auto fd = base::OpenFile(kKallsymsPath, O_RDONLY);
162   symbol_map_->Parse(*fd);
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