/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "src/kallsyms/lazy_kernel_symbolizer.h" #include #include #include "perfetto/base/build_config.h" #include "perfetto/base/compiler.h" #include "perfetto/ext/base/file_utils.h" #include "perfetto/ext/base/scoped_file.h" #include "perfetto/ext/base/utils.h" #include "src/kallsyms/kernel_symbol_map.h" #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) #include #endif namespace perfetto { namespace { const char kKallsymsPath[] = "/proc/kallsyms"; const char kPtrRestrictPath[] = "/proc/sys/kernel/kptr_restrict"; const char kLowerPtrRestrictAndroidProp[] = "security.lower_kptr_restrict"; // This class takes care of temporarily lowering kptr_restrict and putting it // back to the original value if necessary. It solves the following problem: // When reading /proc/kallsyms on Linux/Android, the symbol addresses can be // masked out (i.e. they are all 00000000) through the kptr_restrict file. // On Android kptr_restrict defaults to 2. On Linux, it depends on the // distribution. On Android we cannot simply write() kptr_restrict ourselves. // Doing so requires the union of: // - filesystem ACLs: kptr_restrict is rw-r--r--// and owned by root. // - Selinux rules: kptr_restrict is labelled as proc_security and restricted. // - CAP_SYS_ADMIN: when writing to kptr_restrict, the kernel enforces that the // caller has the SYS_ADMIN capability at write() time. // The latter would be problematic, we don't want traced_probes to have that, // CAP_SYS_ADMIN is too broad. // Instead, we opt for the following model: traced_probes sets an Android // property introduced in S (security.lower_kptr_restrict); init (which // satisfies all the requirements above) in turn sets kptr_restrict. // On Linux and standalone builds, instead, we don't have many options. Either: // - The system administrator takes care of lowering kptr_restrict before // tracing. // - The system administrator runs traced_probes as root / CAP_SYS_ADMIN and we // temporarily lower and restore kptr_restrict ourselves. // This class deals with all these cases. class ScopedKptrUnrestrict { public: ScopedKptrUnrestrict(); // Lowers kptr_restrict if necessary. ~ScopedKptrUnrestrict(); // Restores the initial kptr_restrict. private: static void WriteKptrRestrict(const std::string&); static const bool kUseAndroidProperty; std::string initial_value_; bool restore_on_dtor_ = true; }; #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) // This is true only on Android in-tree builds (not on standalone). const bool ScopedKptrUnrestrict::kUseAndroidProperty = true; #else const bool ScopedKptrUnrestrict::kUseAndroidProperty = false; #endif ScopedKptrUnrestrict::ScopedKptrUnrestrict() { if (LazyKernelSymbolizer::CanReadKernelSymbolAddresses()) { // Everything seems to work (e.g., we are running as root and kptr_restrict // is < 2). Don't touching anything. restore_on_dtor_ = false; return; } if (kUseAndroidProperty) { #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) __system_property_set(kLowerPtrRestrictAndroidProp, "1"); #endif // Init takes some time to react to the property change. // Unfortunately, we cannot read kptr_restrict because of SELinux. Instead, // we detect this by reading the initial lines of kallsyms and checking // that they are non-zero. This loop waits for at most 250ms (50 * 5ms). for (int attempt = 1; attempt <= 50; ++attempt) { usleep(5000); if (LazyKernelSymbolizer::CanReadKernelSymbolAddresses()) return; } PERFETTO_ELOG("kallsyms addresses are still masked after setting %s", kLowerPtrRestrictAndroidProp); return; } // if (kUseAndroidProperty) // On Linux and Android standalone, read the kptr_restrict value and lower it // if needed. bool read_res = base::ReadFile(kPtrRestrictPath, &initial_value_); if (!read_res) { PERFETTO_PLOG("Failed to read %s", kPtrRestrictPath); return; } // Progressively lower kptr_restrict until we can read kallsyms. for (int value = atoi(initial_value_.c_str()); value > 0; --value) { WriteKptrRestrict(std::to_string(value)); if (LazyKernelSymbolizer::CanReadKernelSymbolAddresses()) return; } } ScopedKptrUnrestrict::~ScopedKptrUnrestrict() { if (!restore_on_dtor_) return; if (kUseAndroidProperty) { #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) __system_property_set(kLowerPtrRestrictAndroidProp, "0"); #endif } else if (!initial_value_.empty()) { WriteKptrRestrict(initial_value_); } } void ScopedKptrUnrestrict::WriteKptrRestrict(const std::string& value) { // Note: kptr_restrict requires O_WRONLY. O_RDWR won't work. PERFETTO_DCHECK(!value.empty()); base::ScopedFile fd = base::OpenFile(kPtrRestrictPath, O_WRONLY); auto wsize = write(*fd, value.c_str(), value.size()); if (wsize <= 0) PERFETTO_PLOG("Failed to set %s to %s", kPtrRestrictPath, value.c_str()); } } // namespace LazyKernelSymbolizer::LazyKernelSymbolizer() = default; LazyKernelSymbolizer::~LazyKernelSymbolizer() = default; KernelSymbolMap* LazyKernelSymbolizer::GetOrCreateKernelSymbolMap() { PERFETTO_DCHECK_THREAD(thread_checker_); if (symbol_map_) return symbol_map_.get(); symbol_map_.reset(new KernelSymbolMap()); // If kptr_restrict is set, try temporarily lifting it (it works only if // traced_probes is run as a privileged user). ScopedKptrUnrestrict kptr_unrestrict; symbol_map_->Parse(kKallsymsPath); return symbol_map_.get(); } void LazyKernelSymbolizer::Destroy() { PERFETTO_DCHECK_THREAD(thread_checker_); symbol_map_.reset(); base::MaybeReleaseAllocatorMemToOS(); // For Scudo, b/170217718. } // static bool LazyKernelSymbolizer::CanReadKernelSymbolAddresses( const char* ksyms_path_for_testing) { auto* path = ksyms_path_for_testing ? ksyms_path_for_testing : kKallsymsPath; base::ScopedFile fd = base::OpenFile(path, O_RDONLY); if (!fd) { PERFETTO_PLOG("open(%s) failed", kKallsymsPath); return false; } // Don't just use fscanf() as that might read the whole file (b/36473442). char buf[4096]; auto rsize_signed = base::Read(*fd, buf, sizeof(buf) - 1); if (rsize_signed <= 0) { PERFETTO_PLOG("read(%s) failed", kKallsymsPath); return false; } size_t rsize = static_cast(rsize_signed); buf[rsize] = '\0'; // Iterate over the first page of kallsyms. If we find any non-zero address // call it success. If all addresses are 0, pessimistically assume // kptr_restrict is still restricted. // We cannot look only at the first line because on some devices // /proc/kallsyms can look like this (note the zeros in the first two addrs): // 0000000000000000 A fixed_percpu_data // 0000000000000000 A __per_cpu_start // 0000000000001000 A cpu_debug_store bool reading_addr = true; bool addr_is_zero = true; for (size_t i = 0; i < rsize; i++) { const char c = buf[i]; if (reading_addr) { const bool is_hex = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); if (is_hex) { addr_is_zero = addr_is_zero && c == '0'; } else { if (!addr_is_zero) return true; reading_addr = false; // Will consume the rest of the line until \n. } } else if (c == '\n') { reading_addr = true; } // if (!reading_addr) } // for char in buf return false; } } // namespace perfetto