123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565 |
- // Copyright 2017 The Crashpad Authors. All rights reserved.
- //
- // 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 "snapshot/linux/process_reader_linux.h"
- #include <elf.h>
- #include <errno.h>
- #include <sched.h>
- #include <string.h>
- #include <sys/resource.h>
- #include <unistd.h>
- #include <algorithm>
- #include "base/logging.h"
- #include "base/strings/stringprintf.h"
- #include "build/build_config.h"
- #include "snapshot/linux/debug_rendezvous.h"
- #include "util/linux/auxiliary_vector.h"
- #include "util/linux/proc_stat_reader.h"
- #if BUILDFLAG(IS_ANDROID)
- #include <android/api-level.h>
- #endif
- namespace crashpad {
- namespace {
- bool ShouldMergeStackMappings(const MemoryMap::Mapping& stack_mapping,
- const MemoryMap::Mapping& adj_mapping) {
- DCHECK(stack_mapping.readable);
- return adj_mapping.readable && stack_mapping.device == adj_mapping.device &&
- stack_mapping.inode == adj_mapping.inode &&
- (stack_mapping.name == adj_mapping.name ||
- stack_mapping.name.empty() || adj_mapping.name.empty());
- }
- } // namespace
- ProcessReaderLinux::Thread::Thread()
- : thread_info(),
- stack_region_address(0),
- stack_region_size(0),
- name(),
- tid(-1),
- static_priority(-1),
- nice_value(-1) {}
- ProcessReaderLinux::Thread::~Thread() {}
- bool ProcessReaderLinux::Thread::InitializePtrace(
- PtraceConnection* connection) {
- if (!connection->GetThreadInfo(tid, &thread_info)) {
- return false;
- }
- // From man proc(5):
- //
- // /proc/[pid]/comm (since Linux 2.6.33)
- //
- // Different threads in the same process may have different comm values,
- // accessible via /proc/[pid]/task/[tid]/comm.
- const std::string path = base::StringPrintf(
- "/proc/%d/task/%d/comm", connection->GetProcessID(), tid);
- if (connection->ReadFileContents(base::FilePath(path), &name)) {
- if (!name.empty() && name.back() == '\n') {
- // Remove the final newline character.
- name.pop_back();
- }
- } else {
- // Continue on without the thread name.
- }
- // TODO(jperaza): Collect scheduling priorities via the broker when they can't
- // be collected directly.
- have_priorities = false;
- // TODO(jperaza): Starting with Linux 3.14, scheduling policy, static
- // priority, and nice value can be collected all in one call with
- // sched_getattr().
- int res = sched_getscheduler(tid);
- if (res < 0) {
- PLOG(WARNING) << "sched_getscheduler";
- return true;
- }
- sched_policy = res;
- sched_param param;
- if (sched_getparam(tid, ¶m) != 0) {
- PLOG(WARNING) << "sched_getparam";
- return true;
- }
- static_priority = param.sched_priority;
- errno = 0;
- res = getpriority(PRIO_PROCESS, tid);
- if (res == -1 && errno) {
- PLOG(WARNING) << "getpriority";
- return true;
- }
- nice_value = res;
- have_priorities = true;
- return true;
- }
- void ProcessReaderLinux::Thread::InitializeStack(ProcessReaderLinux* reader) {
- LinuxVMAddress stack_pointer;
- #if defined(ARCH_CPU_X86_FAMILY)
- stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.rsp
- : thread_info.thread_context.t32.esp;
- #elif defined(ARCH_CPU_ARM_FAMILY)
- stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.sp
- : thread_info.thread_context.t32.sp;
- #elif defined(ARCH_CPU_MIPS_FAMILY)
- stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.regs[29]
- : thread_info.thread_context.t32.regs[29];
- #elif defined(ARCH_CPU_RISCV_FAMILY)
- stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.sp
- : thread_info.thread_context.t32.sp;
- #else
- #error Port.
- #endif
- InitializeStackFromSP(reader, stack_pointer);
- }
- void ProcessReaderLinux::Thread::InitializeStackFromSP(
- ProcessReaderLinux* reader,
- LinuxVMAddress stack_pointer) {
- const MemoryMap* memory_map = reader->GetMemoryMap();
- // If we can't find the mapping, it's probably a bad stack pointer
- const MemoryMap::Mapping* mapping = memory_map->FindMapping(stack_pointer);
- if (!mapping) {
- LOG(WARNING) << "no stack mapping";
- return;
- }
- LinuxVMAddress stack_region_start =
- reader->Memory()->PointerToAddress(stack_pointer);
- // We've hit what looks like a guard page; skip to the end and check for a
- // mapped stack region.
- if (!mapping->readable) {
- stack_region_start = mapping->range.End();
- mapping = memory_map->FindMapping(stack_region_start);
- if (!mapping) {
- LOG(WARNING) << "no stack mapping";
- return;
- }
- } else {
- #if defined(ARCH_CPU_X86_FAMILY)
- // Adjust start address to include the red zone
- if (reader->Is64Bit()) {
- constexpr LinuxVMSize kRedZoneSize = 128;
- LinuxVMAddress red_zone_base =
- stack_region_start - std::min(kRedZoneSize, stack_region_start);
- // Only include the red zone if it is part of a valid mapping
- if (red_zone_base >= mapping->range.Base()) {
- stack_region_start = red_zone_base;
- } else {
- const MemoryMap::Mapping* rz_mapping =
- memory_map->FindMapping(red_zone_base);
- if (rz_mapping && ShouldMergeStackMappings(*mapping, *rz_mapping)) {
- stack_region_start = red_zone_base;
- } else {
- stack_region_start = mapping->range.Base();
- }
- }
- }
- #endif
- }
- stack_region_address = stack_region_start;
- // If there are more mappings at the end of this one, they may be a
- // continuation of the stack.
- LinuxVMAddress stack_end = mapping->range.End();
- const MemoryMap::Mapping* next_mapping;
- while ((next_mapping = memory_map->FindMapping(stack_end)) &&
- ShouldMergeStackMappings(*mapping, *next_mapping)) {
- stack_end = next_mapping->range.End();
- }
- // The main thread should have an entry in the maps file just for its stack,
- // so we'll assume the base of the stack is at the end of the region. Other
- // threads' stacks may not have their own entries in the maps file if they
- // were user-allocated within a larger mapping, but pthreads places the TLS
- // at the high-address end of the stack so we can try using that to shrink
- // the stack region.
- stack_region_size = stack_end - stack_region_address;
- VMAddress tls_address = reader->Memory()->PointerToAddress(
- thread_info.thread_specific_data_address);
- if (tid != reader->ProcessID() && tls_address > stack_region_address &&
- tls_address < stack_end) {
- stack_region_size = tls_address - stack_region_address;
- }
- }
- ProcessReaderLinux::Module::Module()
- : name(), elf_reader(nullptr), type(ModuleSnapshot::kModuleTypeUnknown) {}
- ProcessReaderLinux::Module::~Module() = default;
- ProcessReaderLinux::ProcessReaderLinux()
- : connection_(),
- process_info_(),
- memory_map_(),
- threads_(),
- modules_(),
- elf_readers_(),
- is_64_bit_(false),
- initialized_threads_(false),
- initialized_modules_(false),
- initialized_() {}
- ProcessReaderLinux::~ProcessReaderLinux() {}
- bool ProcessReaderLinux::Initialize(PtraceConnection* connection) {
- INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
- DCHECK(connection);
- connection_ = connection;
- if (!process_info_.InitializeWithPtrace(connection_)) {
- return false;
- }
- if (!memory_map_.Initialize(connection_)) {
- return false;
- }
- is_64_bit_ = process_info_.Is64Bit();
- INITIALIZATION_STATE_SET_VALID(initialized_);
- return true;
- }
- bool ProcessReaderLinux::StartTime(timeval* start_time) const {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- return process_info_.StartTime(start_time);
- }
- bool ProcessReaderLinux::CPUTimes(timeval* user_time,
- timeval* system_time) const {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- timerclear(user_time);
- timerclear(system_time);
- timeval local_user_time;
- timerclear(&local_user_time);
- timeval local_system_time;
- timerclear(&local_system_time);
- for (const Thread& thread : threads_) {
- ProcStatReader stat;
- if (!stat.Initialize(connection_, thread.tid)) {
- return false;
- }
- timeval thread_user_time;
- if (!stat.UserCPUTime(&thread_user_time)) {
- return false;
- }
- timeval thread_system_time;
- if (!stat.SystemCPUTime(&thread_system_time)) {
- return false;
- }
- timeradd(&local_user_time, &thread_user_time, &local_user_time);
- timeradd(&local_system_time, &thread_system_time, &local_system_time);
- }
- *user_time = local_user_time;
- *system_time = local_system_time;
- return true;
- }
- const std::vector<ProcessReaderLinux::Thread>& ProcessReaderLinux::Threads() {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- if (!initialized_threads_) {
- InitializeThreads();
- }
- return threads_;
- }
- const std::vector<ProcessReaderLinux::Module>& ProcessReaderLinux::Modules() {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- if (!initialized_modules_) {
- InitializeModules();
- }
- return modules_;
- }
- void ProcessReaderLinux::InitializeAbortMessage() {
- #if BUILDFLAG(IS_ANDROID)
- const MemoryMap::Mapping* mapping =
- memory_map_.FindMappingWithName("[anon:abort message]");
- if (!mapping) {
- return;
- }
- if (is_64_bit_) {
- ReadAbortMessage<true>(mapping);
- } else {
- ReadAbortMessage<false>(mapping);
- }
- #endif
- }
- #if BUILDFLAG(IS_ANDROID)
- // These structure definitions and the magic numbers below were copied from
- // bionic/libc/bionic/android_set_abort_message.cpp
- template <bool is64Bit>
- struct abort_msg_t {
- uint32_t size;
- char msg[0];
- };
- template <>
- struct abort_msg_t<true> {
- uint64_t size;
- char msg[0];
- };
- template <bool is64Bit>
- struct magic_abort_msg_t {
- uint64_t magic1;
- uint64_t magic2;
- abort_msg_t<is64Bit> msg;
- };
- template <bool is64Bit>
- void ProcessReaderLinux::ReadAbortMessage(const MemoryMap::Mapping* mapping) {
- magic_abort_msg_t<is64Bit> header;
- if (!Memory()->Read(
- mapping->range.Base(), sizeof(magic_abort_msg_t<is64Bit>), &header)) {
- return;
- }
- size_t size = header.msg.size - sizeof(magic_abort_msg_t<is64Bit>) - 1;
- if (header.magic1 != 0xb18e40886ac388f0ULL ||
- header.magic2 != 0xc6dfba755a1de0b5ULL ||
- mapping->range.Size() <
- offsetof(magic_abort_msg_t<is64Bit>, msg.msg) + size) {
- return;
- }
- abort_message_.resize(size);
- if (!Memory()->Read(
- mapping->range.Base() + offsetof(magic_abort_msg_t<is64Bit>, msg.msg),
- size,
- &abort_message_[0])) {
- abort_message_.clear();
- }
- }
- #endif // BUILDFLAG(IS_ANDROID)
- const std::string& ProcessReaderLinux::AbortMessage() {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- if (abort_message_.empty()) {
- InitializeAbortMessage();
- }
- return abort_message_;
- }
- void ProcessReaderLinux::InitializeThreads() {
- DCHECK(threads_.empty());
- initialized_threads_ = true;
- pid_t pid = ProcessID();
- if (pid == getpid()) {
- // TODO(jperaza): ptrace can't be used on threads in the same thread group.
- // Using clone to create a new thread in it's own thread group doesn't work
- // because glibc doesn't support threads it didn't create via pthreads.
- // Fork a new process to snapshot us and copy the data back?
- LOG(ERROR) << "not implemented";
- return;
- }
- Thread main_thread;
- main_thread.tid = pid;
- if (main_thread.InitializePtrace(connection_)) {
- main_thread.InitializeStack(this);
- threads_.push_back(main_thread);
- } else {
- LOG(WARNING) << "Couldn't initialize main thread.";
- }
- bool main_thread_found = false;
- std::vector<pid_t> thread_ids;
- bool result = connection_->Threads(&thread_ids);
- DCHECK(result);
- for (pid_t tid : thread_ids) {
- if (tid == pid) {
- DCHECK(!main_thread_found);
- main_thread_found = true;
- continue;
- }
- Thread thread;
- thread.tid = tid;
- if (connection_->Attach(tid) && thread.InitializePtrace(connection_)) {
- thread.InitializeStack(this);
- threads_.push_back(thread);
- }
- }
- DCHECK(main_thread_found);
- }
- void ProcessReaderLinux::InitializeModules() {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- initialized_modules_ = true;
- AuxiliaryVector aux;
- if (!aux.Initialize(connection_)) {
- return;
- }
- LinuxVMAddress phdrs;
- if (!aux.GetValue(AT_PHDR, &phdrs)) {
- return;
- }
- ProcessMemoryRange range;
- if (!range.Initialize(Memory(), is_64_bit_)) {
- return;
- }
- // The strategy used for identifying loaded modules depends on ELF files
- // conventionally loading their header and program headers into memory.
- // Locating the correct module could fail if the headers aren't mapped, are
- // mapped at an unexpected location, or if there are other mappings
- // constructed to look like the ELF module being searched for.
- const MemoryMap::Mapping* exe_mapping = nullptr;
- std::unique_ptr<ElfImageReader> exe_reader;
- {
- const MemoryMap::Mapping* phdr_mapping = memory_map_.FindMapping(phdrs);
- if (!phdr_mapping) {
- return;
- }
- auto possible_mappings =
- memory_map_.FindFilePossibleMmapStarts(*phdr_mapping);
- const MemoryMap::Mapping* mapping = nullptr;
- while ((mapping = possible_mappings->Next())) {
- auto parsed_exe = std::make_unique<ElfImageReader>();
- if (parsed_exe->Initialize(
- range,
- mapping->range.Base(),
- /* verbose= */ possible_mappings->Count() == 0) &&
- parsed_exe->GetProgramHeaderTableAddress() == phdrs) {
- exe_mapping = mapping;
- exe_reader = std::move(parsed_exe);
- break;
- }
- }
- if (!exe_mapping) {
- LOG(ERROR) << "no exe mappings 0x" << std::hex
- << phdr_mapping->range.Base();
- return;
- }
- }
- LinuxVMAddress debug_address;
- if (!exe_reader->GetDebugAddress(&debug_address)) {
- return;
- }
- DebugRendezvous debug;
- if (!debug.Initialize(range, debug_address)) {
- return;
- }
- Module exe = {};
- exe.name = !debug.Executable()->name.empty() ? debug.Executable()->name
- : exe_mapping->name;
- exe.elf_reader = exe_reader.get();
- exe.type = ModuleSnapshot::ModuleType::kModuleTypeExecutable;
- modules_.push_back(exe);
- elf_readers_.push_back(std::move(exe_reader));
- LinuxVMAddress loader_base = 0;
- aux.GetValue(AT_BASE, &loader_base);
- for (const DebugRendezvous::LinkEntry& entry : debug.Modules()) {
- const MemoryMap::Mapping* module_mapping = nullptr;
- std::unique_ptr<ElfImageReader> elf_reader;
- {
- const MemoryMap::Mapping* dyn_mapping =
- memory_map_.FindMapping(entry.dynamic_array);
- if (!dyn_mapping) {
- continue;
- }
- #if BUILDFLAG(IS_ANDROID)
- // Beginning at API 21, Bionic provides android_dlopen_ext() which allows
- // passing a file descriptor with an existing relro segment to the loader.
- // This means that the mapping attributes of dyn_mapping may be unrelated
- // to the attributes of the other mappings for the module. In this case,
- // search all mappings in reverse order from dyn_mapping until a module is
- // parsed whose dynamic address matches the value in the debug link.
- static int api_level = android_get_device_api_level();
- auto possible_mappings =
- (api_level >= 21 || api_level < 0)
- ? memory_map_.ReverseIteratorFrom(*dyn_mapping)
- : memory_map_.FindFilePossibleMmapStarts(*dyn_mapping);
- #else
- auto possible_mappings =
- memory_map_.FindFilePossibleMmapStarts(*dyn_mapping);
- #endif
- const MemoryMap::Mapping* mapping = nullptr;
- while ((mapping = possible_mappings->Next())) {
- auto parsed_module = std::make_unique<ElfImageReader>();
- VMAddress dynamic_address;
- if (parsed_module->Initialize(
- range,
- mapping->range.Base(),
- /* verbose= */ possible_mappings->Count() == 0) &&
- parsed_module->GetDynamicArrayAddress(&dynamic_address) &&
- dynamic_address == entry.dynamic_array) {
- module_mapping = mapping;
- elf_reader = std::move(parsed_module);
- break;
- }
- }
- if (!module_mapping) {
- LOG(ERROR) << "no module mappings 0x" << std::hex
- << dyn_mapping->range.Base();
- continue;
- }
- }
- Module module = {};
- std::string soname;
- if (elf_reader->SoName(&soname) && !soname.empty()) {
- module.name = soname;
- } else {
- module.name = !entry.name.empty() ? entry.name : module_mapping->name;
- }
- module.elf_reader = elf_reader.get();
- module.type = loader_base && elf_reader->Address() == loader_base
- ? ModuleSnapshot::kModuleTypeDynamicLoader
- : ModuleSnapshot::kModuleTypeSharedLibrary;
- modules_.push_back(module);
- elf_readers_.push_back(std::move(elf_reader));
- }
- }
- } // namespace crashpad
|