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