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