// 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