Rework ProcessMemory.Read(Self|Forked) to work without fork()

Instead of allocating test memory in the parent and then forking and
comparing against it, the child does the allocation and passes back the
region's size and address. Additionally, switch the memcmp()s to be
value-based comparisons instead because the region isn't available in
the parent.

Also renames ProcessMemory.ReadForked to .ReadChild to be correct after
the change from Multiprocess to MultiprocessExec.

This is necessary to have the tests work on Fuchsia.

Bug: crashpad:196, crashpad:215
Change-Id: Id996a21180d87c7f2556283e9f54f6128726f9b8
Reviewed-on: https://chromium-review.googlesource.com/892102
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Scott Graham 2018-01-30 10:12:47 -08:00 committed by Commit Bot
parent c04352a2e6
commit ec71b63d6f

View File

@ -23,17 +23,131 @@
#include "gtest/gtest.h"
#include "test/errors.h"
#include "test/multiprocess.h"
#include "test/multiprocess_exec.h"
#include "test/process_type.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_linux.h"
#include "util/process/process_memory_native.h"
namespace crashpad {
namespace test {
namespace {
// TODO(scottmg): https://crashpad.chromium.org/bug/196. Multiprocess isn't
// ported yet.
void DoChildReadTestSetup(size_t* region_size,
std::unique_ptr<char[]>* region) {
*region_size = 4 * getpagesize();
region->reset(new char[*region_size]);
for (size_t index = 0; index < *region_size; ++index) {
(*region)[index] = index % 256;
}
}
CRASHPAD_CHILD_TEST_MAIN(ReadTestChild) {
size_t region_size;
std::unique_ptr<char[]> region;
DoChildReadTestSetup(&region_size, &region);
FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
CheckedWriteFile(out, &region_size, sizeof(region_size));
VMAddress address = FromPointerCast<VMAddress>(region.get());
CheckedWriteFile(out, &address, sizeof(address));
CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
return 0;
}
class ReadTest : public MultiprocessExec {
public:
ReadTest() : MultiprocessExec() {
SetChildTestMainFunction("ReadTestChild");
}
void RunAgainstSelf() {
size_t region_size;
std::unique_ptr<char[]> region;
DoChildReadTestSetup(&region_size, &region);
DoTest(GetSelfProcess(),
region_size,
FromPointerCast<VMAddress>(region.get()));
}
void RunAgainstChild() { Run(); }
private:
void MultiprocessParent() override {
size_t region_size;
VMAddress region;
ASSERT_TRUE(
ReadFileExactly(ReadPipeHandle(), &region_size, sizeof(region_size)));
ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &region, sizeof(region)));
DoTest(ChildProcess(), region_size, region);
}
void DoTest(ProcessType process, size_t region_size, VMAddress address) {
ProcessMemoryNative memory;
ASSERT_TRUE(memory.Initialize(process));
std::unique_ptr<char[]> result(new char[region_size]);
// Ensure that the entire region can be read.
ASSERT_TRUE(memory.Read(address, region_size, result.get()));
for (size_t i = 0; i < region_size; ++i) {
EXPECT_EQ(result[i], static_cast<char>(i % 256));
}
// Ensure that a read of length 0 succeeds and doesnt touch the result.
memset(result.get(), '\0', region_size);
ASSERT_TRUE(memory.Read(address, 0, result.get()));
for (size_t i = 0; i < region_size; ++i) {
EXPECT_EQ(result[i], 0);
}
// Ensure that a read starting at an unaligned address works.
ASSERT_TRUE(memory.Read(address + 1, region_size - 1, result.get()));
for (size_t i = 0; i < region_size - 1; ++i) {
EXPECT_EQ(result[i], static_cast<char>((i + 1) % 256));
}
// Ensure that a read ending at an unaligned address works.
ASSERT_TRUE(memory.Read(address, region_size - 1, result.get()));
for (size_t i = 0; i < region_size - 1; ++i) {
EXPECT_EQ(result[i], static_cast<char>(i % 256));
}
// Ensure that a read starting and ending at unaligned addresses works.
ASSERT_TRUE(memory.Read(address + 1, region_size - 2, result.get()));
for (size_t i = 0; i < region_size - 2; ++i) {
EXPECT_EQ(result[i], static_cast<char>((i + 1) % 256));
}
// Ensure that a read of exactly one page works.
size_t page_size = 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) {
EXPECT_EQ(result[i], static_cast<char>((i + page_size) % 256));
}
// Ensure that reading exactly a single byte works.
result[1] = 'J';
ASSERT_TRUE(memory.Read(address + 2, 1, result.get()));
EXPECT_EQ(result[0], 2);
EXPECT_EQ(result[1], 'J');
}
DISALLOW_COPY_AND_ASSIGN(ReadTest);
};
TEST(ProcessMemory, ReadSelf) {
ReadTest test;
test.RunAgainstSelf();
}
TEST(ProcessMemory, ReadChild) {
ReadTest test;
test.RunAgainstChild();
}
// TODO(scottmg): Need to be ported to MultiprocessExec and not rely on fork().
#if !defined(OS_FUCHSIA)
class TargetProcessTest : public Multiprocess {
@ -55,77 +169,6 @@ class TargetProcessTest : public Multiprocess {
DISALLOW_COPY_AND_ASSIGN(TargetProcessTest);
};
class ReadTest : public TargetProcessTest {
public:
ReadTest()
: TargetProcessTest(),
page_size_(getpagesize()),
region_size_(4 * page_size_),
region_(new char[region_size_]) {
for (size_t index = 0; index < region_size_; ++index) {
region_[index] = index % 256;
}
}
private:
void DoTest(pid_t pid) override {
ProcessMemoryLinux memory;
ASSERT_TRUE(memory.Initialize(pid));
VMAddress address = FromPointerCast<VMAddress>(region_.get());
std::unique_ptr<char[]> result(new char[region_size_]);
// Ensure that the entire region can be read.
ASSERT_TRUE(memory.Read(address, region_size_, result.get()));
EXPECT_EQ(memcmp(region_.get(), result.get(), region_size_), 0);
// Ensure that a read of length 0 succeeds and doesnt touch the result.
memset(result.get(), '\0', region_size_);
ASSERT_TRUE(memory.Read(address, 0, result.get()));
for (size_t i = 0; i < region_size_; ++i) {
EXPECT_EQ(result[i], 0);
}
// Ensure that a read starting at an unaligned address works.
ASSERT_TRUE(memory.Read(address + 1, region_size_ - 1, result.get()));
EXPECT_EQ(memcmp(region_.get() + 1, result.get(), region_size_ - 1), 0);
// Ensure that a read ending at an unaligned address works.
ASSERT_TRUE(memory.Read(address, region_size_ - 1, result.get()));
EXPECT_EQ(memcmp(region_.get(), result.get(), region_size_ - 1), 0);
// Ensure that a read starting and ending at unaligned addresses works.
ASSERT_TRUE(memory.Read(address + 1, region_size_ - 2, result.get()));
EXPECT_EQ(memcmp(region_.get() + 1, result.get(), region_size_ - 2), 0);
// Ensure that a read of exactly one page works.
ASSERT_TRUE(memory.Read(address + page_size_, page_size_, result.get()));
EXPECT_EQ(memcmp(region_.get() + page_size_, result.get(), page_size_), 0);
// Ensure that reading exactly a single byte works.
result[1] = 'J';
ASSERT_TRUE(memory.Read(address + 2, 1, result.get()));
EXPECT_EQ(result[0], region_[2]);
EXPECT_EQ(result[1], 'J');
}
const size_t page_size_;
const size_t region_size_;
std::unique_ptr<char[]> region_;
DISALLOW_COPY_AND_ASSIGN(ReadTest);
};
TEST(ProcessMemory, ReadSelf) {
ReadTest test;
test.RunAgainstSelf();
}
TEST(ProcessMemory, ReadForked) {
ReadTest test;
test.RunAgainstForked();
}
bool ReadCString(const ProcessMemory& memory,
const char* pointer,
std::string* result) {
@ -159,7 +202,7 @@ class ReadCStringTest : public TargetProcessTest {
private:
void DoTest(pid_t pid) override {
ProcessMemoryLinux memory;
ProcessMemoryNative memory;
ASSERT_TRUE(memory.Initialize(pid));
std::string result;
@ -263,7 +306,7 @@ class ReadUnmappedTest : public TargetProcessTest {
private:
void DoTest(pid_t pid) override {
ProcessMemoryLinux memory;
ProcessMemoryNative memory;
ASSERT_TRUE(memory.Initialize(pid));
VMAddress page_addr1 = pages_.addr_as<VMAddress>();
@ -341,8 +384,8 @@ class ReadCStringUnmappedTest : public TargetProcessTest {
}
private:
void DoTest(pid_t pid) {
ProcessMemoryLinux memory;
void DoTest(pid_t pid) override {
ProcessMemoryNative memory;
ASSERT_TRUE(memory.Initialize(pid));
if (limit_size_) {