// Copyright 2017 The Crashpad Authors // // 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/exception_snapshot_linux.h" #include #include "base/logging.h" #include "snapshot/linux/capture_memory_delegate_linux.h" #include "snapshot/linux/cpu_context_linux.h" #include "snapshot/linux/process_reader_linux.h" #include "snapshot/linux/signal_context.h" #include "util/linux/traits.h" #include "util/misc/reinterpret_bytes.h" #include "util/numeric/safe_assignment.h" #include "util/posix/signals.h" namespace crashpad { namespace internal { ExceptionSnapshotLinux::ExceptionSnapshotLinux() : ExceptionSnapshot(), context_union_(), context_(), codes_(), thread_id_(0), exception_address_(0), signal_number_(0), signal_code_(0), initialized_() {} ExceptionSnapshotLinux::~ExceptionSnapshotLinux() {} #if defined(ARCH_CPU_X86_FAMILY) template <> bool ExceptionSnapshotLinux::ReadContext( ProcessReaderLinux* reader, LinuxVMAddress context_address) { UContext ucontext; if (!reader->Memory()->Read(context_address, sizeof(ucontext), &ucontext)) { LOG(ERROR) << "Couldn't read ucontext"; return false; } context_.architecture = kCPUArchitectureX86; context_.x86 = &context_union_.x86; if (!ucontext.mcontext.fpptr) { InitializeCPUContextX86_NoFloatingPoint(ucontext.mcontext.gprs, context_.x86); return true; } SignalFloatContext32 fprs; if (!reader->Memory()->Read(ucontext.mcontext.fpptr, sizeof(fprs), &fprs)) { LOG(ERROR) << "Couldn't read float context"; return false; } if (fprs.magic == X86_FXSR_MAGIC) { InitializeCPUContextX86_NoFloatingPoint(ucontext.mcontext.gprs, context_.x86); if (!reader->Memory()->Read( ucontext.mcontext.fpptr + offsetof(SignalFloatContext32, fxsave), sizeof(CPUContextX86::Fxsave), &context_.x86->fxsave)) { LOG(ERROR) << "Couldn't read fxsave"; return false; } } else if (fprs.magic == 0xffff) { InitializeCPUContextX86(ucontext.mcontext.gprs, fprs, context_.x86); } else { LOG(ERROR) << "unexpected magic 0x" << std::hex << fprs.magic; return false; } return true; } template <> bool ExceptionSnapshotLinux::ReadContext( ProcessReaderLinux* reader, LinuxVMAddress context_address) { UContext ucontext; if (!reader->Memory()->Read(context_address, sizeof(ucontext), &ucontext)) { LOG(ERROR) << "Couldn't read ucontext"; return false; } context_.architecture = kCPUArchitectureX86_64; context_.x86_64 = &context_union_.x86_64; if (!ucontext.mcontext.fpptr) { InitializeCPUContextX86_64_NoFloatingPoint(ucontext.mcontext.gprs, context_.x86_64); return true; } SignalFloatContext64 fprs; if (!reader->Memory()->Read(ucontext.mcontext.fpptr, sizeof(fprs), &fprs)) { LOG(ERROR) << "Couldn't read float context"; return false; } InitializeCPUContextX86_64(ucontext.mcontext.gprs, fprs, context_.x86_64); return true; } #elif defined(ARCH_CPU_ARM_FAMILY) template <> bool ExceptionSnapshotLinux::ReadContext( ProcessReaderLinux* reader, LinuxVMAddress context_address) { context_.architecture = kCPUArchitectureARM; context_.arm = &context_union_.arm; CPUContextARM* dest_context = context_.arm; const ProcessMemory* memory = reader->Memory(); LinuxVMAddress gprs_address = context_address + offsetof(UContext, mcontext32) + offsetof(ContextTraits32::MContext32, gprs); SignalThreadContext32 thread_context; if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) { LOG(ERROR) << "Couldn't read gprs"; return false; } InitializeCPUContextARM_NoFloatingPoint(thread_context, dest_context); LinuxVMAddress reserved_address = context_address + offsetof(UContext, reserved); if ((reserved_address & 7) != 0) { LOG(ERROR) << "invalid alignment 0x" << std::hex << reserved_address; return false; } constexpr VMSize kMaxContextSpace = 1024; ProcessMemoryRange range; if (!range.Initialize(memory, false, reserved_address, kMaxContextSpace)) { return false; } do { CoprocessorContextHead head; if (!range.Read(reserved_address, sizeof(head), &head)) { LOG(ERROR) << "missing context terminator"; return false; } reserved_address += sizeof(head); switch (head.magic) { case VFP_MAGIC: if (head.size != sizeof(SignalVFPContext) + sizeof(head)) { LOG(ERROR) << "unexpected vfp context size " << head.size; return false; } static_assert( sizeof(SignalVFPContext::vfp) == sizeof(dest_context->vfp_regs), "vfp context size mismatch"); if (!range.Read(reserved_address + offsetof(SignalVFPContext, vfp), sizeof(dest_context->vfp_regs), &dest_context->vfp_regs)) { LOG(ERROR) << "Couldn't read vfp"; return false; } dest_context->have_vfp_regs = true; return true; case CRUNCH_MAGIC: case IWMMXT_MAGIC: case DUMMY_MAGIC: reserved_address += head.size - sizeof(head); continue; case 0: return true; default: LOG(ERROR) << "invalid magic number 0x" << std::hex << head.magic; return false; } } while (true); } template <> bool ExceptionSnapshotLinux::ReadContext( ProcessReaderLinux* reader, LinuxVMAddress context_address) { context_.architecture = kCPUArchitectureARM64; context_.arm64 = &context_union_.arm64; CPUContextARM64* dest_context = context_.arm64; const ProcessMemory* memory = reader->Memory(); LinuxVMAddress gprs_address = context_address + offsetof(UContext, mcontext64) + offsetof(ContextTraits64::MContext64, gprs); ThreadContext::t64_t thread_context; if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) { LOG(ERROR) << "Couldn't read gprs"; return false; } InitializeCPUContextARM64_NoFloatingPoint(thread_context, dest_context); LinuxVMAddress reserved_address = context_address + offsetof(UContext, reserved); if ((reserved_address & 15) != 0) { LOG(ERROR) << "invalid alignment 0x" << std::hex << reserved_address; return false; } constexpr VMSize kMaxContextSpace = 4096; ProcessMemoryRange range; if (!range.Initialize(memory, true, reserved_address, kMaxContextSpace)) { return false; } do { CoprocessorContextHead head; if (!range.Read(reserved_address, sizeof(head), &head)) { LOG(ERROR) << "missing context terminator"; return false; } reserved_address += sizeof(head); switch (head.magic) { case FPSIMD_MAGIC: if (head.size != sizeof(SignalFPSIMDContext) + sizeof(head)) { LOG(ERROR) << "unexpected fpsimd context size " << head.size; return false; } SignalFPSIMDContext fpsimd; if (!range.Read(reserved_address, sizeof(fpsimd), &fpsimd)) { LOG(ERROR) << "Couldn't read fpsimd " << head.size; return false; } InitializeCPUContextARM64_OnlyFPSIMD(fpsimd, dest_context); return true; case ESR_MAGIC: case EXTRA_MAGIC: reserved_address += head.size - sizeof(head); continue; case 0: LOG(WARNING) << "fpsimd not found"; return true; default: LOG(ERROR) << "invalid magic number 0x" << std::hex << head.magic; return false; } } while (true); } #elif defined(ARCH_CPU_MIPS_FAMILY) template static bool ReadContext(ProcessReaderLinux* reader, LinuxVMAddress context_address, typename Traits::CPUContext* dest_context) { const ProcessMemory* memory = reader->Memory(); LinuxVMAddress gregs_address = context_address + offsetof(UContext, mcontext) + offsetof(typename Traits::MContext, gregs); typename Traits::SignalThreadContext thread_context; if (!memory->Read(gregs_address, sizeof(thread_context), &thread_context)) { LOG(ERROR) << "Couldn't read gregs"; return false; } LinuxVMAddress fpregs_address = context_address + offsetof(UContext, mcontext) + offsetof(typename Traits::MContext, fpregs); typename Traits::SignalFloatContext fp_context; if (!memory->Read(fpregs_address, sizeof(fp_context), &fp_context)) { LOG(ERROR) << "Couldn't read fpregs"; return false; } InitializeCPUContextMIPS(thread_context, fp_context, dest_context); return true; } template <> bool ExceptionSnapshotLinux::ReadContext( ProcessReaderLinux* reader, LinuxVMAddress context_address) { context_.architecture = kCPUArchitectureMIPSEL; context_.mipsel = &context_union_.mipsel; return internal::ReadContext( reader, context_address, context_.mipsel); } template <> bool ExceptionSnapshotLinux::ReadContext( ProcessReaderLinux* reader, LinuxVMAddress context_address) { context_.architecture = kCPUArchitectureMIPS64EL; context_.mips64 = &context_union_.mips64; return internal::ReadContext( reader, context_address, context_.mips64); } #endif // ARCH_CPU_X86_FAMILY bool ExceptionSnapshotLinux::Initialize( ProcessReaderLinux* process_reader, LinuxVMAddress siginfo_address, LinuxVMAddress context_address, pid_t thread_id, uint32_t* gather_indirectly_referenced_memory_cap) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); thread_id_ = thread_id; const ProcessReaderLinux::Thread* thread = nullptr; for (const auto& loop_thread : process_reader->Threads()) { if (thread_id == loop_thread.tid) { thread = &loop_thread; break; } } if (!thread) { // This is allowed until {ProcessReaderLinux::InitializeThreads()} is // improved to support target threads in the same thread group. LOG(WARNING) << "thread ID " << thread_id << " not found in process"; } if (process_reader->Is64Bit()) { if (!ReadContext(process_reader, context_address) || !ReadSiginfo(process_reader, siginfo_address)) { return false; } } else { if (!ReadContext(process_reader, context_address) || !ReadSiginfo(process_reader, siginfo_address)) { return false; } } CaptureMemoryDelegateLinux capture_memory_delegate( process_reader, thread, &extra_memory_, gather_indirectly_referenced_memory_cap); CaptureMemory::PointedToByContext(context_, &capture_memory_delegate); INITIALIZATION_STATE_SET_VALID(initialized_); return true; } template bool ExceptionSnapshotLinux::ReadSiginfo(ProcessReaderLinux* reader, LinuxVMAddress siginfo_address) { Siginfo siginfo; if (!reader->Memory()->Read(siginfo_address, sizeof(siginfo), &siginfo)) { LOG(ERROR) << "Couldn't read siginfo"; return false; } signal_number_ = siginfo.signo; signal_code_ = siginfo.code; uint64_t extra_code; #define PUSH_CODE(value) \ do { \ if (!ReinterpretBytes(value, &extra_code)) { \ LOG(ERROR) << "bad code"; \ return false; \ } \ codes_.push_back(extra_code); \ } while (false) switch (siginfo.signo) { case SIGILL: case SIGFPE: case SIGSEGV: case SIGBUS: case SIGTRAP: exception_address_ = siginfo.address; break; case SIGPOLL: // SIGIO PUSH_CODE(siginfo.band); PUSH_CODE(siginfo.fd); break; case SIGSYS: exception_address_ = siginfo.call_address; PUSH_CODE(siginfo.syscall); PUSH_CODE(siginfo.arch); break; case SIGALRM: case SIGVTALRM: case SIGPROF: PUSH_CODE(siginfo.timerid); PUSH_CODE(siginfo.overrun); PUSH_CODE(siginfo.sigval.sigval); break; case SIGABRT: case SIGQUIT: case SIGXCPU: case SIGXFSZ: case SIGHUP: case SIGINT: case SIGPIPE: case SIGTERM: case SIGUSR1: case SIGUSR2: #if defined(SIGEMT) case SIGEMT: #endif // SIGEMT #if defined(SIGPWR) case SIGPWR: #endif // SIGPWR #if defined(SIGSTKFLT) case SIGSTKFLT: #endif // SIGSTKFLT PUSH_CODE(siginfo.pid); PUSH_CODE(siginfo.uid); PUSH_CODE(siginfo.sigval.sigval); break; case Signals::kSimulatedSigno: break; default: LOG(WARNING) << "Unhandled signal " << siginfo.signo; } return true; } const CPUContext* ExceptionSnapshotLinux::Context() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return &context_; } uint64_t ExceptionSnapshotLinux::ThreadID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return thread_id_; } uint32_t ExceptionSnapshotLinux::Exception() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return signal_number_; } uint32_t ExceptionSnapshotLinux::ExceptionInfo() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return signal_code_; } uint64_t ExceptionSnapshotLinux::ExceptionAddress() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return exception_address_; } const std::vector& ExceptionSnapshotLinux::Codes() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return codes_; } std::vector ExceptionSnapshotLinux::ExtraMemory() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::vector result; result.reserve(extra_memory_.size()); for (const auto& em : extra_memory_) { result.push_back(em.get()); } return result; } } // namespace internal } // namespace crashpad