Add ProcessMemoryWin and re-factor tests

Currently, ProcessMemory is only implemented for Linux and Fuchsia.
Implement the interface for Windows as well and re-factor tests to
support it, mostly this consists of using a new ScopedGuardedPage class
instead of ScopedMmap in the ProcessMemory tests.

BUG=crashpad:262

Change-Id: I1b42718972be5ad838d12356d09f764053f09e4f
Reviewed-on: https://chromium-review.googlesource.com/c/1278829
Commit-Queue: Vlad Tsyrklevich <vtsyrklevich@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Vlad Tsyrklevich 2018-11-05 13:14:20 -08:00 committed by Commit Bot
parent 131dd81d4c
commit a9be1b1403
14 changed files with 434 additions and 127 deletions

View File

@ -36,6 +36,7 @@ static_library("test") {
"multiprocess_exec.h",
"process_type.cc",
"process_type.h",
"scoped_guarded_page.h",
"scoped_module_handle.cc",
"scoped_module_handle.h",
"scoped_temp_dir.cc",
@ -45,7 +46,10 @@ static_library("test") {
]
if (crashpad_is_posix || crashpad_is_fuchsia) {
sources += [ "scoped_temp_dir_posix.cc" ]
sources += [
"scoped_guarded_page_posix.cc",
"scoped_temp_dir_posix.cc",
]
if (!crashpad_is_fuchsia) {
sources += [
@ -81,6 +85,7 @@ static_library("test") {
if (crashpad_is_win) {
sources += [
"multiprocess_exec_win.cc",
"scoped_guarded_page_win.cc",
"scoped_temp_dir_win.cc",
"win/child_launcher.cc",
"win/child_launcher.h",
@ -144,6 +149,7 @@ source_set("test_test") {
"hex_string_test.cc",
"main_arguments_test.cc",
"multiprocess_exec_test.cc",
"scoped_guarded_page_test.cc",
"scoped_temp_dir_test.cc",
"test_paths_test.cc",
]

View File

@ -0,0 +1,49 @@
// Copyright 2018 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.
#ifndef CRASHPAD_TEST_SCOPED_GUARDED_PAGE_
#define CRASHPAD_TEST_SCOPED_GUARDED_PAGE_
#include "base/macros.h"
namespace crashpad {
namespace test {
//! \brief A RAII object that allocates a read-write page with an inacessible
//! page following it.
//!
//! Upon construction, a mapping will be created. Failure to create the mapping
//! is fatal. On destruction, the mapping is freed.
//!
//! This object should not be used in multi-threded contexts, the POSIX
//! implementation can not be made thread-safe.
class ScopedGuardedPage {
public:
ScopedGuardedPage();
~ScopedGuardedPage();
//! \brief Returns the address of the read-write page.
//!
//! \return The address of the read-write page.
void* Pointer() const { return ptr_; }
private:
void* ptr_;
DISALLOW_COPY_AND_ASSIGN(ScopedGuardedPage);
};
} // namespace test
} // namespace crashpad
#endif // CRASHPAD_TEST_SCOPED_GUARDED_PAGE_

View File

@ -0,0 +1,47 @@
// Copyright 2018 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 "test/scoped_guarded_page.h"
#include <sys/mman.h>
#include "base/logging.h"
#include "base/process/process_metrics.h"
namespace crashpad {
namespace test {
ScopedGuardedPage::ScopedGuardedPage() {
ptr_ = mmap(nullptr,
base::GetPageSize() * 2,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0);
PCHECK(ptr_ != MAP_FAILED) << "mmap";
// Simply mprotect()ing the guard page PROT_NONE does not make it inaccessible
// using ptrace() or /proc/$pid/mem so we munmap() the following page instead.
// Unfortunately, this means that the guarded page is not thread safe from
// other threads mapping a single page into the empty region.
char* second_page = static_cast<char*>(ptr_) + base::GetPageSize();
PCHECK(munmap(second_page, base::GetPageSize()) >= 0) << "munmap";
}
ScopedGuardedPage::~ScopedGuardedPage() {
PCHECK(munmap(ptr_, base::GetPageSize()) >= 0) << "munmap";
}
} // namespace test
} // namespace crashpad

View File

@ -0,0 +1,35 @@
// Copyright 2018 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 "test/scoped_guarded_page.h"
#include "base/process/process_metrics.h"
#include "gtest/gtest.h"
namespace crashpad {
namespace test {
namespace {
TEST(ScopedGuardedPage, BasicFunctionality) {
ScopedGuardedPage page;
char* address = (char*)page.Pointer();
EXPECT_NE(address, nullptr);
address[0] = 0;
address[base::GetPageSize() - 1] = 0;
EXPECT_DEATH({ address[base::GetPageSize()] = 0; }, "");
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -0,0 +1,39 @@
// Copyright 2018 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 "test/scoped_guarded_page.h"
#include <windows.h>
#include "base/logging.h"
#include "base/process/process_metrics.h"
namespace crashpad {
namespace test {
ScopedGuardedPage::ScopedGuardedPage() {
const size_t page_size = base::GetPageSize();
ptr_ = VirtualAlloc(nullptr, page_size * 2, MEM_RESERVE, PAGE_NOACCESS);
PCHECK(ptr_ != nullptr) << "VirtualAlloc";
PCHECK(VirtualAlloc(ptr_, page_size, MEM_COMMIT, PAGE_READWRITE) != nullptr)
<< "VirtualAlloc";
}
ScopedGuardedPage::~ScopedGuardedPage() {
PCHECK(VirtualFree(ptr_, 0, MEM_RELEASE)) << "VirtualFree";
}
} // namespace test
} // namespace crashpad

View File

@ -311,7 +311,8 @@ static_library("util") {
]
}
if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia ||
crashpad_is_win) {
sources += [
"process/process_memory.cc",
"process/process_memory.h",
@ -332,6 +333,8 @@ static_library("util") {
"misc/paths_win.cc",
"misc/time_win.cc",
"net/http_transport_win.cc",
"process/process_memory_win.cc",
"process/process_memory_win.h",
"synchronization/semaphore_win.cc",
"thread/thread_win.cc",
"win/address_types.h",
@ -622,7 +625,8 @@ source_set("util_test") {
sources += [ "misc/capture_context_test_util_fuchsia.cc" ]
}
if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia ||
crashpad_is_win) {
sources += [
# TODO: Port to all platforms.
"process/process_memory_range_test.cc",

View File

@ -14,6 +14,8 @@
#include "util/process/process_memory.h"
#include <algorithm>
#include "base/logging.h"
namespace crashpad {

View File

@ -19,8 +19,14 @@
#include <string>
#include "build/build_config.h"
#include "util/misc/address_types.h"
#if defined(OS_WIN)
#include <basetsd.h>
typedef SSIZE_T ssize_t;
#endif // defined(OS_WIN)
namespace crashpad {
//! \brief Abstract base class for accessing the memory of another process.

View File

@ -18,6 +18,8 @@
#include "util/process/process_memory_fuchsia.h"
#elif defined(OS_LINUX) || defined(OS_ANDROID)
#include "util/process/process_memory_linux.h"
#elif defined(OS_WIN)
#include "util/process/process_memory_win.h"
#endif
namespace crashpad {
@ -27,6 +29,8 @@ namespace crashpad {
using ProcessMemoryNative = ProcessMemoryFuchsia;
#elif defined(OS_LINUX) || defined(OS_ANDROID)
using ProcessMemoryNative = ProcessMemoryLinux;
#elif defined(OS_WIN)
using ProcessMemoryNative = ProcessMemoryWin;
#else
#error Port.
#endif

View File

@ -86,7 +86,7 @@ bool ProcessMemoryRange::ReadCStringSizeLimited(VMAddress address,
LOG(ERROR) << "read out of range";
return false;
}
size = std::min(static_cast<VMSize>(size), range_.End() - address);
size = std::min(size, base::checked_cast<size_t>(range_.End() - address));
return memory_->ReadCStringSizeLimited(address, size, string);
}

View File

@ -14,8 +14,6 @@
#include "util/process/process_memory_range.h"
#include <unistd.h>
#include <limits>
#include "base/logging.h"
@ -27,7 +25,11 @@
#include <lib/zx/process.h>
#include "util/process/process_memory_fuchsia.h"
#elif defined(OS_WIN)
#include "util/process/process_memory_win.h"
#else
#include <unistd.h>
#include "util/process/process_memory_linux.h"
#endif
@ -41,20 +43,21 @@ struct TestObject {
} kTestObject = {"string1", "string2"};
TEST(ProcessMemoryRange, Basic) {
#if defined(OS_FUCHSIA)
ProcessMemoryFuchsia memory;
ASSERT_TRUE(memory.Initialize(*zx::process::self()));
constexpr bool is_64_bit = true;
#else
pid_t pid = getpid();
#if defined(ARCH_CPU_64_BITS)
constexpr bool is_64_bit = true;
#else
constexpr bool is_64_bit = false;
#endif // ARCH_CPU_64_BITS
#if defined(OS_FUCHSIA)
ProcessMemoryFuchsia memory;
ASSERT_TRUE(memory.Initialize(*zx::process::self()));
#elif defined(OS_WIN)
ProcessMemoryWin memory;
ASSERT_TRUE(memory.Initialize(GetCurrentProcess()));
#elif defined(OS_LINUX)
ProcessMemoryLinux memory;
ASSERT_TRUE(memory.Initialize(pid));
ASSERT_TRUE(memory.Initialize(getpid()));
#endif // OS_FUCHSIA
ProcessMemoryRange range;

View File

@ -15,19 +15,18 @@
#include "util/process/process_memory.h"
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <memory>
#include "base/process/process_metrics.h"
#include "gtest/gtest.h"
#include "test/errors.h"
#include "test/multiprocess.h"
#include "test/multiprocess_exec.h"
#include "test/process_type.h"
#include "test/scoped_guarded_page.h"
#include "util/file/file_io.h"
#include "util/misc/from_pointer_cast.h"
#include "util/posix/scoped_mmap.h"
#include "util/process/process_memory_native.h"
namespace crashpad {
@ -36,7 +35,7 @@ namespace {
void DoChildReadTestSetup(size_t* region_size,
std::unique_ptr<char[]>* region) {
*region_size = 4 * getpagesize();
*region_size = 4 * base::GetPageSize();
region->reset(new char[*region_size]);
for (size_t index = 0; index < *region_size; ++index) {
(*region)[index] = index % 256;
@ -120,7 +119,7 @@ class ReadTest : public MultiprocessExec {
}
// Ensure that a read of exactly one page works.
size_t page_size = getpagesize();
size_t page_size = base::GetPageSize();
ASSERT_GE(region_size, page_size + page_size);
ASSERT_TRUE(memory.Read(address + page_size, page_size, result.get()));
for (size_t i = 0; i < page_size; ++i) {
@ -154,7 +153,7 @@ constexpr char kConstCharShort[] = "A short const char[]";
std::string MakeLongString() {
std::string long_string;
const size_t kStringLongSize = 4 * getpagesize();
const size_t kStringLongSize = 4 * base::GetPageSize();
for (size_t index = 0; index < kStringLongSize; ++index) {
long_string.push_back((index % 255) + 1);
}
@ -322,101 +321,60 @@ TEST(ProcessMemory, ReadCStringSizeLimitedChild) {
test.RunAgainstChild();
}
void DoReadUnmappedChildMainSetup(ScopedMmap* pages,
VMAddress* address,
size_t* page_size,
size_t* region_size) {
*page_size = getpagesize();
*region_size = 2 * (*page_size);
if (!pages->ResetMmap(nullptr,
*region_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0)) {
ADD_FAILURE();
return;
}
*address = pages->addr_as<VMAddress>();
char* region = pages->addr_as<char*>();
for (size_t index = 0; index < *region_size; ++index) {
void DoReadUnmappedChildMainSetup(void* page) {
char* region = reinterpret_cast<char*>(page);
for (size_t index = 0; index < base::GetPageSize(); ++index) {
region[index] = index % 256;
}
EXPECT_TRUE(pages->ResetAddrLen(region, *page_size));
}
CRASHPAD_CHILD_TEST_MAIN(ReadUnmappedChildMain) {
ScopedMmap pages;
VMAddress address = 0;
size_t page_size, region_size;
DoReadUnmappedChildMainSetup(&pages, &address, &page_size, &region_size);
ScopedGuardedPage pages;
VMAddress address = reinterpret_cast<VMAddress>(pages.Pointer());
DoReadUnmappedChildMainSetup(pages.Pointer());
FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
CheckedWriteFile(out, &address, sizeof(address));
CheckedWriteFile(out, &page_size, sizeof(page_size));
CheckedWriteFile(out, &region_size, sizeof(region_size));
CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
return 0;
}
// This test only supports running against a child process because
// ScopedGuardedPage is not thread-safe.
class ReadUnmappedTest : public MultiprocessExec {
public:
ReadUnmappedTest() : MultiprocessExec() {
SetChildTestMainFunction("ReadUnmappedChildMain");
}
void RunAgainstSelf() {
ScopedMmap pages;
VMAddress address = 0;
size_t page_size, region_size;
DoReadUnmappedChildMainSetup(&pages, &address, &page_size, &region_size);
DoTest(GetSelfProcess(), address, page_size, region_size);
}
void RunAgainstChild() { Run(); }
private:
void MultiprocessParent() override {
VMAddress address = 0;
size_t page_size, region_size;
ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &address, sizeof(address)));
ASSERT_TRUE(
ReadFileExactly(ReadPipeHandle(), &page_size, sizeof(page_size)));
ASSERT_TRUE(
ReadFileExactly(ReadPipeHandle(), &region_size, sizeof(region_size)));
DoTest(ChildProcess(), address, page_size, region_size);
DoTest(ChildProcess(), address);
}
void DoTest(ProcessType process,
VMAddress address,
size_t page_size,
size_t region_size) {
void DoTest(ProcessType process, VMAddress address) {
ProcessMemoryNative memory;
ASSERT_TRUE(memory.Initialize(process));
VMAddress page_addr1 = address;
VMAddress page_addr2 = page_addr1 + page_size;
VMAddress page_addr2 = page_addr1 + base::GetPageSize();
std::unique_ptr<char[]> result(new char[region_size]);
EXPECT_TRUE(memory.Read(page_addr1, page_size, result.get()));
std::unique_ptr<char[]> result(new char[base::GetPageSize() * 2]);
EXPECT_TRUE(memory.Read(page_addr1, base::GetPageSize(), result.get()));
EXPECT_TRUE(memory.Read(page_addr2 - 1, 1, result.get()));
EXPECT_FALSE(memory.Read(page_addr1, region_size, result.get()));
EXPECT_FALSE(memory.Read(page_addr2, page_size, result.get()));
EXPECT_FALSE(
memory.Read(page_addr1, base::GetPageSize() * 2, result.get()));
EXPECT_FALSE(memory.Read(page_addr2, base::GetPageSize(), result.get()));
EXPECT_FALSE(memory.Read(page_addr2 - 1, 2, result.get()));
}
DISALLOW_COPY_AND_ASSIGN(ReadUnmappedTest);
};
TEST(ProcessMemory, ReadUnmappedSelf) {
ReadUnmappedTest test;
ASSERT_FALSE(testing::Test::HasFailure());
test.RunAgainstSelf();
}
TEST(ProcessMemory, ReadUnmappedChild) {
ReadUnmappedTest test;
ASSERT_FALSE(testing::Test::HasFailure());
@ -428,9 +386,13 @@ constexpr size_t kChildProcessStringLength = 10;
class StringDataInChildProcess {
public:
// This constructor only makes sense in the child process.
explicit StringDataInChildProcess(const char* cstring)
explicit StringDataInChildProcess(const char* cstring, bool valid)
: address_(FromPointerCast<VMAddress>(cstring)) {
memcpy(expected_value_, cstring, kChildProcessStringLength + 1);
if (valid) {
memcpy(expected_value_, cstring, kChildProcessStringLength + 1);
} else {
memset(expected_value_, 0xff, kChildProcessStringLength + 1);
}
}
void Write(FileHandle out) {
@ -457,22 +419,10 @@ class StringDataInChildProcess {
};
void DoCStringUnmappedTestSetup(
ScopedMmap* pages,
void* page,
std::vector<StringDataInChildProcess>* strings) {
const size_t page_size = getpagesize();
const size_t region_size = 2 * page_size;
if (!pages->ResetMmap(nullptr,
region_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0)) {
ADD_FAILURE();
return;
}
char* region = pages->addr_as<char*>();
for (size_t index = 0; index < region_size; ++index) {
char* region = reinterpret_cast<char*>(page);
for (size_t index = 0; index < base::GetPageSize(); ++index) {
region[index] = 1 + index % 255;
}
@ -481,29 +431,25 @@ void DoCStringUnmappedTestSetup(
string1[kChildProcessStringLength] = '\0';
// A string near the end of the mapped region
char* string2 = region + page_size - kChildProcessStringLength * 2;
char* string2 = region + base::GetPageSize() - kChildProcessStringLength * 2;
string2[kChildProcessStringLength] = '\0';
// A string that crosses from the mapped into the unmapped region
char* string3 = region + page_size - kChildProcessStringLength + 1;
string3[kChildProcessStringLength] = '\0';
char* string3 = region + base::GetPageSize() - kChildProcessStringLength + 1;
// A string entirely in the unmapped region
char* string4 = region + page_size + 10;
string4[kChildProcessStringLength] = '\0';
char* string4 = region + base::GetPageSize() + 10;
strings->push_back(StringDataInChildProcess(string1));
strings->push_back(StringDataInChildProcess(string2));
strings->push_back(StringDataInChildProcess(string3));
strings->push_back(StringDataInChildProcess(string4));
EXPECT_TRUE(pages->ResetAddrLen(region, page_size));
strings->push_back(StringDataInChildProcess(string1, true));
strings->push_back(StringDataInChildProcess(string2, true));
strings->push_back(StringDataInChildProcess(string3, false));
strings->push_back(StringDataInChildProcess(string4, false));
}
CRASHPAD_CHILD_TEST_MAIN(ReadCStringUnmappedChildMain) {
ScopedMmap pages;
ScopedGuardedPage pages;
std::vector<StringDataInChildProcess> strings;
DoCStringUnmappedTestSetup(&pages, &strings);
DoCStringUnmappedTestSetup(pages.Pointer(), &strings);
FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
strings[0].Write(out);
strings[1].Write(out);
@ -513,6 +459,8 @@ CRASHPAD_CHILD_TEST_MAIN(ReadCStringUnmappedChildMain) {
return 0;
}
// This test only supports running against a child process because
// ScopedGuardedPage is not thread-safe.
class ReadCStringUnmappedTest : public MultiprocessExec {
public:
ReadCStringUnmappedTest(bool limit_size)
@ -520,13 +468,6 @@ class ReadCStringUnmappedTest : public MultiprocessExec {
SetChildTestMainFunction("ReadCStringUnmappedChildMain");
}
void RunAgainstSelf() {
ScopedMmap pages;
std::vector<StringDataInChildProcess> strings;
DoCStringUnmappedTestSetup(&pages, &strings);
DoTest(GetSelfProcess(), strings);
}
void RunAgainstChild() { Run(); }
private:
@ -536,8 +477,7 @@ class ReadCStringUnmappedTest : public MultiprocessExec {
strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
ASSERT_NO_FATAL_FAILURE();
DoTest(ChildProcess(), strings);
ASSERT_NO_FATAL_FAILURE(DoTest(ChildProcess(), strings));
}
void DoTest(ProcessType process,
@ -574,24 +514,12 @@ class ReadCStringUnmappedTest : public MultiprocessExec {
DISALLOW_COPY_AND_ASSIGN(ReadCStringUnmappedTest);
};
TEST(ProcessMemory, ReadCStringUnmappedSelf) {
ReadCStringUnmappedTest test(/* limit_size= */ false);
ASSERT_FALSE(testing::Test::HasFailure());
test.RunAgainstSelf();
}
TEST(ProcessMemory, ReadCStringUnmappedChild) {
ReadCStringUnmappedTest test(/* limit_size= */ false);
ASSERT_FALSE(testing::Test::HasFailure());
test.RunAgainstChild();
}
TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedSelf) {
ReadCStringUnmappedTest test(/* limit_size= */ true);
ASSERT_FALSE(testing::Test::HasFailure());
test.RunAgainstSelf();
}
TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedChild) {
ReadCStringUnmappedTest test(/* limit_size= */ true);
ASSERT_FALSE(testing::Test::HasFailure());

View File

@ -0,0 +1,118 @@
// Copyright 2018 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_win.h"
#include <windows.h>
#include <algorithm>
#include <limits>
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/process/process_metrics.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%llx 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%llx 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

View File

@ -0,0 +1,66 @@
// Copyright 2018 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.
#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_WIN_H_
#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_WIN_H_
#include <windows.h>
#include "base/macros.h"
#include "util/misc/address_types.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/process/process_memory.h"
#include "util/win/process_info.h"
namespace crashpad {
//! \brief Accesses the memory of another Windows process.
class ProcessMemoryWin final : public ProcessMemory {
public:
ProcessMemoryWin();
~ProcessMemoryWin();
//! \brief Initializes this object to read the memory of a process with the
//! provided handle.
//!
//! This method must be called successfully prior to calling any other method
//! in this class.
//!
//! \param[in] handle The HANDLE of a target process.
//!
//! \return `true` on success, `false` on failure with a message logged.
bool Initialize(HANDLE handle);
//! \brief Attempts to read \a size bytes from the target process starting at
//! address \a address into \a buffer. If some of the specified range is
//! not accessible, reads up to the first inaccessible byte.
//!
//! \return The actual number of bytes read.
size_t ReadAvailableMemory(VMAddress address,
size_t num_bytes,
void* buffer) const;
private:
ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;
HANDLE handle_;
ProcessInfo process_info_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessMemoryWin);
};
} // namespace crashpad
#endif // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_WIN_H_