// Copyright 2014 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 "util/process/process_memory_mac.h" #include #include #include #include "base/logging.h" #include "base/mac/mach_logging.h" #include "base/strings/stringprintf.h" #include "util/stdlib/strnlen.h" namespace crashpad { ProcessMemoryMac::MappedMemory::~MappedMemory() {} bool ProcessMemoryMac::MappedMemory::ReadCString(size_t offset, std::string* string) const { if (offset >= user_size_) { LOG(WARNING) << "offset out of range"; return false; } const char* string_base = reinterpret_cast(data_) + offset; size_t max_length = user_size_ - offset; size_t string_length = strnlen(string_base, max_length); if (string_length == max_length) { LOG(WARNING) << "unterminated string"; return false; } string->assign(string_base, string_length); return true; } ProcessMemoryMac::MappedMemory::MappedMemory(vm_address_t vm_address, size_t vm_size, size_t user_offset, size_t user_size) : vm_(vm_address, vm_size), data_(reinterpret_cast(vm_address + user_offset)), user_size_(user_size) { vm_address_t vm_end = vm_address + vm_size; vm_address_t user_address = reinterpret_cast(data_); vm_address_t user_end = user_address + user_size; DCHECK_GE(user_address, vm_address); DCHECK_LE(user_address, vm_end); DCHECK_GE(user_end, vm_address); DCHECK_LE(user_end, vm_end); } ProcessMemoryMac::ProcessMemoryMac() : task_(TASK_NULL), initialized_() {} bool ProcessMemoryMac::Initialize(task_t task) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); task_ = task; INITIALIZATION_STATE_SET_VALID(initialized_); return true; } std::unique_ptr ProcessMemoryMac::ReadMapped( mach_vm_address_t address, size_t size) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); if (size == 0) { return std::unique_ptr(new MappedMemory(0, 0, 0, 0)); } mach_vm_address_t region_address = mach_vm_trunc_page(address); mach_vm_size_t region_size = mach_vm_round_page(address - region_address + size); vm_offset_t region; mach_msg_type_number_t region_count; kern_return_t kr = mach_vm_read(task_, region_address, region_size, ®ion, ®ion_count); if (kr != KERN_SUCCESS) { MACH_LOG(WARNING, kr) << base::StringPrintf( "mach_vm_read(0x%llx, 0x%llx)", region_address, region_size); return std::unique_ptr(); } if (region_count != region_size) { LOG(ERROR) << base::StringPrintf( "mach_vm_read() unexpected read: 0x%x != 0x%llx bytes", region_count, region_size); if (region_count) vm_deallocate(mach_task_self(), region, region_count); return std::unique_ptr(); } return std::unique_ptr( new MappedMemory(region, region_size, address - region_address, size)); } ssize_t ProcessMemoryMac::ReadUpTo(VMAddress address, size_t size, void* buffer) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); DCHECK_LE(size, (size_t)std::numeric_limits::max()); std::unique_ptr memory = ReadMapped(address, size); if (!memory) { // If we can not read the entire mapping, try to perform a short read of the // first page instead. This is necessary to support ReadCString(). size_t short_read = PAGE_SIZE - (address % PAGE_SIZE); if (short_read >= size) return -1; memory = ReadMapped(address, short_read); if (!memory) return -1; size = short_read; } memcpy(buffer, memory->data(), size); return static_cast(size); } } // namespace crashpad