| // Copyright 2018 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 "util/process/process_memory_win.h" |
| |
| #include <windows.h> |
| |
| #include <algorithm> |
| #include <limits> |
| |
| #include "base/check_op.h" |
| #include "base/logging.h" |
| #include "base/memory/page_size.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/strings/stringprintf.h" |
| |
| namespace crashpad { |
| |
| ProcessMemoryWin::ProcessMemoryWin() |
| : ProcessMemory(), handle_(), process_info_(), initialized_() {} |
| |
| ProcessMemoryWin::~ProcessMemoryWin() {} |
| |
| bool ProcessMemoryWin::Initialize(HANDLE handle) { |
| INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
| |
| handle_ = handle; |
| if (!process_info_.Initialize(handle)) { |
| LOG(ERROR) << "Failed to initialize ProcessInfo."; |
| return false; |
| } |
| |
| INITIALIZATION_STATE_SET_VALID(initialized_); |
| return true; |
| } |
| |
| ssize_t ProcessMemoryWin::ReadUpTo(VMAddress address, |
| size_t size, |
| void* buffer) const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| DCHECK_LE(size, (size_t)std::numeric_limits<ssize_t>::max()); |
| |
| SIZE_T size_out = 0; |
| BOOL success = ReadProcessMemory( |
| handle_, reinterpret_cast<void*>(address), buffer, size, &size_out); |
| if (success) |
| return base::checked_cast<ssize_t>(size_out); |
| |
| if (GetLastError() == ERROR_PARTIAL_COPY) { |
| // If we can not read the entire section, perform a short read of the first |
| // page instead. This is necessary to support ReadCString(). |
| size_t short_read = |
| base::GetPageSize() - (address & (base::GetPageSize() - 1)); |
| success = ReadProcessMemory(handle_, |
| reinterpret_cast<void*>(address), |
| buffer, |
| short_read, |
| &size_out); |
| if (success) |
| return base::checked_cast<ssize_t>(size_out); |
| } |
| |
| PLOG(ERROR) << "ReadMemory at 0x" << std::hex << address << std::dec << " of " |
| << size << " bytes failed"; |
| return -1; |
| } |
| |
| size_t ProcessMemoryWin::ReadAvailableMemory(VMAddress address, |
| size_t size, |
| void* buffer) const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| DCHECK_LE(size, (size_t)std::numeric_limits<ssize_t>::max()); |
| |
| if (size == 0) |
| return 0; |
| |
| auto ranges = process_info_.GetReadableRanges( |
| CheckedRange<WinVMAddress, WinVMSize>(address, size)); |
| |
| // We only read up until the first unavailable byte, so we only read from the |
| // first range. If we have no ranges, then no bytes were accessible anywhere |
| // in the range. |
| if (ranges.empty()) { |
| LOG(ERROR) << base::StringPrintf( |
| "range at 0x%llx, size 0x%zx completely inaccessible", address, size); |
| return 0; |
| } |
| |
| // If the start address was adjusted, we couldn't read even the first |
| // requested byte. |
| if (ranges.front().base() != address) { |
| LOG(ERROR) << base::StringPrintf( |
| "start of range at 0x%llx, size 0x%zx inaccessible", address, size); |
| return 0; |
| } |
| |
| DCHECK_LE(ranges.front().size(), size); |
| |
| ssize_t result = ReadUpTo(ranges.front().base(), |
| base::checked_cast<size_t>(ranges.front().size()), |
| buffer); |
| if (result < 0) |
| return 0; |
| |
| return base::checked_cast<size_t>(result); |
| } |
| |
| } // namespace crashpad |